feat(#284): /pdf — export any doc with destination prompt#287
Conversation
…stination prompt Adds /pdf skill that converts framework-generated docs (markdown, HTML, BPMN) to PDF via dispatch across pandoc → md-to-pdf → wkhtmltopdf → bpmn-to-image. At export time the operator picks where the PDF lands via a 4-option prompt that mirrors the "would it follow the code if the project spun out?" rule from docs/multi-project.md: (1) workspace/<name>/docs/<stem>.pdf — travels with the code (2) projects/<name>/pdfs/<stem>.pdf — ApexYard's view (3) <custom path> — anywhere (k) keep next to source — <input-dir>/<stem>.pdf Graceful-degrades when no converter is installed (exit 3 + advisory naming each install option) — same shape as /process (bpmnlint) and /c4 (Mermaid lint), so adopters who never want PDFs pay zero install cost. Includes: - .claude/skills/pdf/SKILL.md (full process + rules) - .claude/skills/pdf/convert.sh (converter dispatch) - .claude/skills/pdf/tests/smoke.sh (format sniffing, missing-converter degrade, --check-only, destination resolution for each prompt slot) - pdf config block in .claude/project-config.defaults.json (preferred_converter, pdf_engine, default_destination) - docs/multi-project.md § "Architecture diagrams" extended with the "PDF exports follow the same rule" subsection - docs/agdr/AgDR-0034 covering the three load-bearing decisions: standalone-skill vs --pdf flag on every doc skill, dispatch vs hardcoded pandoc, prompt-always vs auto-pick by path - CLAUDE.md skill table + count bumped 48 → 49 Closes #284 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
atlas-apex
left a comment
There was a problem hiding this comment.
Code Review: PR #287
Commit: 10b95a4da3edafe681892cf2a3fc6e6088e343f8
Summary
Adds the /pdf skill — converts framework-generated docs (markdown / HTML / BPMN) to PDF with a 4-option destination prompt that mirrors the existing "would it follow the code if the project spun out?" rule from docs/multi-project.md. Shells out to a converter-dispatch helper (convert.sh) that picks the best available backend from pandoc / md-to-pdf / wkhtmltopdf / bpmn-to-image and graceful-degrades (exit 3 + advisory) when no converter is installed. Same shape as /process (bpmnlint) and /c4 (Mermaid lint), so adopters who never need PDFs pay zero install cost.
Checklist Results
- Architecture & Design: Pass
- Code Quality: Pass
- Testing: Pass
- Security: Pass (no auth/secrets touched)
- Performance: Pass (synchronous shell, single conversion per invocation, no hot paths)
- PR Description & Glossary: Pass (6 well-explained terms)
- Technical Decisions (AgDR): Pass — AgDR-0034 covers all three load-bearing axes (standalone vs per-skill flag, single converter vs dispatch, prompt vs auto-pick)
- Adopter Handbooks: Pass — no architecture/migration/language-handbook triggers fire; commit-message-quality passes cleanly
Verification Done
- Exit-code taxonomy (0/1/2/3) matches sibling shapes in
_lib-mermaid-lint.shandprocess/lint.sh✓ set -uo pipefail(no-e) matches_lib-mermaid-lint.sh(intentional — explicitexit 1after each converter invocation) ✓- Portfolio path resolution: SKILL.md sources
_lib-read-config.sh+_lib-portfolio-paths.shand callsportfolio_projects_dir/portfolio_workspace_dir— no hardcoded literals ✓ - Test PATH-stripping technique correctly forces the "no converter" branch regardless of host environment; falls back to
skipif a symlink leaks ✓ - BPMN multi-step pipeline (bpmn-to-image → SVG → pandoc → PDF) clearly described in SKILL.md § 6, AgDR-0034, and the
convert.shheader comment ✓ - 4-option destination prompt clearly described (lines 117–130 of SKILL.md) with explicit hint text ✓
- AgDR slot 0034 doesn't collide with open PRs #285 (0031) or #289 (0033) ✓
- Commit message quality: subject + body +
Closes #284+ Co-Authored-By ✓
Issues Found
None blocking.
Suggestions (non-blocking)
-
Smoke test §8 option 3 is a tautology. Lines 932–936:
custom="/tmp/out.pdf" got="$custom" if [ "$got" = "/tmp/out.pdf" ]; then ok "option 3 → ..."; ...
This asserts
$custom == $custom. The intent — verify option 3 passes through the literal — is documented in the comments but not actually exercised. Consider replacing with a real call into aresolve_destextension that handles option 3, or remove the assertion. Keeping it as-is is fine for v1; just flagging that it does not exercise real behavior. -
convert.shdoes not source thepdf.*config block. Config keyspreferred_converterandpdf_engineare read by the model orchestrating the skill (SKILL.md instructs the agent to pass them as--converter/--pdf-engineflags), not byconvert.shitself. This is a reasonable separation-of-concerns choice —convert.shstays config-free — but the SKILL.md "Path resolution" section could state it explicitly. Future readers may otherwise expectconvert.shto read the config block. Single-line addition under the Config heading would close the loop. -
$eng_flagunquoted at lines 525/568/620 is the standard "optional flag" pattern (quoting an emptyeng_flagwould inject""as a literal arg to pandoc). Intentional and correct, but a one-line comment# unquoted on purpose — empty PDF_ENGINE means omit the flag entirelywould prevent a well-meaning future shellcheck-driven cleanup from breaking it. -
npx … >&2routing converter chatter to stderr (lines 537/579/599) is unusual — most callers route to/dev/nulland trust the exit code. Current shape keeps the operator informed during the npx fetch (~5s cold start) which is helpful UX; just noting for any future refactor. -
AgDR slot ordering. This PR uses AgDR-0034 while slots 0032 / one of {0033} are still unallocated on
dev. Not blocking — apexyard's AgDR sequence has accepted gaps before — and PRs #285 (0031) and #289 (0033) keep the global sequence monotone-ish. Worth a one-liner in the AgDR's intro if there's a story behind the gap; otherwise let it be.
Verdict
APPROVED
The PR is consistent with the agent's plan, mirrors the existing graceful-degrade patterns (/process lint, _lib-mermaid-lint.sh), routes through portfolio_* helpers (so split-portfolio adopters get correct paths transparently), and ships an AgDR that genuinely earns its keep across three real design axes. The 4-option destination prompt is a thoughtful echo of the existing "would it follow the code?" rule and avoids the silent-auto-pick foot-gun. Tests cover the load-bearing behaviors. The five style nits above are suggestions, not requested changes.
🤖 Reviewed by Rex (Code Reviewer Agent)
📌 Reviewed commit: 10b95a4da3edafe681892cf2a3fc6e6088e343f8
Dev advanced significantly while this branch was in flight (v1.3.0 release + #281 templates reorg + #283 tracker config + #287 /pdf config). Resolution: - Framework / docs (CHANGELOG, CLAUDE, multi-project, sdlc, templates/README, custom-templates example README, site/architecture, workflows/sdlc): --theirs (dev has the integrated state) - 5 ticket SKILL.md / test files (spike, task, investigation, investigation tests README + smoke.sh): --theirs (dev has the templates/tickets/ paths from #281) - .claude/project-config.defaults.json: --theirs (dev has #287's pdf block) - .claude/skills/update/SKILL.md: --ours (this IS #282's deliverable — the chain-walk flow + step 8b) Refs #282
Both sides added a new top-level key after the portfolio block: - HEAD (#283): tracker dispatch (AgDR-0033) - origin/dev (#287 via #284): pdf config (AgDR-0034) Both blocks are independent + additive, so kept both with a comma separator. JSON validates clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dev advanced ~15 commits since this branch (v1.3.0 release + #281 ticket templates + #283 tracker abstraction + #284 /pdf + #286 update chain + #287 + #288 /feature-diagram + #293 Rex domain handbooks + #275 ui-paths- exclude + #266 mermaid lint + #270 DFD snapshot + #246 tech-vision + #245 investigation + #257 dfd + #268 skill-gated ticket-create). Resolution strategy: --theirs for framework drift (CLAUDE.md, multi-project, project-config.defaults, all new tests, hook files, other SKILL.mds, handbooks/README, site/architecture.html, templates/*, workflows/sdlc, etc. — 26 files). Manual merge for the #290 deliverable `.claude/skills/extract-features/SKILL.md` — kept BOTH the argument-hint + `--with-mockups` doc section + step 4b (this PR's deliverable) AND the `/feature-diagram` See-also pointer dev added (complementary, not overlapping). 8 conflict regions in extract-features/SKILL.md handled: 1 kept dev's new See-also block, 7 kept ours (argument-hint, Usage block, the full --with-mockups doc, the IF-mockups insert marker, the giant 4b ASCII- wireframes section, the report-line append, the anti-pattern bullets). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…stination prompt (#287) Adds /pdf skill that converts framework-generated docs (markdown, HTML, BPMN) to PDF via dispatch across pandoc → md-to-pdf → wkhtmltopdf → bpmn-to-image. At export time the operator picks where the PDF lands via a 4-option prompt that mirrors the "would it follow the code if the project spun out?" rule from docs/multi-project.md: (1) workspace/<name>/docs/<stem>.pdf — travels with the code (2) projects/<name>/pdfs/<stem>.pdf — ApexYard's view (3) <custom path> — anywhere (k) keep next to source — <input-dir>/<stem>.pdf Graceful-degrades when no converter is installed (exit 3 + advisory naming each install option) — same shape as /process (bpmnlint) and /c4 (Mermaid lint), so adopters who never want PDFs pay zero install cost. Includes: - .claude/skills/pdf/SKILL.md (full process + rules) - .claude/skills/pdf/convert.sh (converter dispatch) - .claude/skills/pdf/tests/smoke.sh (format sniffing, missing-converter degrade, --check-only, destination resolution for each prompt slot) - pdf config block in .claude/project-config.defaults.json (preferred_converter, pdf_engine, default_destination) - docs/multi-project.md § "Architecture diagrams" extended with the "PDF exports follow the same rule" subsection - docs/agdr/AgDR-0034 covering the three load-bearing decisions: standalone-skill vs --pdf flag on every doc skill, dispatch vs hardcoded pandoc, prompt-always vs auto-pick by path - CLAUDE.md skill table + count bumped 48 → 49 Closes #284 Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Adds the
/pdfskill — convert any framework-generated doc (markdown, HTML, BPMN) to PDF for sharing with non-technical stakeholders, board members, customers, or auditors.At export time the operator picks where the PDF lands via a 4-option prompt that mirrors the existing "would it follow the code if the project spun out?" rule from
docs/multi-project.md:Converter dispatch picks the right backend for the input format:
When no converter is installed, exit 3 with an advisory naming each install option — same shape as
/process(bpmnlint) and/c4(Mermaid lint). Adopters who never need PDFs pay zero install cost.Changes
.claude/skills/pdf/SKILL.md— full process + rules + when-to-use table.claude/skills/pdf/convert.sh— converter dispatch helper.claude/skills/pdf/tests/smoke.sh— covers format sniffing, missing-flag handling, missing-converter graceful degrade (mocked PATH),--check-onlymode, optional pandoc + wkhtmltopdf paths (skip cleanly when absent), and destination resolution for each of the 4 prompt options.claude/project-config.defaults.json— newpdfblock (preferred_converter,pdf_engine,default_destination)docs/multi-project.md§ "Architecture diagrams" — extended with a "PDF exports follow the same rule" subsectiondocs/agdr/AgDR-0034-pdf-export-and-converter-dispatch.md— covers the three load-bearing decisions: standalone-skill vs--pdfflag on every doc skill, dispatch vs hardcoded pandoc, prompt-always vs auto-pick by pathCLAUDE.md— skill row added; skill count bumped 48 → 49Testing
Smoke also covered manually (in this worktree pandoc isn't installed, so the missing-converter advisory branch +
--check-onlyreporting both verified end-to-end).Out of scope (deferred to v1.5, per the ticket)
--pdfflag on each doc-emitting skill (cleaner one-command UX but couples every doc skill to the converter dep). The AgDR explains why standalone/pdfis the v1 path./pdfin shell.Closes #284
Glossary
/pdfasks at export time about where to write the PDF (the 4-option list above)docs/multi-project.md— if a project spun out tomorrow, would this doc need to come with it? If YES, the doc lives in the project repo (workspace/<name>/docs/); if NO, it lives in ApexYard'sprojects/<name>/convert.shthat picks the best PDF backend for the input format from what's actually installed on the operator's machine/processand/c4's lint) where missing optional deps produce an exit-3 advisory rather than blocking the skillprojects/<name>/audits/<dim>/<YYYY-MM-DD>.md. PDFs of these inherit the date in their stem automaticallydocs/multi-project.md./pdfreadsprojects_dirandworkspace_dirvia the portfolio-paths helper so it works in both transparently🤖 Generated with Claude Code