Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .claude/commands/tachi.security-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,18 @@ Single-command entry point for tachi security assessment PDF generation — the
```
- Halt if not installed.

2. **Auto-detect artifacts in target directory**:
2. **Check mmdc is installed when attack trees are present**:
- 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.
- If attack trees are present, run `command -v mmdc >/dev/null 2>&1` to verify mmdc CLI is available.
- If mmdc is not found, display to stderr:
```
Attack path rendering requires @mermaid-js/mermaid-cli (mmdc).
Install with: npm install -g @mermaid-js/mermaid-cli
Then re-run /tachi.security-report.
```
- 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.

3. **Auto-detect artifacts in target directory**:

Scan `target_dir` for the following 7 artifact types:

Expand Down
130 changes: 130 additions & 0 deletions .github/workflows/tachi-mmdc-preflight.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# =============================================================================
# tachi mmdc preflight gate — CI fresh-install acceptance test (FR-130.7)
# =============================================================================
#
# Purpose: Prove the Feature 130 preflight gate fires in a shell where
# `mmdc` (@mermaid-js/mermaid-cli) is NOT on PATH. ubuntu-latest ships
# without mmdc (plan.md spike S3), so omitting the install step satisfies
# the "mmdc absent" precondition automatically.
#
# The workflow:
# 1. Installs Typst + Python 3.11 (but NOT mmdc)
# 2. Asserts mmdc is absent (guards against transitive installs — plan Risk #6)
# 3. Runs scripts/extract-report-data.py against examples/mermaid-agentic-app/
# (direct Python invocation — slash commands cannot run in CI)
# 4. Asserts non-zero exit code
# 5. Asserts the canonical three tokens from the RuntimeError message:
# - @mermaid-js/mermaid-cli
# - npm install -g @mermaid-js/mermaid-cli
# - Attack path rendering
#
# References:
# - specs/130-prd-130-fix/plan.md (spike S3 + Risk #6)
# - specs/130-prd-130-fix/tasks.md (T018)
# - docs/architecture/02_ADRs/ADR-022-mmdc-hard-prerequisite.md
# - scripts/extract-report-data.py:render_mermaid_to_png (preflight gate)
# =============================================================================

name: tachi mmdc preflight

on:
pull_request:
paths:
- scripts/extract-report-data.py
- templates/tachi/security-report/attack-path.typ
- scripts/install.sh
- .claude/commands/tachi.security-report.md
- README.md
- .github/workflows/tachi-mmdc-preflight.yml

permissions:
contents: read

jobs:
preflight-fresh-install:
name: Verify preflight gate fires when mmdc is absent
# ubuntu-latest ships WITHOUT @mermaid-js/mermaid-cli (plan.md spike S3
# confirmed). Do not switch to a custom image that preinstalls mmdc.
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"

# Typst setup. typst-community/setup-typst@v5 is the current stable
# major tag at time of authoring (T018). This step MUST NOT transitively
# install @mermaid-js/mermaid-cli; the next step will fail the job if
# that invariant is ever broken by an upstream action change.
- name: Set up Typst
uses: typst-community/setup-typst@v5

# Architect refinement R3 (Medium): make the intentional absence of
# mmdc visible in CI logs for future debuggers.
- name: Diagnostic — show mmdc absence
run: |
which mmdc || echo "expected absence: mmdc not on PATH, preflight gate should fire"

# Team-lead T4 enforcement assertion (Medium) / plan Risk #6 mitigation.
# Guards against future transitive installs of mmdc via Typst setup or
# other actions. If mmdc unexpectedly appears on PATH, the S3 spike
# assumption is broken and this test is no longer meaningful — fail fast.
- name: "Enforce mmdc absence (plan Risk #6)"
run: |
if command -v mmdc >/dev/null 2>&1; then
echo "FATAL: mmdc unexpectedly present on PATH. S3 spike assumption broken."
exit 1
fi

# Direct Python invocation of the extraction script. Claude Code slash
# commands cannot run in CI — we call scripts/extract-report-data.py
# with its required CLI args (--target-dir, --output, --template-dir).
# The preflight gate in render_mermaid_to_png() fires before any file
# is written, so --output is only needed to satisfy argparse.
- name: Run extract-report-data.py against mermaid-agentic-app (expect failure)
run: |
set +e
python3 scripts/extract-report-data.py \
--target-dir examples/mermaid-agentic-app \
--output /tmp/report-data.typ \
--template-dir templates/tachi/security-report \
>/tmp/out.txt 2>&1
EXIT=$?
set -e
echo "---- captured output (stdout + stderr) ----"
cat /tmp/out.txt
echo "---- end captured output ----"
echo "exit code: $EXIT"
if [ "$EXIT" -eq 0 ]; then
echo "FAIL: expected non-zero exit code, got 0. Preflight gate did not fire."
exit 1
fi
echo "OK: non-zero exit code as expected ($EXIT)"

# Assert all three canonical tokens from the preflight RuntimeError
# message appear in the captured output. Grep each token individually
# so a missing token produces a specific, actionable error.
- name: Assert canonical error tokens present
run: |
MISSING=0
if ! grep -q '@mermaid-js/mermaid-cli' /tmp/out.txt; then
echo "FAIL: canonical token missing: @mermaid-js/mermaid-cli"
MISSING=1
fi
if ! grep -q 'npm install -g @mermaid-js/mermaid-cli' /tmp/out.txt; then
echo "FAIL: canonical token missing: npm install -g @mermaid-js/mermaid-cli"
MISSING=1
fi
if ! grep -q 'Attack path rendering' /tmp/out.txt; then
echo "FAIL: canonical token missing: Attack path rendering"
MISSING=1
fi
if [ "$MISSING" -ne 0 ]; then
echo "One or more canonical tokens were missing from the preflight error output."
echo "This means the FR-130.1 preflight gate either did not fire or emitted a non-canonical message."
exit 1
fi
echo "OK: all three canonical tokens present in preflight error output"
32 changes: 32 additions & 0 deletions .security/reports/2c97e8091c13.sarif
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"version": "2.1.0",
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"runs": [
{
"tool": {
"driver": {
"name": "aod-security",
"version": "1.0.0",
"informationUri": "https://github.com/anthropics/agentic-oriented-development",
"rules": []
}
},
"results": [],
"invocations": [
{
"executionSuccessful": true,
"endTimeUtc": "2026-04-11T15:47:02Z",
"properties": {
"scan_id": "65936668-f62b-4548-a9bd-e503c6e114ff",
"branch": "130-prd-130-fix",
"commit_sha": "2c97e8091c13",
"files_scanned": 3,
"manifests_audited": 0,
"status": "PASSED",
"note": "No findings detected. AI-powered analysis — supplement with dedicated SAST tools for production-critical systems."
}
}
]
}
]
}
1 change: 1 addition & 0 deletions .security/scan-log.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
{"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"}
{"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"}
{"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"}
{"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"}
10 changes: 10 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ When invoked as a subagent (via Agent tool), return ONLY:
- Review `agent-assignments.md` for workload distribution

## Recent Changes
- **Feature 130**: Fix Attack Path Mermaid Rendering When mmdc Is Not Installed
- 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).
- 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`.
- 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.
- 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.
- **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.
- **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.
- **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.
- **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.
- **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.
- **Feature 136**: MAESTRO Canonical Layer Correctness Fix
- 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".
- 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.
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,35 @@ tachi is built with the [Agentic Oriented Development Kit (AOD Kit)](https://git

---

## Prerequisites

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.

**macOS**:

```bash
brew install typst
npm install -g @mermaid-js/mermaid-cli
```

**Linux** (Debian/Ubuntu):

```bash
apt install typst # or: cargo install typst-cli / dnf install typst on Fedora
npm install -g @mermaid-js/mermaid-cli
```

**WSL** (use your distro's package manager, same as Linux):

```bash
apt install typst
npm install -g @mermaid-js/mermaid-cli
```

`/tachi.security-report` aborts at preflight with a clear install command if either CLI is missing when attack-trees are present.

---

## Quick Start

### 1. Clone tachi (one-time)
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture/00_Tech_Stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,14 +269,14 @@ python3 -m pytest tests/ --cov=scripts # with coverage
| `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` |
| `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) |
| `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) |
| `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` |
| `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` |
| `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` |

**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.

**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.

**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.
**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).

### External API Dependencies (Optional)

Expand Down
Loading
Loading