| name | shipwright-compliance |
| description | On-demand detective audit for cross-artifact consistency plus auto-background compliance documentation. Scans specs, plans, configs, event log, ADRs, and compliance reports for drift that the preventive Canon gate and reactive Phase-Quality Stop hook don't catch. Still produces the traceability matrix, test evidence, change history, SBOM, and dashboard when auto-background updates fire between phases.
TRIGGER when: user wants a cross-artifact consistency audit, drift check, FR coherence verify, ADR integrity check, compliance report, audit documentation, traceability matrix, test evidence report, SBOM, or change history. Also when user asks about regulatory compliance, audit readiness, or documentation for auditors.
DO NOT TRIGGER when: user asks to write code (/shipwright-build), run tests (/shipwright-test), fix a bug (/shipwright-iterate), deploy (/shipwright-deploy), create requirements (/shipwright-project), plan implementation (/shipwright-plan), or design UI (/shipwright-design). If the user just wants to *add a feature*, route to /shipwright-iterate — do not accidentally consume 'add' / 'feature' prompts. |
| license | MIT |
| compatibility | Requires uv (Python 3.11+), git repository required |
Shipwright Compliance Skill
Detective cross-artifact audit (/shipwright-compliance) plus the auto-background compliance-doc generation that fires after every phase completion. Plan v7 Option Z — plugin and command name unchanged; behavior expanded.
Positioning (3-layer):
| Layer | What | When |
|---|
| Preventive (Canon) | phase_validators.py at phase-complete | every phase |
| Reactive (Phase-Quality) | Stop-hook audit_phase_quality_on_stop.py | every session end |
| Detective (this skill) | /shipwright-compliance on-demand | explicit user invocation |
CRITICAL: First Actions
Governing rules: Read and follow shared/constitution.md (ALWAYS / ASK FIRST / NEVER boundaries).
A. Print Intro Banner
================================================================================
SHIPWRIGHT-COMPLIANCE: Detective Audit
================================================================================
Cross-artifact consistency scan.
Usage:
/shipwright-compliance # full audit, writes report
/shipwright-compliance --fix # also regenerate stale docs (Group E)
/shipwright-compliance --only C,F # restrict to specific groups
/shipwright-compliance --format json # JSON output only
Groups:
A — Artifact + path integrity (A2 dev-block npm/uv/make, A3 [project.scripts], A4 config path-fields, A5 CI security workflow integrity incl. A5.8 behavioral gate probe)
B — Config ↔ Config ↔ Event-log coherence (splits, sections, events, reverse git scan)
C — Planning internal coherence (preventive re-run of plan_checks)
D — Event-log FR coverage (uncovered FRs, stale refs, promised-not-delivered, last-build state)
E — Compliance-doc content staleness (regen + byte compare; --fix rewrites stale docs)
F — ADR structural integrity (preventive re-run)
G — Agent-docs freshness vs. git activity (G2 conventional-commit scope, G3 ADR-ID body refs)
H — Bloat-policy detective audit (new crossings of the 300/400-line budgets + ratchets vs shipwright_bloat_baseline.json; Campaign A.review)
Reports written (both gitignored — transient):
- .shipwright/compliance/audit-report.md ← human-readable summary
- .shipwright/compliance/audit-report.json ← structured payload (also on stdout)
================================================================================
B. Discover Plugin Root
The SessionStart hook injects SHIPWRIGHT_PLUGIN_ROOT=<path>. Use it directly.
Step 1: Parse Arguments
Accept these flags (pass through to run_audit.py):
--fix — enable Group E per-doc auto-regeneration (writes to working tree, does not commit).
--only <groups> — comma-separated group letters (e.g. C,F,E). Defaults to all.
--format md|json|both — output format. Default both.
One flag is not passed through and is handled by this skill directly:
--refresh-pr — skip the audit entirely and run Step 2c instead: open a
documents-only pull request that brings the committed evidence up to date.
Step 2: Run the Audit
uv run "{plugin_root}/scripts/audit/run_audit.py" \
--project-root "$(pwd)" \
[--fix] [--only {groups}] [--format {format}]
Exit codes:
0 — all groups passed (or only skipped/WARN-level findings).
1 — at least one check failed. Report still written.
2 — --project-root missing.
3 — iterate-12 import-gate trip. See audit-report.md → "Import Gate Error" for the drift detail.
Step 2b: Refresh the Evidence Documents
uv run "{plugin_root}/scripts/tools/update_compliance.py" \
--project-root "$(pwd)" --phase compliance
The audit records that it ran (shipwright_compliance_config.json → last_audit),
and every evidence document discloses that date on its Generated: line. Without
this step those documents keep reporting the previous answer — often "never
run" — right at the moment the operator asked for the check. Run it after every
audit, including a failing one. It regenerates documents only; it commits nothing.
Step 2c: Open a Documents-Only PR (--refresh-pr only)
The seven documents under .shipwright/compliance/ are refreshed at each
release and on demand, not continuously — an iterate branch must not carry them
(a branch-local derivation reads the branch's history and an event log missing
every concurrently-merging branch, so it is wrong for the default branch by
construction; check_no_derived_snapshots_committed enforces it). This step is
the on-demand half: when somebody needs the committed evidence current between
releases, they run this and it goes through an ordinary reviewed pull request.
Offer this whenever an audit reveals the committed documents are behind, and run
it when the operator asks for fresh evidence. Requires a clean checkout of an
up-to-date default branch — the tool refuses otherwise rather than repairing,
because a refresh computed on top of unrelated local work describes the wrong
tree no matter how carefully it is committed.
Reset the tree first. Step 2b has just regenerated the five markdown
documents, so the checkout is dirty — and it is dirtiest in exactly the case that
triggers this offer, because documents that were behind are the ones Step 2b
moves. Without this the preflight refuses every time it is actually needed:
uv run "{plugin_root}/../../shared/scripts/tools/refresh_compliance_docs.py" \
--project-root "$(pwd)" --restore
uv run "{plugin_root}/../../shared/scripts/tools/refresh_compliance_docs.py" \
--project-root "$(pwd)" --pr
status: "pr_opened" → report the URL in pr.
status: "noop" → the committed documents already match. Nothing to do.
status: "refused" → detail says what to fix first (dirty tree, or behind).
status: "base_moved" → the default branch advanced mid-run. The work is kept
on the local branch named in detail; re-run from a fresh base.
- any other status → do not work around it. Each refusal exists because a
refresh that reports green while shipping frozen or emptied documents is worse
than one that stays frozen.
No credential beyond the operator's own gh login. There is no bot, no
deploy key, and no exception to the branch-protection rule anywhere in this path
— that is the whole point of it. Never pass --release: a documents-only branch
shipped with no release, and the tool refuses the combination rather than
claiming one. Full rationale:
.shipwright/planning/iterate/2026-07-30-derived-snapshots-decision.md.
Step 3: Present Results
Parse the JSON stdout (report.any_fail, findings, fixes_applied, groups_run, groups_skipped).
Render to the user:
================================================================================
DETECTIVE AUDIT: {PASS|FAIL}
================================================================================
Groups run: {letters}
Groups skipped: {letters + reasons}
Findings by group:
A {fail}/{skip}/{pass}
B {fail}/{skip}/{pass}
...
Top failures (detective-only):
- B7 "commit on main without work_completed event" — {evidence}
- E1 "RTM stale after FR-08.03 added" — diff at line 142
Top failures (preventive re-run):
- C2 "plan.md references FR-05.02 not in spec.md"
- F3 "ADR-041 superseded but no replacement linked"
Fixes applied:
- .shipwright/compliance/traceability-matrix.md (regenerated)
Report:
.shipwright/compliance/audit-report.md
.shipwright/compliance/audit-report.json
================================================================================
For each failing detective finding, the report contains a copy-pasteable /shipwright-iterate command. Do not auto-run it — surface the suggestion and let the user decide.
Auto-Background Compliance Updates
Separately from /shipwright-compliance, shipwright-run's orchestrator calls scripts/tools/update_compliance.py --phase <name> after every completed pipeline phase. That code path is unchanged by plan v7; it still regenerates the affected subset of compliance docs:
| Phase | Reports Updated |
|---|
| project | RTM, Dashboard |
| plan | RTM, Dashboard |
| build | RTM, Test Evidence, Change History, Dashboard, SBOM |
| test | Test Evidence, Dashboard |
| deploy | Dashboard |
| changelog | RTM, Test Evidence, Change History, Dashboard, SBOM |
| iterate | RTM, Test Evidence, Change History, Dashboard, SBOM |
No user interaction needed for auto-background mode. When the compliance plugin is missing (e.g. a partial install), the orchestrator now loud-fails with a stderr warning and writes a compliance_update_failed event — so a broken install is visible rather than silently skipped (plan v7, Step 1).
Coverage notes & follow-up
All Plan-v7 groups (A, B, C, D, E, F, G) are wired. The CLI no longer
falls through to groups_skipped=[(letter, "not-implemented")] for any
group — individual checks may still self-skip when their preconditions
are absent (e.g. B7 / G2 / G3 skip on a non-git repo or before the first
release tag).
Adopted-status note (B1 / B4): Both checks scope themselves to
splits with status="complete". Adopted projects (status="adopted")
intentionally fall outside that scope: adopted code is treated as
retroactively complete without a planning artifact and without a
split_completed event — there's nothing for B1 (sections recorded) or
B4 (matching event) to compare against. Coverage for adopted projects
comes from B7 (commit-on-default-branch ↔ event match) and Group G
(commit-quality scans), which both run regardless of split status.
All nine detective groups (A–I) are wired — the seven Plan-v7 groups
(A–G), Group H (bloat-policy audit, Campaign A.review) and Group I
(requirement hygiene vs shared/fr-authoring.md; I1–I3 and I6 advisory,
I4/I5 fail). The
post-Plan-v7 A5 follow-up (CI security workflow integrity) is live. A5 ships in the Group A
rollup via a composite registry handler that merges A2/A3/A4 (group_a)
and A5 (group_a5) findings.
audit_config.json schema (extend in audit_detector._DEFAULT_CONFIG):
a4_path_fields — dotted paths into shipwright_*_config.json whose
string values must point to existing files. Grammar: key (descend),
key[] (iterate list), key{} (iterate dict values). Defaults cover
plan_config.splits.{}.plan_file (multi-split layout) and
plan_config.spec_file (single-split layout).
g2_stoplist — conventional-commit scopes G2 should silently accept
(generic / cross-cutting scopes that don't map to a split).
g2_alias_map — split-name → variant list. A scope passes G2 when it
matches any alias-map value or a known splits[].name.
b7_exclusions.exclude_merge_commits, exclude_authors,
exclude_path_prefixes — rule data for B7.
b7_exclusions.last_release_tag_pattern (default v*) — glob passed
to git describe --tags --match.
retention.rule_a/rule_b/rule_c — per-rule on/off switches for B7
(lets users keep rule data while disabling individual rules for
archaeology runs).
a5_workflow_path / a5_required_permissions /
a5_critical_gate_step_id / a5_sarif_category — escape hatches for
projects that legitimately diverge from the convention-lock at
shared/scripts/lib/security_workflow.py. null (default) means
"consume the constant"; bad-type overrides fall back to the constant
rather than crashing the audit.