refactor(output): relocate templates with examples

This commit is contained in:
2025-10-15 16:13:24 -05:00
parent f629d1f350
commit e0816486cb
8 changed files with 38 additions and 8 deletions

View File

@@ -10,5 +10,5 @@ Agents should treat these areas independently so changes can be reasoned about a
## Working Guidelines
- Keep shared instructions in this file minimal; place deeper guidance in `input/AGENTS.md` or `output/AGENTS.md` as appropriate.
- When making automated edits, avoid touching both `input/` and `output/` in the same change set unless the work explicitly spans both pipelines.
- Resume conversion templates live under `input/templates`. Output services mount them read-only; update templates from the input side and verify with a fresh conversion run.
- Resume conversion templates live under `output/templates`. Copy the `.example` files to matching names (without the suffix) when you need local overrides, and verify with a fresh conversion run.
- Use conventional commits (`<type>(scope): <message>`) to signal which side of the system a change targets, e.g., `feat(output): add failed-processing bucket`.

View File

@@ -20,7 +20,7 @@ Operate the output pipeline that turns one approved Markdown resume at a time in
## Guardrails (Do Not Cross)
- Treat `ForRelease/outbox` and `ForRelease/processed` as immutable history managed exclusively by the human. Only append new artifacts when explicitly directed; never delete, move, rename, reorganize, or even contemplate cleanup, retention, or consolidation actions.
- Refrain from generating additional files outside the described workflow. All job-specific Markdown arrives from the human, and all cleanup decisions belong to the human.
- Leave shared templates mounted from `input/templates` untouched; template maintenance happens in the input system.
- Leave shared templates under `templates/` untouched unless the human provides explicit replacements (copy `.example` files to matching names without the suffix to override).
- Do not queue multiple Markdown files or attempt parallel conversions. This pipeline is intentionally single-job at any moment.
- Escalate irregularities (missing templates, repeated failures, permission issues) to the human instead of improvising fixes.

View File

@@ -16,4 +16,4 @@ services:
- ../ForRelease/outbox:/data/outbox
- ../ForRelease/processed:/data/processed
- ../ForRelease/failed:/data/failed
- ../../input/templates:/templates:ro
- ../templates:/templates:ro

View File

@@ -20,17 +20,39 @@ OUTBOX = Path("/data/outbox")
PROCESSED = Path("/data/processed")
FAILED = Path("/data/failed")
TEMPLATES = Path("/templates")
TEMPLATE_CACHE = Path("/tmp/templates")
DOCX_TEMPLATE = TEMPLATES / "resume-reference.docx"
DOCX_TEMPLATE_EXAMPLE = TEMPLATES / "resume-reference.docx.example"
TEX_TEMPLATE = TEMPLATES / "resume-template.tex"
TEX_TEMPLATE_EXAMPLE = TEMPLATES / "resume-template.tex.example"
RESOLVED_DOCX_TEMPLATE: Path | None = None
RESOLVED_TEX_TEMPLATE: Path | None = None
POLL_INTERVAL_SECONDS = 5
def resolve_template(primary: Path, example: Path, cache_dir: Path) -> Path:
"""Return the template path, copying .example into a writable cache if needed."""
if primary.exists():
return primary
if example.exists():
cache_dir.mkdir(parents=True, exist_ok=True)
cached = cache_dir / primary.name
shutil.copy(example, cached)
return cached
raise FileNotFoundError(f"Template missing: {primary} (no example found)")
def ensure_environment() -> None:
"""Verify required files and directories exist before processing starts."""
global RESOLVED_DOCX_TEMPLATE, RESOLVED_TEX_TEMPLATE
missing = []
for path in (INBOX, OUTBOX, PROCESSED, FAILED, DOCX_TEMPLATE, TEX_TEMPLATE):
for path in (INBOX, OUTBOX, PROCESSED, FAILED, TEMPLATES):
if not path.exists():
missing.append(str(path))
@@ -39,6 +61,9 @@ def ensure_environment() -> None:
"Required paths are missing inside the container: " + ", ".join(missing)
)
RESOLVED_DOCX_TEMPLATE = resolve_template(DOCX_TEMPLATE, DOCX_TEMPLATE_EXAMPLE, TEMPLATE_CACHE)
RESOLVED_TEX_TEMPLATE = resolve_template(TEX_TEMPLATE, TEX_TEMPLATE_EXAMPLE, TEMPLATE_CACHE)
def run_pandoc(input_md: Path, output_docx: Path, output_pdf: Path) -> None:
"""Invoke pandoc twice to create DOCX and PDF artifacts."""
@@ -51,7 +76,7 @@ def run_pandoc(input_md: Path, output_docx: Path, output_pdf: Path) -> None:
"--to",
"docx",
"--reference-doc",
str(DOCX_TEMPLATE),
str(RESOLVED_DOCX_TEMPLATE),
"--output",
str(output_docx),
],
@@ -67,7 +92,7 @@ def run_pandoc(input_md: Path, output_docx: Path, output_pdf: Path) -> None:
"--pdf-engine",
"xelatex",
"--template",
str(TEX_TEMPLATE),
str(RESOLVED_TEX_TEMPLATE),
"--output",
str(output_pdf),
],

View File

@@ -8,6 +8,7 @@ This directory houses the post-processing side of ResumeCustomizer. It accepts o
- `ForRelease/processed/YYYY/MM/DD/HHMM` timestamped archives of Markdown files that converted successfully.
- `ForRelease/failed` holding area for Markdown files that Pandoc could not render.
- `Docker/` Dockerfile, compose stack, watcher script, and wrapper used to run the conversion container.
- `templates/` default Pandoc assets (`*.example`) the watcher copies into `/tmp` at runtime; copy any `.example` file without the suffix to override it locally (ignored by git).
All `ForRelease` subdirectories include `.gitkeep` and `.gitignore` so artifacts stay local and never reach version control.
@@ -23,7 +24,7 @@ The wrapper auto-detects the Docker Compose plugin or legacy `docker-compose`, f
## Conversion Flow
1. The watcher polls `ForRelease/inbox` every few seconds for exactly one Markdown resume.
2. Pandoc renders DOCX and PDF using the shared templates.
2. Pandoc renders DOCX and PDF using the shared templates (and auto-falls back to the bundled `.example` files if no overrides exist).
3. Artifacts land in a timestamped folder under `ForRelease/outbox`.
4. The source Markdown moves into the matching timestamp folder under `ForRelease/processed`.
5. On Pandoc failure, the Markdown shifts into `ForRelease/failed` for human review before retrying.
@@ -32,7 +33,7 @@ Outbox and processed directories are append-only historical records managed sole
## Prerequisites
- Docker Engine with the Compose plugin (`docker compose`) or the standalone `docker-compose` binary.
- Template assets mounted read-only from `input/templates`.
- Template assets mounted read-only from `templates/` (copy `.example` files to matching names without the suffix to customize them).
Stop or inspect the stack with:

4
output/templates/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.docx
*.tex
!*.docx.example
!*.tex.example