Skip to content

Commit d35a667

Browse files
fix(130): enforce mmdc as hard prerequisite with loud preflight/mid-render aborts (#148)
* docs(130): scaffold feature workspace with PRD, spec, plan, ADR-022 Feature 130 fixes silent failure in attack path Mermaid rendering when mmdc is not installed. This commit lays the governance groundwork for the fix: PRD, spec, plan, research, tasks, agent assignments, and a new ADR establishing mmdc as a hard prerequisite. - PRD 130: Fix Attack Path Mermaid Rendering (PM/Architect/Team-Lead approved) - ADR-022: First ADR governing CLI-prerequisite posture in tachi. Decision: mmdc is a hard prerequisite when attack-trees/ contains Critical/High findings. Cross-refs ADR-014 (optional external APIs) and ADR-021 (determinism). Future Work clause defers install.sh helper extraction until a 3rd CLI prereq arrives. - Spec/plan/tasks/research/agent-assignments under specs/130-prd-130-fix/ - docs/architecture/01_system_design/README.md: Feature 130 components section covering preflight gate (shell + Python), mid-render aggregator, CI workflow, and docs sync cluster. - docs/product/02_PRD/INDEX.md: add row 130 - docs/product/_backlog/BACKLOG.md: regenerate after Issue #130 moved to Build stage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(130): add mmdc preflight gate with defense-in-depth Turn silent failure into loud failure when mmdc is missing. Previously the pipeline silently fell back to raw Mermaid text in the PDF when mmdc was unavailable; now the pipeline aborts at preflight with a canonical three-line install message and non-zero exit. User Story 1 (T004-T008) — prerequisites are enforced, not assumed: - .claude/commands/tachi.security-report.md Step 2: shell-level gate. Detects attack-trees/*.md presence, then `command -v mmdc`. If mmdc is missing, echoes canonical message to stderr and halts non-zero. Mirrors the existing Typst check. Skip the check on projects without attack trees — they do not need mmdc. - scripts/extract-report-data.py render_mermaid_to_png(): replace the silent shutil.which("mmdc") warn+fallback with raise RuntimeError using the canonical message. Defense-in-depth for direct Python invocations (tests, tooling). Also: add `sys.path.insert(0, ...)` before the tachi_parsers import, parallel to extract-infographic-data.py, required enabler for the importlib-based test fixture. - templates/tachi/security-report/attack-path.typ: delete the `else if mermaid-text != ""` branch entirely. With the preflight gate guaranteeing mmdc presence, the text-fallback branch is dead code. The `if has-img and img-path != ""` branch is now the only render path. - tests/scripts/test_mmdc_preflight.py (new): 4 preflight tests + 5 mid-render aggregator tests. The 4 preflight tests pass; the 5 mid-render tests fail as expected (Wave 4 T010/T011 will land the module-level _render_single + failure aggregator to make them green). Verification: - 4/4 preflight tests green under test_mmdc_preflight.py -k preflight - 5/5 backward-compatibility baselines still byte-identical under SOURCE_DATE_EPOCH=1700000000 (happy path unchanged) - Canonical three-line error message contains: @mermaid-js/mermaid-cli, `npm install -g @mermaid-js/mermaid-cli`, and "Attack path rendering" Tasks: T001-T008 marked [X] in tasks.md. Waves 1-3 complete (8/32); Wave 4 (US2 mid-render aggregator) next. Refs: ADR-022, spec.md FR-130.1/FR-130.3, plan.md Phase 1 Design R5-R7 refinements Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(130): abort on mid-render failure with per-finding error list User Story 2 (T009-T012) — mid-render failures are loud, not silent. When mmdc is installed but a specific attack tree fails to render (syntax error, crash, timeout), the pipeline previously set has_image=False and silently produced a PDF with some pages missing. Now any render failure aborts the pipeline with an informative per- finding error list on stderr. Implementation: - scripts/extract-report-data.py: promote _render_single from nested closure to module-level function so tests can patch.object on it. Extend the return shape to (entry, success, value) where on failure `value` is a structured error record dict: - id: finding ID - file_path: canonical "attack-trees/{fid_lower}.mmd" path - failure_class: "exit:<code>", "timeout", or "signal" - stderr_excerpt: first 200 bytes of stderr (utf-8, errors=replace) - render_mermaid_to_png() as_completed loop: collect failures into a list. When the loop ends, if the failure list is non-empty, format per R6 spec and raise RuntimeError: Attack path rendering failed for N findings: - F-002 (attack-trees/f-002.mmd) failure: exit:2 stderr: Parse error on line 5 R6 (the highest-priority architect refinement) is the format that decides whether this feature delivers on its fail-loud promise. - _render_single uses two module-level globals (_render_attack_trees_dir and _render_rel_target) published by render_mermaid_to_png before pool execution. This keeps _render_single's signature at 2 positional args (entry, tmp_path) — required by the test harness's 2-arg mock. Thread-safe for the sequential CLI model (documented inline). - No new imports. No new dependencies. Runtime stdlib-only constraint preserved per ADR-001 / Constitution Principle III. Verification: - 9/9 tests pass under test_mmdc_preflight.py (4 preflight + 5 mid-render) - 5/5 backward-compatibility baselines still byte-identical under SOURCE_DATE_EPOCH=1700000000 — happy path is unchanged - R6 format assertions verified: * summary line "Attack path rendering failed for N findings:" * per-finding ID + file path + failure class + stderr excerpt - R7 distinction verified: mid-render message does NOT contain "npm install -g @mermaid-js/mermaid-cli" (preflight's distinct shape) Tasks: T009-T012 marked [X]. Wave 4 complete (12/32, 37.5%). Wave 5 (US3 docs sync, T013-T017) next. Refs: spec.md FR-130.3/FR-130.4, plan.md R5/R6/R7 refinements, ADR-022 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(130): declare mmdc as hard prerequisite across README, install.sh, Tech Stack, spec 112 User Story 3 (T013-T017) — document the mmdc hard-prerequisite posture consistently across all user-facing and spec-level documentation. Changes: - README.md: new Prerequisites section between "What is tachi?" and "Quick Start". Names typst and @mermaid-js/mermaid-cli as required external CLIs with macOS/Linux/WSL install commands. Cross-links to ADR-022 for rationale. - scripts/install.sh: courtesy warning block if mmdc is missing. Points users to README Prerequisites and the canonical install command. Does NOT check Typst (scope containment per plan S2 decision — the per-command preflight is the enforcement, install.sh warning is advisory only). - docs/architecture/00_Tech_Stack/README.md: update mmdc entry (line 272) from "optional with graceful fallback" to "hard prerequisite as of Feature 130 — see ADR-022". Rewrite the surrounding note (line 279) to describe preflight enforcement with cross-link to ADR-022. Makes ADR-022 discoverable from Tech Stack per plan Risk #5. - specs/112-attack-path-pages/spec.md: invert SC-004. Was "When the rendering tool is unavailable, 100% of attack path pages still appear with text fallback." Now "Rendering tool availability is verified at preflight; pipeline aborts loudly if unavailable." Adds audit-trail comment above the inverted criterion: <!-- Inverted by Feature 130 (2026-04-11): text fallback is no longer a supported shipping mode --> Also rewrites the Assumptions bullet that previously said mmdc is not a hard dependency. - specs/112-attack-path-pages/research.md: correct the pymmdc factual error — pymmdc on PyPI is a GPL-3.0 Python wrapper around the Node.js @mermaid-js/mermaid-cli CLI, not a pure-Python renderer. Adds a "Durable Decision Rationale" block documenting the mmdc-hard-prereq choice with references to Feature 130 PRD Rejected Alternatives (A-E) and ADR-022. Verification: - All 14 tests still green (9 preflight + 5 baselines) - Canonical install command `npm install -g @mermaid-js/mermaid-cli` appears verbatim in all 5 edited files - 8 ADR-022 references across 4 files (Tech Stack + spec/research 112 + README) - Audit-trail comment present in spec 112 with exact date 2026-04-11 Tasks: T013-T017 marked [X]. Wave 5 complete (17/32, 53%). Wave 6 (Cross-Cutting & CI, T018-T029) next. Refs: spec.md FR-130.5/FR-130.6, ADR-022, plan Risk #5 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(130): CI workflow, docs, and Wave 6 verification Wave 6 of Feature 130 — cross-cutting artifacts and verification gates. CI workflow (T018, FR-130.7): - .github/workflows/tachi-mmdc-preflight.yml on ubuntu-latest - Installs typst-community/setup-typst@v5 + Python 3.11; does NOT install mmdc - Diagnostic echo (R3) + enforcement assertion (team-lead T4, plan Risk #6) — fails workflow if mmdc unexpectedly appears on PATH - Invokes python3 scripts/extract-report-data.py directly (no slash-command agent orchestration in CI); asserts non-zero exit + all 3 canonical tokens (@mermaid-js/mermaid-cli, npm install -g @mermaid-js/mermaid-cli, Attack path rendering) grep-asserted individually Docs (T025, T026): - CLAUDE.md Recent Changes: prepend Feature 130 entry above Feature 136 - specs/130-prd-130-fix/quickstart.md: developer reproduction, loud-failure validation, happy-path baseline run, regeneration instructions Verification (T019-T024, T027-T031 automation): - T019: BASELINE_EXAMPLES confirmed to exclude agentic-app/sample-report/ (R8 negative — Feature 128 decision stands) - T020/T021: mermaid-agentic-app baseline and agentic-app/sample-report PDFs regenerated under SOURCE_DATE_EPOCH=1700000000; byte-identical to prior - T022: backward-compat 5/5 baselines byte-identical vs T002 pre-flight snapshot (R9 before/after guardrail pair complete) - T023: canonical install command appears in exactly the 7 required enforcement locations (R4) - T024: dead-code greps return zero — else-if-mermaid-text branch deleted in attack-path.typ, silent-fallback has_image=False loop replaced with raise RuntimeError in extract-report-data.py shutil.which context - T027/T028/T029 automated: pipeline exit 0 with mmdc present (byte-ident to baseline, 25 image xobjects); pipeline exit 1 with clean PATH emitting all 3 canonical tokens; 47-tree sample-report embeds 39 image xobjects - T030: full pytest 48/48 pass - T031: constitutional walk-through — no runtime deps added, stdlib-only preserved, feature branch workflow intact, conventional commits Tasks.md: T018-T031 marked [X]. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(130): PR description, docstring cleanup, Wave 7 polish Wave 7 polish — code-reviewer non-blocking fixes and PR assembly. - templates/tachi/security-report/attack-path.typ: remove stale "image or text fallback" references in header comment (line 4-5) and attack-path-page parameter docstring (line 38-39). The `mermaid-text` field and text-fallback branch were deleted in T007; comments now reflect that only the rendered-image path exists. Pure comment change — backward-compat baselines verified byte-identical post-fix. - specs/130-prd-130-fix/PR-description.md: T032 PR body for /aod.deliver. Assembles FR-130.1 through FR-130.7 deliverables with commit SHAs, Before/After CHANGELOG narrative, test coverage summary, automated manual-validation results (T027/T028/T029), governance links (ADR-022, spec 112 corrections, Tech Stack doc), and reviewer checklist. - specs/130-prd-130-fix/tasks.md: T032 marked [X]. All 32 tasks complete. - docs/product/_backlog/BACKLOG.md: timestamp bump from /aod.build Step 1 GitHub stage label update (issue #130 to stage:build). Full suite 48/48 pass. Backward-compat 5/5 baselines byte-identical under SOURCE_DATE_EPOCH=1700000000. Feature ready for Step 6 security scan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * security(130): run security scan [2c97e80] SAST: 3 files scanned (extract-report-data.py, install.sh, test_mmdc_preflight.py). OWASP P0 pattern review clean — no injection, no hardcoded secrets, no path traversal introduced, no weak crypto, no auth/session handling. SCA: skipped — no dependency manifests changed on this feature branch. Status: PASSED, 0 findings across all severity tiers. Scan ID: 65936668-f62b-4548-a9bd-e503c6e114ff Chain hash: 0bf47a425fcfddef942052831cb8ea9e25a3629d3b9529a45383a4136d844892 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(130): mark T030/T031 complete, finalize NEXT-SESSION notes T030: full pytest suite 48/48 green T031: conventional commits, stdlib-only, branch hygiene verified Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7240695 commit d35a667

File tree

28 files changed

+3526
-60
lines changed

28 files changed

+3526
-60
lines changed

.claude/commands/tachi.security-report.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,18 @@ Single-command entry point for tachi security assessment PDF generation — the
5353
```
5454
- Halt if not installed.
5555
56-
2. **Auto-detect artifacts in target directory**:
56+
2. **Check mmdc is installed when attack trees are present**:
57+
- Detect attack-tree presence: `ls "$target_dir"/attack-trees/*.md 2>/dev/null | head -n 1`. If the result is non-empty, the target project has at least one attack-tree file and mmdc is required.
58+
- If attack trees are present, run `command -v mmdc >/dev/null 2>&1` to verify mmdc CLI is available.
59+
- If mmdc is not found, display to stderr:
60+
```
61+
Attack path rendering requires @mermaid-js/mermaid-cli (mmdc).
62+
Install with: npm install -g @mermaid-js/mermaid-cli
63+
Then re-run /tachi.security-report.
64+
```
65+
- Halt (exit non-zero) if mmdc is not installed. Skip this check entirely when the target has no attack trees — projects without attack-path output do not need mmdc.
66+
67+
3. **Auto-detect artifacts in target directory**:
5768
5869
Scan `target_dir` for the following 7 artifact types:
5970
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# =============================================================================
2+
# tachi mmdc preflight gate — CI fresh-install acceptance test (FR-130.7)
3+
# =============================================================================
4+
#
5+
# Purpose: Prove the Feature 130 preflight gate fires in a shell where
6+
# `mmdc` (@mermaid-js/mermaid-cli) is NOT on PATH. ubuntu-latest ships
7+
# without mmdc (plan.md spike S3), so omitting the install step satisfies
8+
# the "mmdc absent" precondition automatically.
9+
#
10+
# The workflow:
11+
# 1. Installs Typst + Python 3.11 (but NOT mmdc)
12+
# 2. Asserts mmdc is absent (guards against transitive installs — plan Risk #6)
13+
# 3. Runs scripts/extract-report-data.py against examples/mermaid-agentic-app/
14+
# (direct Python invocation — slash commands cannot run in CI)
15+
# 4. Asserts non-zero exit code
16+
# 5. Asserts the canonical three tokens from the RuntimeError message:
17+
# - @mermaid-js/mermaid-cli
18+
# - npm install -g @mermaid-js/mermaid-cli
19+
# - Attack path rendering
20+
#
21+
# References:
22+
# - specs/130-prd-130-fix/plan.md (spike S3 + Risk #6)
23+
# - specs/130-prd-130-fix/tasks.md (T018)
24+
# - docs/architecture/02_ADRs/ADR-022-mmdc-hard-prerequisite.md
25+
# - scripts/extract-report-data.py:render_mermaid_to_png (preflight gate)
26+
# =============================================================================
27+
28+
name: tachi mmdc preflight
29+
30+
on:
31+
pull_request:
32+
paths:
33+
- scripts/extract-report-data.py
34+
- templates/tachi/security-report/attack-path.typ
35+
- scripts/install.sh
36+
- .claude/commands/tachi.security-report.md
37+
- README.md
38+
- .github/workflows/tachi-mmdc-preflight.yml
39+
40+
permissions:
41+
contents: read
42+
43+
jobs:
44+
preflight-fresh-install:
45+
name: Verify preflight gate fires when mmdc is absent
46+
# ubuntu-latest ships WITHOUT @mermaid-js/mermaid-cli (plan.md spike S3
47+
# confirmed). Do not switch to a custom image that preinstalls mmdc.
48+
runs-on: ubuntu-latest
49+
steps:
50+
- name: Checkout repository
51+
uses: actions/checkout@v4
52+
53+
- name: Set up Python 3.11
54+
uses: actions/setup-python@v5
55+
with:
56+
python-version: "3.11"
57+
58+
# Typst setup. typst-community/setup-typst@v5 is the current stable
59+
# major tag at time of authoring (T018). This step MUST NOT transitively
60+
# install @mermaid-js/mermaid-cli; the next step will fail the job if
61+
# that invariant is ever broken by an upstream action change.
62+
- name: Set up Typst
63+
uses: typst-community/setup-typst@v5
64+
65+
# Architect refinement R3 (Medium): make the intentional absence of
66+
# mmdc visible in CI logs for future debuggers.
67+
- name: Diagnostic — show mmdc absence
68+
run: |
69+
which mmdc || echo "expected absence: mmdc not on PATH, preflight gate should fire"
70+
71+
# Team-lead T4 enforcement assertion (Medium) / plan Risk #6 mitigation.
72+
# Guards against future transitive installs of mmdc via Typst setup or
73+
# other actions. If mmdc unexpectedly appears on PATH, the S3 spike
74+
# assumption is broken and this test is no longer meaningful — fail fast.
75+
- name: "Enforce mmdc absence (plan Risk #6)"
76+
run: |
77+
if command -v mmdc >/dev/null 2>&1; then
78+
echo "FATAL: mmdc unexpectedly present on PATH. S3 spike assumption broken."
79+
exit 1
80+
fi
81+
82+
# Direct Python invocation of the extraction script. Claude Code slash
83+
# commands cannot run in CI — we call scripts/extract-report-data.py
84+
# with its required CLI args (--target-dir, --output, --template-dir).
85+
# The preflight gate in render_mermaid_to_png() fires before any file
86+
# is written, so --output is only needed to satisfy argparse.
87+
- name: Run extract-report-data.py against mermaid-agentic-app (expect failure)
88+
run: |
89+
set +e
90+
python3 scripts/extract-report-data.py \
91+
--target-dir examples/mermaid-agentic-app \
92+
--output /tmp/report-data.typ \
93+
--template-dir templates/tachi/security-report \
94+
>/tmp/out.txt 2>&1
95+
EXIT=$?
96+
set -e
97+
echo "---- captured output (stdout + stderr) ----"
98+
cat /tmp/out.txt
99+
echo "---- end captured output ----"
100+
echo "exit code: $EXIT"
101+
if [ "$EXIT" -eq 0 ]; then
102+
echo "FAIL: expected non-zero exit code, got 0. Preflight gate did not fire."
103+
exit 1
104+
fi
105+
echo "OK: non-zero exit code as expected ($EXIT)"
106+
107+
# Assert all three canonical tokens from the preflight RuntimeError
108+
# message appear in the captured output. Grep each token individually
109+
# so a missing token produces a specific, actionable error.
110+
- name: Assert canonical error tokens present
111+
run: |
112+
MISSING=0
113+
if ! grep -q '@mermaid-js/mermaid-cli' /tmp/out.txt; then
114+
echo "FAIL: canonical token missing: @mermaid-js/mermaid-cli"
115+
MISSING=1
116+
fi
117+
if ! grep -q 'npm install -g @mermaid-js/mermaid-cli' /tmp/out.txt; then
118+
echo "FAIL: canonical token missing: npm install -g @mermaid-js/mermaid-cli"
119+
MISSING=1
120+
fi
121+
if ! grep -q 'Attack path rendering' /tmp/out.txt; then
122+
echo "FAIL: canonical token missing: Attack path rendering"
123+
MISSING=1
124+
fi
125+
if [ "$MISSING" -ne 0 ]; then
126+
echo "One or more canonical tokens were missing from the preflight error output."
127+
echo "This means the FR-130.1 preflight gate either did not fire or emitted a non-canonical message."
128+
exit 1
129+
fi
130+
echo "OK: all three canonical tokens present in preflight error output"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"version": "2.1.0",
3+
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
4+
"runs": [
5+
{
6+
"tool": {
7+
"driver": {
8+
"name": "aod-security",
9+
"version": "1.0.0",
10+
"informationUri": "https://github.com/anthropics/agentic-oriented-development",
11+
"rules": []
12+
}
13+
},
14+
"results": [],
15+
"invocations": [
16+
{
17+
"executionSuccessful": true,
18+
"endTimeUtc": "2026-04-11T15:47:02Z",
19+
"properties": {
20+
"scan_id": "65936668-f62b-4548-a9bd-e503c6e114ff",
21+
"branch": "130-prd-130-fix",
22+
"commit_sha": "2c97e8091c13",
23+
"files_scanned": 3,
24+
"manifests_audited": 0,
25+
"status": "PASSED",
26+
"note": "No findings detected. AI-powered analysis — supplement with dedicated SAST tools for production-critical systems."
27+
}
28+
}
29+
]
30+
}
31+
]
32+
}

.security/scan-log.jsonl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
{"branch":"091-maestro-infographic-templates","chain_hash":"71f7621a39bc9739baddbdc00b666ea5c726676d4f0888a375c0be8c6b3dd4dd","commit_sha":"c0231b653428","files_scanned":2,"finding_counts":{"CRITICAL":0,"HIGH":0,"INFO":0,"LOW":0,"MEDIUM":0},"manifests_audited":0,"scan_id":"e2800472-784d-485f-8f00-fd780b493e4c","status":"PASSED","ts":"2026-04-08T17:55:00Z"}
55
{"branch":"112-attack-path-pages","chain_hash":"15a9e35ed930eed548a5e557878be13c7663e86d2263b150b5911691bc3fa28b","commit_sha":"cefa1b10218a","files_scanned":2,"finding_counts":{"CRITICAL":0,"HIGH":0,"INFO":0,"LOW":0,"MEDIUM":0},"manifests_audited":0,"scan_id":"e223a216-8fa2-4371-8f49-0be915af71c7","status":"PASSED","ts":"2026-04-09T13:42:51Z"}
66
{"branch":"128-prd-128-executive","chain_hash":"540fc7a6a439394b2f50528c801b41255550c2bb4ac3c9c3664c2287f3e569e7","commit_sha":"01d3e8587191","files_scanned":12,"finding_counts":{"CRITICAL":0,"HIGH":0,"INFO":0,"LOW":0,"MEDIUM":0},"manifests_audited":2,"scan_id":"ba390e43-68fd-4627-b3c4-92e79c24141a","status":"PASSED","ts":"2026-04-10T04:55:26Z"}
7+
{"branch":"130-prd-130-fix","chain_hash":"0bf47a425fcfddef942052831cb8ea9e25a3629d3b9529a45383a4136d844892","commit_sha":"2c97e8091c13","files_scanned":3,"finding_counts":{"CRITICAL":0,"HIGH":0,"INFO":0,"LOW":0,"MEDIUM":0},"manifests_audited":0,"scan_id":"65936668-f62b-4548-a9bd-e503c6e114ff","status":"PASSED","ts":"2026-04-11T15:47:02Z"}

CLAUDE.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ When invoked as a subagent (via Agent tool), return ONLY:
9898
- Review `agent-assignments.md` for workload distribution
9999

100100
## Recent Changes
101+
- **Feature 130**: Fix Attack Path Mermaid Rendering When mmdc Is Not Installed
102+
- Silent text-fallback replaced with loud fail-fast behavior at two enforcement points. Defense-in-depth: shell-level preflight gate in `.claude/commands/tachi.security-report.md` Step 1 + Python-level `shutil.which("mmdc")` raise in `scripts/extract-report-data.py::render_mermaid_to_png()`. Both fire only when `attack-trees/` contains Critical/High findings (gated — backward compatible for projects without attack trees).
103+
- Mid-render failure aggregator: `_render_single` now returns structured `error_record` dicts (keys: `id`, `file_path`, `failure_class` of `"exit:<code>"`/`"timeout"`/`"signal"`, `stderr_excerpt` first 200 bytes). `render_mermaid_to_png()` `as_completed` loop collects failures and raises `RuntimeError` with a per-finding failure list instead of silently marking `has_image=False`.
104+
- Text-fallback Typst branch deleted outright: `templates/tachi/security-report/attack-path.typ` lines 78-86 (the `else if mermaid-text != ""` block) removed — no placeholder, no "removed in 130" stub.
105+
- Documentation sync across 5 files: `README.md` (new `## Prerequisites` section naming `typst` + `@mermaid-js/mermaid-cli` with macOS/Linux/WSL install commands), `scripts/install.sh` (courtesy `command -v mmdc` warning), `docs/architecture/00_Tech_Stack/README.md` line 279 (rewritten as hard prerequisite with ADR-022 cross-ref), `specs/112-attack-path-pages/spec.md` SC-004 (inverted — text fallback is no longer a supported shipping mode), `specs/112-attack-path-pages/research.md` line 80 (pymmdc factual correction: GPL-3.0 Node.js wrapper, NOT a pure-Python renderer) plus a Durable Decision Rationale block.
106+
- **New ADR-022** (`docs/architecture/02_ADRs/ADR-022-mmdc-hard-prerequisite.md`) — the first ADR in tachi governing CLI-prerequisite posture. Establishes the new rule: **pipeline is fail-loud when a required CLI is absent, gated on input detection** (mmdc is required only when attack trees are present). Cross-references ADR-014 (optional external APIs) and ADR-021 (determinism). "Future Work" clause flags extraction of an `install.sh` prerequisite helper if a third CLI prerequisite is ever added.
107+
- **New CI workflow** (`.github/workflows/tachi-mmdc-preflight.yml`) — runs on `ubuntu-latest` (no mmdc preinstalled), installs Typst + Python 3.11, asserts the pipeline aborts non-zero on `examples/mermaid-agentic-app/` with all 3 canonical tokens in stderr (`@mermaid-js/mermaid-cli`, `npm install -g @mermaid-js/mermaid-cli`, `Attack path rendering`). Includes team-lead T4 enforcement assertion guarding against transitive mmdc install.
108+
- **New test file** `tests/scripts/test_mmdc_preflight.py` (9 tests: 4 preflight + 5 mid-render aggregator). Full pytest suite: 48/48 green. Backward-compatibility suite: 5/5 baselines byte-identical under `SOURCE_DATE_EPOCH=1700000000` (per ADR-021) — happy path (mmdc present) is byte-identical pre/post refactor.
109+
- **Seven-location canonical command consistency** (Architect refinement R4): the install command `npm install -g @mermaid-js/mermaid-cli` appears in exactly 7 enforcement locations (extract-report-data.py raise, tachi.security-report.md shell echo, install.sh warning, README Prerequisites, test_mmdc_preflight.py assertion, tachi-mmdc-preflight.yml grep, ADR-022 decision body). Verified via T023 grep consistency check.
110+
- **Breaking change (explicit, documented)**: silent text fallback is no longer a supported shipping mode. Previously-broken PDFs (Mermaid source dumped verbatim into PDF when mmdc was absent) now produce a loud preflight error instead. Deliberate correctness improvement — documented in spec 112 SC-004 (inverted) and the Feature 130 PRD Risk 130.2.
101111
- **Feature 136**: MAESTRO Canonical Layer Correctness Fix
102112
- Renamed MAESTRO L5/L6/L7 to canonical CSA names per Ken Huang authoritative definition: L5 "Security" → "Evaluation and Observability", L6 "Agent Ecosystem" → "Security and Compliance", L7 "User Interface" → "Agent Ecosystem". Corrected MAESTRO acronym expansion to "Multi-Agent Environment, Security, Threat, Risk, and Outcome".
103113
- Schema bump: `schemas/finding.yaml` `maestro_layer` enum values renamed; `schema_version` bumped 1.2 to 1.3. Establishes new **enum-value-only minor-bump rule**: enum-value-only breaking changes warrant a minor schema bump (x.y+1), not major, provided schema shape and required fields unchanged. Rule documented in ADR-020 Revision History as precedent for future enum corrections.

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,35 @@ tachi is built with the [Agentic Oriented Development Kit (AOD Kit)](https://git
2727

2828
---
2929

30+
## Prerequisites
31+
32+
tachi requires two external CLIs for full functionality. Both are required — `typst` compiles the PDF security report and `@mermaid-js/mermaid-cli` (`mmdc`) renders attack path diagrams. See [ADR-022](docs/architecture/02_ADRs/ADR-022-mmdc-hard-prerequisite.md) for the rationale.
33+
34+
**macOS**:
35+
36+
```bash
37+
brew install typst
38+
npm install -g @mermaid-js/mermaid-cli
39+
```
40+
41+
**Linux** (Debian/Ubuntu):
42+
43+
```bash
44+
apt install typst # or: cargo install typst-cli / dnf install typst on Fedora
45+
npm install -g @mermaid-js/mermaid-cli
46+
```
47+
48+
**WSL** (use your distro's package manager, same as Linux):
49+
50+
```bash
51+
apt install typst
52+
npm install -g @mermaid-js/mermaid-cli
53+
```
54+
55+
`/tachi.security-report` aborts at preflight with a clear install command if either CLI is missing when attack-trees are present.
56+
57+
---
58+
3059
## Quick Start
3160

3261
### 1. Clone tachi (one-time)

docs/architecture/00_Tech_Stack/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,14 +269,14 @@ python3 -m pytest tests/ --cov=scripts # with coverage
269269
| `gh` | `github-lifecycle.sh`, `run-state.sh` (optional), `scripts/init.sh` (optional) | GitHub Issue/label management, Projects board creation during init | `brew install gh` / `gh auth login` |
270270
| `python3` | `scripts/extract-report-data.py` (invoked by report-assembler agent) | Deterministic markdown-to-Typst data extraction for security report pipeline; stdlib only, no pip dependencies (Feature 067) | Pre-installed on macOS; `apt-get install python3` (Linux) |
271271
| `typst` | `/tachi.security-report` command (report-assembler agent) | PDF compilation from modular `.typ` templates; renders security assessment reports with brand assets, auto-generated TOC, and conditional page inclusion (Feature 054, extended Feature 060); MAESTRO Findings page conditionally included via `has-maestro-data` flag (Feature 091); Attack Path pages conditionally included via `has-attack-trees` flag (Feature 112) | `brew install typst` (macOS) / `cargo install typst-cli` / [typst.app](https://github.com/typst/typst/releases) |
272-
| `mmdc` | `scripts/extract-report-data.py` (invoked by report-assembler agent, optional) | Mermaid CLI for rendering attack tree Mermaid diagrams to PNG images for PDF report embedding; graceful fallback to raw Mermaid text display when unavailable (Feature 112) | `npm install -g @mermaid-js/mermaid-cli` |
272+
| `mmdc` | `scripts/extract-report-data.py` (invoked by report-assembler agent) | Mermaid CLI for rendering attack tree Mermaid diagrams to PNG images for PDF report embedding; hard prerequisite as of Feature 130 when `attack-trees/` contains Critical/High findings — see [ADR-022](../02_ADRs/ADR-022-mmdc-hard-prerequisite.md) (introduced Feature 112, posture changed Feature 130) | `npm install -g @mermaid-js/mermaid-cli` |
273273
| `pytest` | `tests/scripts/*.py` (developer-only; not runtime) | Python test runner for the `scripts/*.py` extraction pipeline and `tachi_parsers.py` shared module; invoked via `make test` or `python3 -m pytest tests/` (Feature 128) | `pip install -r requirements-dev.txt` |
274274

275275
**Note**: `gh` degrades gracefully -- the orchestrator falls back to artifact-only detection when `gh` is unavailable or unauthenticated. Similarly, `scripts/init.sh` skips GitHub Projects board creation when `gh` is missing, unauthenticated, or lacks the `project` OAuth scope, reporting status in the init summary with remediation guidance.
276276

277277
**Note**: `typst` is required only for PDF report generation. The `/tachi.security-report` command validates Typst installation before compilation and reports a clear error if unavailable. All other tachi commands operate without Typst.
278278

279-
**Note**: `mmdc` (Mermaid CLI) is optional. When absent, the `/tachi.security-report` command still generates attack path pages but renders the raw Mermaid diagram text instead of a PNG image. Install via `npm install -g @mermaid-js/mermaid-cli` for rendered diagrams.
279+
**Note**: `mmdc` (Mermaid CLI) is a **hard prerequisite** when `attack-trees/` contains Critical/High findings. The `/tachi.security-report` pipeline aborts at preflight with a non-zero exit code and a canonical three-line error message (naming the missing tool, the reason, and the exact install command `npm install -g @mermaid-js/mermaid-cli`) if `mmdc` is not on `PATH`. Projects without attack trees continue to work without `mmdc` — the gate fires only when attack-tree artifacts are present. See [ADR-022](../02_ADRs/ADR-022-mmdc-hard-prerequisite.md) for the rationale and the silent-fallback-to-loud-failure migration (Feature 130).
280280

281281
### External API Dependencies (Optional)
282282

0 commit comments

Comments
 (0)