Architecture — Abstractions
Fragment of .correctless/ARCHITECTURE.md. Entry headings are indexed in the root document; full bodies live here.
ABS-001: Shared script library (scripts/lib.sh)
- What: Shared bash utilities sourced by hooks and phase-transition scripts. Provides path helpers (branch_slug, repo_root, config_file, artifacts_dir), file classification (classify_file, read_patterns, read_intensity), state file locking (ABS-003), and write pattern detection (_has_write_pattern, get_target_file).
- Invariant: Functions in lib.sh have a single definition. Scripts must source lib.sh rather than duplicating functions locally.
- Enforced at: scripts/lib.sh (source), hooks/workflow-advance.sh (consumer), hooks/workflow-gate.sh (consumer), hooks/sensitive-file-guard.sh (consumer), hooks/audit-trail.sh (consumer), hooks/statusline.sh (consumer), scripts/antipattern-scan.sh (consumer), scripts/compute-session-cost.sh (consumer), scripts/build-dashboard.sh (consumer), scripts/cross-feature-intel.sh (consumer), scripts/prune-scan.sh (consumer), scripts/wf/transitions.sh (indirect consumer via dispatcher scope), scripts/wf/utility.sh (indirect consumer via dispatcher scope), scripts/wf/metadata.sh (indirect consumer via dispatcher scope)
- Violated when: A hook or script defines branch_slug(), classify_file(), _has_write_pattern(), _acquire_state_lock(), or any other lib.sh function locally instead of sourcing the library
- Test: R-019e in antipattern-scan tests (verifies workflow-advance.sh does not define branch_slug locally), R-021 in test-lib-locking.sh (no flock dependency)
ABS-002: Ephemeral in-context classification (shift-left review)
- What: Historical findings are classified into pattern classes by the LLM during review. Classifications exist only in the review agent’s context — not persisted, not stable across invocations, not accessible to other agents.
- Invariant: No feature may depend on classification stability or persistence. Reliable classification requires a persistent index (deferred to v2).
- Enforced at: skills/creview/SKILL.md, skills/creview-spec/SKILL.md (R-003 instructions)
- Violated when: A skill reads “pattern classes” from a prior review session, or assumes the same findings produce the same classifications
- Test: None (enforced by documentation, not runtime)
ABS-003: State file locking (scripts/lib.sh)
- What: Advisory locking for state file read-modify-write operations. Two implementations, selected at runtime by
_lock_use_flock(override withCORRECTLESS_LOCK_IMPL=ln): (1) flock — the default when theflockbinary is present (Linux/CI). Kernel advisory locking on a persistent, never-deleted${state_file}.flockfile held via an open fd (kept in__CL_LOCK_FDS). Race-free by construction, and auto-released by the kernel if the holder dies — no PID stale-detection needed. This is preferred because file-creation advisory locks cannot guarantee mutual exclusion under adversarial scheduling (observed as intermittent lost-update/double-hold on shared CI runners). (2) ln fallback — whenflockis absent (e.g. macOS default) or forced off. An atomic hard-link create-with-content advisory lock on${state_file}.lock/pid: write$$to a private sibling temp, thenln(2)it onto the pid path (fails if the target exists; the pid content is present the instant the file is visible — no empty-pid window).mkdir -pruns inside the retry loop and the temp is a sibling oflock_dirso a releaser’srmdircannot starve a waiter; only akill -0-dead holder is reclaimed (atomicmv). History: the lock moved mkdir → O_EXCL → ln → flock+ln —mkdirwas non-atomic on the sandbox overlay, the O_EXCL grace-loop raced on CI, and ln (while better) still couldn’t guarantee exclusion under CI’s adversarial scheduling; only kernel flock is truly race-free. Defense-in-depth — Claude Code likely serializes hook calls, but locking protects against manual CLI invocations and concurrent skill writers. - Invariant: All state file modifications must go through write_state() (workflow-advance.sh) or locked_update_state() (lib.sh). No hook or script may modify state files via raw jq-to-file without holding the lock. flock path: the
${state_file}.flockfile is persistent (never deleted — deleting it while waiters hold fds would reopen a double-hold window); release isflock -u+ close fd, and any2>/dev/nullon the fdexecMUST wrap a{ ...; }group (a redirection onexecitself is permanent and would silence later diagnostics). ln fallback:_release_state_lockis holder-owned ($$), tears down withrm -f "$lock_dir/pid"then a best-effortrmdir— never a blindrm -rf; the pid temp is a sibling oflock_dirandmkdir -pruns inside the loop so a releaser’srmdircannot starve a waiter. External reusers (e.g.scripts/meta-record.sh, ABS-047) call_acquire_state_lock/_release_state_lockdirectly and MUST NOT invent a bespoke lock (PRH-006 of the calibration-writer spec). - Enforced at: scripts/lib.sh (_acquire_state_lock, _release_state_lock, locked_update_state), hooks/workflow-advance.sh (write_state), hooks/workflow-gate.sh (override decrement), scripts/wf/transitions.sh (locked_update_state consumer via dispatcher scope), scripts/wf/utility.sh (locked_update_state consumer via dispatcher scope), scripts/wf/metadata.sh (locked_update_state consumer via dispatcher scope), scripts/meta-record.sh (direct reuser of the lock helpers — ABS-047)
- Violated when: A hook or script modifies a workflow-state-*.json file without calling _acquire_state_lock first; the flock lock file is deleted on release (reopens a double-hold window) or a
2>/dev/nullis placed on an fdexec(permanently silences the shell’s stderr); in the ln fallback, mutual exclusion is gated onmkdiralone (or an empty-then-write pid create) rather than the atomiclncreate-with-content, the pid temp is staged insidelock_dir,mkdir -pis hoisted out of the retry loop, or a release uses a blindrm -rf - Test: R-015d/e in test-lib-locking.sh (static analysis verifying write_state and gate reference locking functions), R-021 (locking prefers flock with an ln/mkdir fallback), QA-002 in test-lib-locking.sh (N-way concurrent acquire on the flock path — no lost update, single-holder-in-critical-section — race-free regression guard; the deterministic R-015..R-019 tests force
CORRECTLESS_LOCK_IMPL=lnto cover the fallback), R-020d/e in test-gate-path-exceptions.sh
ABS-004: Hook metadata headers for auto-registration
- What: Comment-based metadata headers (
# HOOK_TYPE:and# HOOK_MATCHER:) in the first 10 lines of hook files. Setup’sregister_hooks()reads these headers to auto-generate settings.json entries. Files without headers are installed but not registered (INV-006). - Invariant: Every hook in
hooks/must have both headers. HOOK_TYPE is one of the registered event types in the generalizedKNOWN_HOOK_TYPESmap — currentlyPreToolUse,PostToolUse, andInstructionsLoaded(added by the instructionsloaded-hook feature, 2026-07-01). HOOK_MATCHER is a pipe-separated tool list for Pre/PostToolUse, or*for InstructionsLoaded (which filters by load reason, so*is required to see all rule-file loads — see the hook’s own INV-002 scope filter). Timeout is looked up from the singleKNOWN_HOOK_TYPEStype→timeout map (PostToolUse=1000ms; all other types incl. Pre/InstructionsLoaded=5000ms) and emitted astimeout_ms— not a hardcoded per-typecasearm or a duplicated literal. Adding a new hook type requires only aKNOWN_HOOK_TYPESentry, no new registration statement (AP-024/PMB-003 avoidance). - What (mechanism):
register_hooks()discovers hooks into a single loop gated byKNOWN_HOOK_TYPESand emits each via the shared_upsert_command_hook/_upsert_agent_hookhelpers, which run identically across all four registration seams — fresh-install, existing-settings update, matcher-drift repair, and invalid-settings regeneration. The refactor from the prior hardcoded 2-type dispatch is verified by test-ci-hook-wiring.sh IL-INV-006/007/013. - Enforced at: setup (register_hooks reads headers, KNOWN_HOOK_TYPES map, upsert* helpers), hooks/*.sh (headers present)
- Violated when: A new hook is added to hooks/ without both metadata headers; a header value is malformed; a hook type is wired via a bespoke
case/ifarm or a duplicated timeout literal instead of the KNOWN_HOOK_TYPES map; or any of the four seams emits some types but omits a KNOWN_HOOK_TYPES type - Test: INV-002 in test-ci-hook-wiring.sh (header format validation), INV-004 (register_hooks reads headers), IL-INV-006 (InstructionsLoaded registered: matcher
*,timeout_ms, type command), IL-INV-007 (generalized map — positive structural assertion that InstructionsLoaded appears only in KNOWN_HOOK_TYPES, never a bespoke per-type arm), IL-INV-013 (all four seams emit it)
ABS-005: Cross-skill calibration data (.correctless/meta/)
- What: Outcome data written by one skill (cverify) and read by another (cspec) across sessions. Lives in
.correctless/meta/intensity-calibration.json, not in workflow state files (which are per-branch and ephemeral). - Invariant: cverify is the sole producer, but it writes through the sanctioned
scripts/meta-record.sh calibration-appendwriter (ABS-047), never via Write/Edit —intensity-calibration.jsonis SFG-protected, so a naive Edit/Write is blocked (AP-037). cspec is read-only — it never writes, modifies, or deletes calibration entries. Recency window (50 entries) caps read size. The append is deep-equal-preserving over prior entries (INV-001 of the calibration-writer spec). - Enforced at: skills/cverify/SKILL.md (invokes
bash .correctless/scripts/meta-record.sh calibration-append), scripts/meta-record.sh (sanctioned writer — ABS-047), skills/cspec/SKILL.md (read-only instructions), skills/cmetrics/SKILL.md (read-only consumer — fix_rounds_triggered warning), tests/test-intensity-calibration.sh (INV-007, PRH-001), tests/test-meta-record.sh (writer contract) - Violated when: A skill other than cverify writes to intensity-calibration.json, or calibration data appears in workflow state files
- Test: grep cspec SKILL.md for write/append/create referencing calibration (must find none); grep workflow-advance.sh for calibration fields (must find none)
ABS-006: Token-log JSONL contract (.correctless/artifacts/)
- What: Per-branch JSONL files at
.correctless/artifacts/token-log-{branch-slug}.jsonlrecording token usage. One producer (hooks/token-tracking.sh PostToolUse hook) writes mechanically on every Agent tool completion. Two consumers: cverify (sums total_tokens for calibration entries via deterministic jq) and cmetrics (aggregates per-feature cost tables and trend analysis). As of session-cost-analysis, thetotal_cost_usdand token usage fields produced by the PostToolUse hook are zeros because PostToolUse payloads do not include these fields (see Claude Code issue #11008). The hook’s metadata (phase, skill, timestamps, agent descriptions) remains useful. For real cost data, see ABS-026 (cost artifact computed from session transcripts). The cost artifact is the canonical source of USD cost. Token-log is retained for phase timing and skill metadata only. - Invariant: The hook is the sole mechanical producer. Every hook-produced entry includes a
skillfield derived from the workflowphasevia a hardcoded mapping (R-001 in token-tracking-skill-field spec). Skills may also append entries with their ownskillfield. Consumers should use theskillfield for category attribution when present, falling back tophasefor historical entries without it. Consumers must handle malformed lines (skip, not fail) and missing files (default to 0). Thetotal_tokensfield is the canonical cost metric — consumers sum this field, never compute from input_tokens + output_tokens independently. Consumers must never derive USD cost from token-log fields — use ABS-026 cost artifacts instead. - Enforced at: hooks/token-tracking.sh (producer), skills/cverify/SKILL.md (consumer — jq -R try/catch), skills/cmetrics/SKILL.md (consumer — per-feature table)
- Violated when: A consumer modifies or deletes token-log files, a consumer fails on malformed JSONL instead of skipping, total_tokens is recomputed from sub-fields, or USD cost is derived from token-log fields
- Test: PRH-001 in token-aware-intensity tests (hook unchanged); INV-001d (jq summation); INV-001e (malformed skip)
ABS-007: Escalation file contract (.correctless/artifacts/)
- What: Structured escalation files at
.correctless/artifacts/escalation-{branch_slug}.md. Written by/cautowhen the pipeline encounters a persistent failure or architectural decision requiring human input. Uses YAML frontmatter for machine-parseable fields (completed_skills, failed_skill, failed_at_phase, failed_at_substep, attempts_before_escalation, pipeline_config) with human-readable prose below. - Invariant:
/cautois the sole writer. Consumers are/cauto(resumption via R-016) and the human (context for decision-making). The escalation file serves double duty as both the human-readable summary and the machine-readable checkpoint for pipeline resumption. - Enforced at: skills/cauto/SKILL.md (writer), R-016 resumption logic (reader)
- Violated when: A skill other than
/cautowrites escalation files, or the YAML frontmatter schema changes without updating the resumption parser - Test: R-005 in semi-auto-mode tests (frontmatter field presence), R-016 (resumption logic)
ABS-008: preferences.md contract (.correctless/)
- What: Project-level preference file at
.correctless/preferences.md. Scaffolded by/csetupfromtemplates/preferences.md. Read by/cautoand all pipeline skills. Edited by the human. Contains codified judgment calls: QA finding triage, documentation scope, commit granularity, escalation sensitivity, PR creation mode. - Invariant:
/csetupscaffolds the file (idempotent — never overwrites)./cautoand pipeline skills read preferences. The human is the sole editor. Falls back to built-in defaults when missing. - Enforced at: setup (scaffolding), skills/cauto/SKILL.md (reader), hooks/sensitive-file-guard.sh (protection), skills/_shared/constraints.md (reader instruction for all skills)
- Violated when: A skill writes to preferences.md, or the pipeline fails when preferences.md is absent
- Test: R-004 in semi-auto-mode tests (template categories), R-013 (setup scaffolding), R-019 (sensitive-file-guard protection)
ABS-009: Path-scoped rule files (.claude/rules/)
- What: Canonical location for path-scoped rule content at
.claude/rules/*.mdwith YAMLpaths:frontmatter. Claude Code loads a rule file’s body into the agent’s editing context whenever the agent opens a file that matches one of thepaths:entries. Migrated PAT entries live here as full-body rules;.correctless/ARCHITECTURE.mdretains only a 2-line index entry (heading + See-link) for each migrated PAT. - Invariant: For any migrated PAT entry, the rule text lives in exactly one location —
.claude/rules/{file}.md. ARCHITECTURE.md contains only the index entry (heading + See-link, nothing else). Duplication between ARCHITECTURE.md and a rule file is prohibited (PRH-001). Every rule file has YAML frontmatter with apaths:key, and the set ofpaths:entries forhooks-pretooluse.mdis set-equal to the set of PreToolUse hooks discovered viaHOOK_TYPE: PreToolUseheaders (INV-017 / ABS-004). - Enforced at:
tests/test-architecture-drift.sh,.claude/rules/hooks-pretooluse.md,.correctless/ARCHITECTURE.md(index entries),hooks/workflow-gate.sh+hooks/sensitive-file-guard.sh(in-file rule pointer comments per INV-021) - Violated when: the rule text is duplicated between ARCHITECTURE.md and a rule file, a rule file lacks
paths:frontmatter, a See-link points at a missing file, thepaths:list drifts from the discovered PreToolUse hook set, or the in-file pointer comment is missing from a scoped hook - Test: INV-001, INV-003, INV-004, INV-005, INV-017, INV-019, INV-021, INV-027 in
tests/test-architecture-drift.sh
ABS-010: Plugin-agent file contract (narrow)
- What: Plugin sub-agents live at
agents/{name}.mdin source and are propagated tocorrectless/agents/{name}.mdbysync.sh. Each file is the sole authoritative source for its named subagent’s system prompt and tool allowlist. The filename basename (without.md) equals the frontmattername:field. Skills invoke the agent via namespacedTask(subagent_type="correctless:{name}")— never inline blockquoted prompts, never baresubagent_type="{name}". Current consumers:skills/carchitect/SKILL.md(architecture-reviewer),skills/ctdd/SKILL.mdStep 4 RED phase (ctdd-red),skills/ctdd/SKILL.mdGREEN phase (ctdd-green),skills/caudit/SKILL.mdstep 6a (fix-diff-reviewer),skills/cauto/SKILL.mdTier 2 (decision-agent),skills/cauto/SKILL.mdTier 3 + review triage (supervisor),skills/cpr-review/SKILL.mdStep 3 (architecture-compliance-reviewer),skills/creview-spec/SKILL.mdStep 1 (review-spec-red-team, review-spec-assumptions, review-spec-testability, review-spec-design-contract, review-spec-upgrade-compat, review-spec-ux — read-only reviewers),skills/cspec/SKILL.mdStep 2 (cspec-research — network-read class). - Invariant: Single source of truth. No inline prompt duplication in any
skills/*/SKILL.mdfile. The source file underagents/and the distribution file undercorrectless/agents/are byte-equal. Frontmattername:equals filename basename. Frontmattertools:uses comma-flow form (per EA-006) and does NOT include any escalation tool. Three tool classes: write-tools (Write/Edit/Bash — permitted only when the agent’s role inherently requires file mutation, e.g., ctdd-red writes test files, ctdd-green writes source files), local-read-only (Read/Grep/Glob — for review and analysis agents), and network-read (WebSearch/WebFetch/Read/Grep — for agents that fetch external data but cannot modify project files, e.g., cspec-research). Read-only and network-read roles (fix-diff-reviewer, architecture-reviewer, architecture-compliance-reviewer, decision-agent, supervisor, review-spec-red-team, review-spec-assumptions, review-spec-testability, review-spec-design-contract, review-spec-upgrade-compat, review-spec-ux, cspec-research) must not include write tools. - Enforced at:
agents/*.md(producer), all consumer skills above,sync.sh(propagation + stale-file detection in both directions),tests/test-fix-diff-reviewer-agent.sh,tests/test-carchitect.sh, andtests/test-carchitect-phase4.sh(structural assertions covering frontmatter, distribution parity, inline-prompt denylist, and tool allowlist). - Violated when: an inline agent prompt reappears in any skill file; the frontmatter
name:drifts from the filename basename; a skill invokes the bare (non-namespaced) subagent_type form; the distribution copy diverges from the source; a read-only or network-read agent’s frontmatter tools list grows a write or escalation tool; a new agent file lands without a corresponding test update. - Test:
tests/test-fix-diff-reviewer-agent.sh,tests/test-carchitect.sh,tests/test-carchitect-phase4.sh,tests/test-ctdd-green-agent.sh,tests/test-creview-spec-agents.sh,tests/test-cspec-research-agent.sh
ABS-011: Decision record (.correctless/artifacts/)
- What: Append-only markdown file at
.correctless/artifacts/decision-record-{slug}.mdrecording every autonomous decision as DD-xxx entries. Primary audience is the human reviewing post-run. Each entry: decision ID, tier, category, summary, disposition, reasoning, timestamp. ASSUMPTION-tagged entries surface in the Auto Run Report. - Invariant: Append-only with size-regression detection. File size stored in workflow state; shrinkage triggers hard stop (INV-016). Cardinality verified post-pipeline against audit trail (INV-002).
- Enforced at:
scripts/decision-record.sh(dr_append, dr_verify_size, dr_verify_cardinality) - Violated when: Record modified in place, truncated, or tier invocation occurs without DD-xxx entry
- Test: test-decision-record.sh — INV-002, INV-016 suites
ABS-012: Intent summary (.correctless/artifacts/)
- What: Immutable markdown file at
.correctless/artifacts/intent-{slug}.mdwritten once at pipeline startup from the approved spec. Captures what the human wants, key constraints, explicit risk acceptances (≤500 words). Passed to the supervisor on every activation. Referenced from DD-000. - Invariant: Written once, never modified. SHA-256 hash stored in workflow state at creation, verified on each supervisor activation and on
/cauto resume. Mismatch triggers hard stop (INV-013). The SHA-256 hash verification is the structural integrity leg;sensitive-file-guard.shadds a write-target guardrail over the Edit/Write tool-path only (accidental/naive Edit/Write tool calls — Bash-mediated writes are accepted non-goals, AP-040; see ABS-045). - Enforced at:
scripts/intent-hash.sh(intent_create, intent_verify),hooks/sensitive-file-guard.sh(protected path) - Violated when: Intent file modified after creation, hash check skipped, or mismatch does not trigger hard stop
- Test: test-auto-report.sh — INV-013 suite
ABS-013: Auto Run Report (.correctless/artifacts/)
- What: Structured markdown report at
.correctless/artifacts/auto-report-{slug}.mdgenerated on pipeline completion or hard stop. Contains 12 required sections: feature, branch, timestamps, duration, token cost, status, decision summary by tier, decisions requiring human review (ASSUMPTION-tagged + hedging scan), spec summary, implementation summary, verification summary, “What to Review First.” - Invariant: Report must be generated on every pipeline termination (complete or paused). All 12 sections required (INV-009). ASSUMPTION-tagged decisions and hedging-scan candidates must appear in review section (INV-011).
- Enforced at:
scripts/auto-report.sh(report_generate, report_section_decisions) - Violated when: Pipeline ends without report, or report missing required sections
- Test: test-auto-report.sh — INV-009 suite
ABS-014: Pending-decision checkpoint (.correctless/artifacts/)
- What: JSON file at
.correctless/artifacts/pending-decision-{slug}.jsonwritten before spawning a Tier 2 decision agent. Contains: DR-xxx being evaluated, current tier, requesting skill, pipeline phase. Enables crash recovery — on resume, if checkpoint exists without corresponding DD-xxx, the Tier 2 invocation is replayed (idempotent per INV-006). - Invariant: Checkpoint must exist before any Tier 2 spawn. Deleted after decision logged. Stale checkpoints (DD-xxx exists) cleaned up on resume (INV-017).
- Enforced at:
/cautoorchestrator (SKILL.md Phase 2 section) - Violated when: Tier 2 spawned without checkpoint, or checkpoint not cleaned after logging
- Test: test-auto-agents.sh — INV-017 suite
ABS-015: Pipeline lockfile (.correctless/artifacts/)
- What: Lockfile at
.correctless/artifacts/cauto-lock-{slug}preventing concurrent/cautoruns on the same branch. Contains the PID of the owning process. Stale locks (PID not running) auto-cleaned. Corrupted locks (unparsable PID) refuse start with manual cleanup message (BND-006). - Invariant: At most one
/cautorun per branch. Lock acquired at startup, released on completion/hard-stop/escalation. Corrupted lockfile = fail-closed refuse, not auto-clean. - Enforced at:
scripts/cauto-lock.sh(lock_acquire, lock_release, lock_check_stale) - Violated when: Two concurrent runs on same branch, or corrupted lockfile silently cleaned
- Test: test-auto-budget.sh — BND-006 suite
ABS-016: Auto-policy config (.correctless/config/)
- What: JSON config at
.correctless/config/auto-policy.jsondefining Tier 0 policy rules. Sections: review_dispositions, qa_dispositions, spec_update, drift, security, budget, time, hard_stops. Controlled category vocabulary (14 values) and disposition vocabulary (8 values). First-match-wins evaluation. Scaffolded by/csetupwith conservative defaults. - Invariant: Tier 0 evaluation is deterministic — same DR-xxx + same policy = same disposition (INV-001). Policy integrity verified via SHA-256 hash on each Tier 0 evaluation (INV-018).
security.never_relax_autonomouslyhardcoded in orchestrator, not overridable. Malformed JSON → all decisions route to Tier 1+ (BND-001). The SHA-256 policy-integrity hash is the structural integrity leg;sensitive-file-guard.shadds a write-target guardrail over the Edit/Write tool-path only (accidental/naive Edit/Write tool calls — Bash-mediated writes are accepted non-goals, AP-040; see ABS-045). - Enforced at:
scripts/auto-policy.sh(policy_evaluate, policy_hash),hooks/sensitive-file-guard.sh - Violated when: Non-deterministic evaluation, hash mismatch not detected, security floor bypassed via config
- Test: test-auto-policy.sh — INV-001, INV-018, BND-001 suites
ABS-017: Structured decision request (DR-xxx)
- What: JSON format for routing decisions beyond Tier 0. Required fields: decision_id, requesting_agent, phase, category (controlled vocabulary), summary, severity, options (array), relevant_rules, relevant_policies, prior_decisions (summary + disposition only). Validated by orchestrator before routing — malformed requests fail-closed (INV-003, BND-003).
- Invariant: Every decision routed to Tier 1/2/3 uses DR-xxx format. All 10 required fields present. Category from controlled vocabulary. Malformed → logged as error DD-xxx, escalated to Tier 3.
- Enforced at:
scripts/decision-record.sh(drx_validate),scripts/decision-routing.sh(route_decision) - Violated when: Decision routed without DR-xxx, missing required fields, or malformed DR-xxx silently dropped
- Test: test-decision-record.sh — INV-003, BND-003 suites
ABS-018: Review-triage artifact (.correctless/artifacts/)
- What: JSON file at
.correctless/artifacts/review-decisions-{branch_slug}.jsonrecording supervisor triage of review findings during Phase 3 spec pipeline. Each entry: finding_id, source_agent, finding_summary, supervisor_decision (accept/reject/hard_stop), supervisor_reasoning, timestamp. Hash-verified per PAT-011 before spec approval gate. - Invariant: All findings triaged by supervisor are logged. Accepted findings are incorporated into spec. Rejected findings are visible in Auto Run Report. Hash mismatch → hard stop.
- Enforced at:
scripts/review-triage.sh(create_review_decisions, hash_review_decisions, verify_review_decisions_hash) - Violated when: A finding is triaged without logging, or artifact tampered between triage and spec approval
- Test: test-auto-review-triage.sh — INV-022 suite
ABS-019: Supervisor mandate contract (agents/supervisor.md)
- What: Extended supervisor input contract for Phase 3. Adds:
preferences(from preferences.md),decision_patterns(category/tier counts from current run),spec_scope(approved spec scope text). Adds 4 activation types:review_triage,override_issued,override_action_review,override_window_closing. Mandate level (conservative/moderate/aggressive) controls approval threshold — conservative requires spec citation validated by orchestrator. - Invariant: Supervisor activations include all 3 new context fields. New activation types use distinct schemas (not overloaded on
escalation). Conservative mandate enforced structurally by orchestrator post-validation (missing/invalid citation → hard_stop). - Enforced at:
agents/supervisor.md(contract),scripts/supervisor-mandate.sh(context building, citation validation),scripts/override-scrutiny.sh(override activation types) - Violated when: Supervisor activated without preferences/patterns/scope context, or conservative citation check bypassed
- Test: test-auto-mandate.sh — INV-028, INV-029, INV-033, INV-034 suites
ABS-020: Override scrutiny lifecycle (scripts/override-scrutiny.sh)
- What: Three-phase supervisor review of override windows: (1) issuance — supervisor reviews override reason before it takes effect, (2) per-action — supervisor reviews each action during the window against override reason + drift evidence, (3) closure — supervisor reviews cumulative work with pretext and spec-completeness checks. Separate activation counter (exempt from 20-cap, soft cap at 50). Override log records full review history per override entry. Cross-run pre-check (R-004): before per-run scrutiny,
review_override_issuancereads.correctless/meta/overrides/*.json(last 10 bycompleted_at) and checks if the incoming override reason matches reasons from recent runs (Jaccard >= 0.4). If 2+ recent runs match, escalates to human with structured message identifying the cross-run pattern. This short-circuits the per-run checks — if cross-run escalation fires, per-run scrutiny is skipped. - Invariant: No override takes effect without supervisor approval. Every action during an active window is reviewed. Window cannot close without final review. Override-window activations tracked separately. Rejected overrides cannot be re-issued (Jaccard similarity ≥ 0.4 detection). Cross-run recurring overrides (2+ matches in last 10 runs) escalate before per-run scrutiny.
- Enforced at:
scripts/override-scrutiny.sh(all functions includingpreserve_override_log,check_cross_run_overrides),scripts/override-crosscheck.sh(evidence gathering: base-commit verification, file-touch drift, spec completeness) - Violated when: Override applied without supervisor review, action during window not reviewed, window closes without final review, rejected override re-issued with similar justification, or cross-run pattern detected but not escalated
- Test: test-auto-override.sh — INV-035 through INV-039, PRH-006 suites; test-auto-crosscheck.sh — INV-040 through INV-042, BND-007 suites; test-override-freq-metrics.sh — R-001 through R-006
ABS-021: Override history directory (.correctless/meta/overrides/)
- What: Persistent directory storing preserved override logs from completed
/cautoruns. Each file is a JSON metadata wrapper withtask_slug,branch,completed_at,override_count, andoverridesarray. Sole writer:/cauto(viapreserve_override_loginscripts/override-scrutiny.sh). Readers:/cmetrics(Override Health dashboard),/cdocs(override count in workflow-history.md),override-scrutiny.sh(cross-run pattern detection). Retention: 50-file cap enforced bypreserve_override_log. Schema:{task_slug}-{YYYYMMDD}.json. Gitignored via.correctless/meta/entry. Project-level (not branch-scoped) — data persists across branches. - Invariant: Only
preserve_override_logwrites to this directory. Files follow the metadata wrapper schema. Cap enforced at write time. Malformed files (missingcompleted_at) evicted first. - Enforced at:
scripts/override-scrutiny.sh(preserve_override_log,check_cross_run_overrides),skills/cauto/SKILL.md(Step 9.5),.gitignore(.correctless/meta/) - Violated when: A tool other than
preserve_override_logwrites to.correctless/meta/overrides/, or files exceed the 50-file cap - Test: test-override-freq-metrics.sh — R-001, R-005, R-006
ABS-022: Install manifest (.correctless/.install-manifest.json)
- What: JSON manifest written by
setupafter installing hooks and scripts, containing SHA-256 checksums for each installed file and its source. Schema:{"installed_at": "{ISO}", "source_dir": "{abs path}", "files": {"hooks/foo.sh": {"installed_hash": "{sha256}", "source_hash": "{sha256}"}, ...}}. Sole writer:setup. Readers:check_install_freshnessinscripts/lib.sh. Lifecycle: per-install local state, overwritten each setup run. Gitignored. - Invariant: Only
setupwrites the manifest.check_install_freshnessis the sole reader. A partial manifest is never written — if any hash fails, setup aborts manifest generation. - Enforced at:
setup(atomic write via temp + mv),scripts/lib.sh(check_install_freshness),.gitignore(.correctless/.install-manifest.json) - Violated when: A tool other than
setupwrites the manifest, or a partial manifest exists on disk - Test: test-stale-hook-detection.sh — R-001, R-002
ABS-023: Entrypoints YAML contract (.correctless/ARCHITECTURE.md)
- What: Machine-referenceable entrypoints definition embedded in
.correctless/ARCHITECTURE.mdas a fenced YAML block between<!-- correctless:entrypoints:start -->and<!-- correctless:entrypoints:end -->marker comments. Schema: list of objects with fieldsname(string),type(enum: http, cli, grpc, queue, cron, library, websocket),handler(string: file path + symbol),test_via(non-empty string: canonical integration test approach),scope(list of glob patterns). Sole writer:/carchitect. Consumers:/cspec(reads entrypoints, matches scope globs, usestest_viafor Entry derivation in integration test contracts),/ctdd(direct consumer — RED phase reads entrypoints for integration test writing, test audit check 10 reads scope globs for internal import bypass detection). Extraction:scripts/extract-entrypoints.sh. Evolution: additive fields only — existing fields are never removed or renamed. Existing field semantics (not just names) are stable. Thescopefield remains a list of glob patterns; changing its type or matching semantics is a breaking change requiring a new field. - Invariant: Only
/carchitectwrites the entrypoints YAML. Enum membership and field validation happen at write time in the skill, not in the extraction script. The extraction script is dumb and fast — it reads markers, strips fences, validates YAML parseability, and outputs to stdout. - Enforced at:
skills/carchitect/SKILL.md(write-time validation),scripts/extract-entrypoints.sh(extraction + parse validation) - Violated when: A tool other than
/carchitectwrites entrypoints YAML, or the extraction script performs semantic validation (enum checks, field presence), or thetest_viaorscopefields are removed or renamed without updating cspec’s contract derivation logic - Test: test-carchitect.sh — R-004, R-005
ABS-024: Entry/Through/Exit integration test contract format
- What: Cross-skill data contract for integration test constraints. Writer:
/cspec(appends Entry/Through/Exit blocks to[integration]rules during spec writing). Consumer:/ctddtest auditor (verifies tests satisfy contracts during the test audit phase). Format: three fields per[integration]rule — Entry (entrypoint to use, derived from ABS-023test_via), Through (components to exercise and not mock), Exit (observable behavior assertion). Verification tiers: Entry=mechanical BLOCKING, Through=semi-mechanical BLOCKING or UNCERTAIN, Exit=semantic BLOCKING for definite mismatches or ADVISORY for uncertain. - Invariant: The three fields (Entry, Through, Exit) and their verification tiers are stable. Adding a field is additive. Changing a verification tier is an architectural decision requiring spec and review.
- Enforced at:
skills/cspec/SKILL.md(contract writing, Step 4a),skills/ctdd/SKILL.md(contract verification in test audit) - Violated when: A verification tier is changed without architectural review, or Entry/Through/Exit fields are removed from the contract format
- Test: test-integration-test-contracts.sh — R-001, R-007
ABS-025: Agent hook JSON contract (hooks/*.json)
-
What: Agent hooks are JSON config files at hooks/*.jsondefining PreToolUse or PostToolUse hooks withtype: "agent". Unlike command hooks (bash scripts with HOOK_TYPE/HOOK_MATCHER metadata headers per ABS-004), agent hooks use JSON fields:hook_type(PreToolUsePostToolUse), type(“agent”),matcher(pipe-separated tool list),prompt(the agent’s system prompt),timeout(seconds), and optionally_description(documentation). Setup reads these files alongside bash hooks and registers them in settings.json with{type: "agent", prompt: ..., timeout: ...}instead of{type: "command", command: ..., timeout_ms: ...}. Sole writer: human (hook authoring). Consumers: setup (registration), sync.sh (distribution propagation with JSON-specific staleness detection). - Invariant: Every JSON file in
hooks/withtype: "agent"is auto-registered by setup. The JSON file is the sole source of truth for the prompt — setup reads it and injects the prompt into settings.json. No inline agent prompts in setup or other scripts. - Enforced at:
setup(register_hooks),sync.sh(JSON hook propagation + staleness detection in both directions) - Violated when: An agent hook is defined as inline prompt text in setup rather than a JSON config file, or a JSON hook file exists in
hooks/but is not discovered by setup’s registration loop - Test: test-agent-hooks.sh — R-001 (config structure), R-006 (integration: setup registration + idempotency), SYNC-001..SYNC-004 (distribution propagation)
ABS-026: Cost artifact contract (.correctless/artifacts/)
- What: Per-branch JSON files at
.correctless/artifacts/cost-{branch-slug}.jsoncontaining real USD cost computed from Claude Code session transcripts. Sole writer:scripts/compute-session-cost.sh(invoked by/cdocs). Consumers:scripts/build-dashboard.sh(R-007 — Cost by Phase section),/cverify(R-009 —actual_cost_usdin calibration entries),/cmetrics(R-010 — ROI calculations with actual USD),hooks/statusline.sh(reads a lightweight cache subset via backgroundcompute-session-cost.sh --cache). Schema defined in session-cost-analysis spec R-005: includestotal_cost_usd,by_phase,by_subagent,model_breakdown,pricing_used,unknown_models,warnings. The--cachemode outputs a subset (total_cost_usd,by_phase,computed_at,current_phase_cost_usd) to stdout without writing the full artifact — the caller handles file placement. - Invariant:
scripts/compute-session-cost.shis the sole writer. All consumers handle missing artifacts gracefully (R-011) — dashboard falls back to token-log data, cverify omitsactual_cost_usd, cmetrics falls back to token estimates, statusline omits cost display when cache is missing ortotal_cost_usdis 0. The cost artifact always undercounts by the invoking /cdocs session’s cost (accepted). Consumers must never derive USD cost from token-log JSONL fields (ABS-006). - Enforced at:
scripts/compute-session-cost.sh(writer),scripts/build-dashboard.sh(consumer),skills/cverify/SKILL.md(consumer),skills/cmetrics/SKILL.md(consumer),hooks/statusline.sh(consumer — reads ephemeral cache at.correctless/artifacts/cost-cache-{slug}.json, spawns background refresh) - Violated when: A consumer writes cost artifacts, a consumer derives USD cost from token-log JSONL instead of cost artifacts, or a consumer fails instead of degrading gracefully when cost artifacts are missing
- Test: test-session-cost.sh (R-001 through R-018)
ABS-027: Harness fingerprint store contract (.correctless/meta/)
- What: JSON file at
.correctless/meta/harness-fingerprint.jsonrecording the literal fingerprint string"{model_name}|{HARNESS_VERSION}"(no hashing — HI-1 round-2 disposition) plusharness_version(integer),model(string), andtimestamp(ISO-8601) fields. Companion file at.correctless/meta/model-baselines.json(withschema_version: 1from creation) stores per-{model+version}baseline metrics for/cmodelupgraderegression comparison. Per-feature granularity (per-skill deferred until upstream producers exist). Session-id used in flag-file paths is produced byget_current_session_id()inscripts/lib.sh(single source of truth — no per-skill derivation drift permitted). - Invariant: Sole writer of
harness-fingerprint.jsonisscripts/harness-fingerprint.sh. Sole writer ofmodel-baselines.jsonis/cmodelupgradevia the sanctionedscripts/meta-record.sh baselines-writewriter (ABS-047) — as of the calibration-writer feature,/cmodelupgradeno longer holds a directWrite(model-baselines.json)grant; it key-merges through meta-record.sh, preserving all sibling baseline keys +schema_versionand failing loud on a schema mismatch (EXT-002). Sole writer ofHARNESS_VERSIONconstant is human commit (scripts/harness-fingerprint.shis sensitive-file-guard protected). All consumers fail-open on missing/malformed files. The fingerprint check is advisory — never blocks any skill (PRH-001). Sole-writer enforcement is structural on the Edit/Write tool-path (sensitive-file-guard); Bash-mediated writes (redirects, writer commands, interpreters, git) are accepted non-goals (AP-040; see ABS-045). There is nocmd_*content gate behind these files, so a runtime out-of-band Bash write is unguarded and undetected — but the fingerprint is advisory (PRH-001), so the residual is accepted. Bash-redirect structural leg removed 2026-06 by sfg-edit-write-only; residual accepted (advisory/owner-scaffolded files, surviving Edit/Write leg). - Enforced at:
scripts/harness-fingerprint.sh,scripts/meta-record.sh(sanctioned model-baselines writer — ABS-047),skills/cmodelupgrade/SKILL.md,skills/cspec/SKILL.md(Step 0 invocation),hooks/sensitive-file-guard.sh(writer enforcement),scripts/lib.sh(session-id helper, locked_update_file helper),tests/test-harness-fingerprint.sh,tests/test-meta-record.sh - Violated when: Any skill or script other than the sanctioned writers writes to either meta file or to the script; an agent autonomously bumps HARNESS_VERSION; the check blocks /cspec; raw probe responses or system-prompt content stored verbatim; per-skill granularity is added without the upstream producer changes; session-id derivation duplicated outside
lib.sh; any consumer derives USD cost from token-log instead of cost artifacts (ABS-026 cross-reference) - Test:
tests/test-harness-fingerprint.shcovers INV-001..019, PRH-001..006, BND-001..005
ABS-028: Test-features baseline contract (.correctless/test-features/)
- What: Reference feature spec at
.correctless/test-features/baseline.md, scaffolded by/csetupStep 2.6 fromtemplates/test-features/baseline.md(idempotent — never overwrites). Consumed by/cmodelupgrade --capture-baselineas the controlled-baseline reference feature run end-to-end through/cautoto record per-feature metrics (qa_rounds, total_tokens, total_cost_usd, phase_count) at the current{model}+{HARNESS_VERSION}combination. The recorded metrics seed.correctless/meta/model-baselines.json(ABS-027 companion file) for future regression comparison. The destination file is user-editable — projects whose structure makes the example unfit (nosrc/directory, different language conventions) adapt the file paths but keep the spec invariant structure intact. - Invariant:
/csetupis the sole scaffolder (template → destination copy, idempotent guard via[ ! -f ]check). Once scaffolded, the user is the sole editor./cmodelupgrade --capture-baselinereads but never writes the file. Falls back to no-baseline mode if the file is absent. - Enforced at:
skills/csetup/SKILL.mdStep 2.6 (scaffolding),templates/test-features/baseline.md(template producer),sync.sh(template propagation tocorrectless/templates/test-features/),skills/cmodelupgrade/SKILL.md(consumer) - Violated when: a skill other than
/csetupwrites to.correctless/test-features/baseline.md, the scaffold step overwrites a user-edited file, or/cmodelupgrade --capture-baselinefails when the file is absent instead of degrading to no-baseline mode (INV-009b) - Test: covered by harness-fingerprint spec test suite (
tests/test-harness-fingerprint.sh); idempotency follows PAT-008 (idempotent migration testing)
ABS-029: Audit findings persistence contract (.correctless/artifacts/findings/)
- What: Per-audit-run findings artifacts at
.correctless/artifacts/findings/audit-{preset}-{date}-round-{N}.json(one per round, including round-1 withfindings: []andrejected: []for clean audits) and append-only run summary at.correctless/artifacts/findings/audit-{preset}-history.md. Round-JSON required schema:preset,date,round,findings(array),rejected(array),started_at(ISO-8601 UTC timestampYYYY-MM-DDTHH:MM:SSZmatching workflow state). Eachfindings[]entry MAY include an optionalescape_typefield (valid values:implementation,spec,non-escape, ornull/absent for unclassified);audit-record.sh write-roundvalidates the vocabulary and rejects the entire payload if any entry has an invalid value. Sole writer:scripts/audit-record.sh(and its install-mirror.correctless/scripts/audit-record.sh) invoked exclusively by/caudit(PAT-003 phase-transition script —write-roundandappend-historysubcommands; verify lives in the gate, not the script). Consumers:/cmetrics(last-Olympics staleness viamax(history.md mtime, latest round-JSON mtime), run counts, average convergence, escape metrics — three-gate breakdown, severity-weighted scores, root-cause classification, per-cycle trends),/caudititself on subsequent runs (recurring-pattern detection, prior-finding context for Round 1 specialists, per-findingescape_typeclassification at specialist submission time),/cdevadv(recurring-pattern referrals). - Invariant:
scripts/audit-record.shis the sole writer and is itself sensitive-file-guard protected (matches the harness-fingerprint.sh sole-writer-convention, AP-022 mitigation).cmd_audit_doneinhooks/workflow-advance.shrefuses the transition todoneunless at least one round-JSON exists whosestarted_atfield equals the workflow state’sstarted_at(content-based string equality, not filesystem mtime — robust to post-git-op timestamp drift; see Environment Assumptions). Zero-finding audits MUST still write round-1 withfindings: []andrejected: []— absence of the file is NOT evidence of “no findings.”/cmetricsMUST cross-check both signals when computing staleness; single-signal staleness reading is forbidden. The override sentinel (workflow-advance.sh standard mechanism) is the only bypass; no flag or env-var escape hatch. - Enforced at:
hooks/workflow-advance.sh(cmd_audit_doneprecondition + content-based match),scripts/audit-record.sh(writer),hooks/sensitive-file-guard.sh(writer-script protection via DEFAULTS — INV-009),skills/caudit/SKILL.md(sole invoker of write-round),skills/cmetrics/SKILL.md(multi-signal consumer),tests/test-audit-findings-persistence.sh - Violated when:
cmd_audit_donetransitions phase todonewithout a content-matching round JSON; the gate uses mtime, date suffix, or any non-content key for matching; a consumer derives staleness from a single mtime; any skill other than/cauditinvokesaudit-record.sh write-round;cmd_audit_doneadds an env-var or flag escape hatch;audit-record.shconstructs destination paths from config-derived input; the round-JSON path format diverges fromaudit-{preset}-{date}-round-{N}.json; the writer script is missing from sensitive-file-guard DEFAULTS - Test:
tests/test-audit-findings-persistence.sh(INV-001..009, PRH-001..005, BND-001..002) - Guards against: AP-026, AP-022
ABS-030: Autonomous decisions JSONL contract
- Artifact:
.correctless/artifacts/autonomous-decisions-{branch_slug}.jsonl - Sole writer: the
scripts/autonomous-decision-writer.shscript, invoked by/cautoOR/cchores(skills return decisions as structured output; the invoking orchestrator persists them via the writer script, same SFG-bypass pattern as ABS-029/audit-record.sh). No path other than this script writes the JSONL. - Consumers: R-007 pipeline summary,
/cwtfaccountability analysis - Invariant: the invoking orchestrator (
/cautoor/cchores) verifies JSONL growth after each skill/agent invocation. Deferred escalations gate PR creation (R-013). Skills must NOT write to this file directly except viaautonomous-decision-writer.sh. - Enforced at:
skills/cauto/SKILL.mdandskills/cchores/SKILL.md(writer-script invocation, JSONL growth check, R-013 confirmation gate),hooks/sensitive-file-guard.sh(Edit/Write tool-path guard forautonomous-decisions-*.jsonl; Bash-mediated writes are accepted non-goals per AP-040/ABS-045 — nocmd_*gate backs the JSONL, so a runtime out-of-band Bash write is an accepted residual, caught only post-hoc by the R-013 growth check) - Violated when: A skill other than
/cautoor/cchoreswrites directly to the JSONL; the orchestrator skips JSONL growth check after skill invocation; deferred escalation confirmation gate is bypassed before PR creation - Test:
tests/test-autonomous-skill-contract.sh(R-006, R-007, R-013 tests),tests/test-sensitive-file-guard.sh(behavioral block tests) - Guards against: AP-026, AP-022
ABS-031: Pipeline manifest artifact contract
- Artifact:
.correctless/artifacts/pipeline-manifest-{branch_slug}.json - Sole writer:
/cautoorchestrator (writes manifest as first action after phase gate, updatescompleted_stepsafter each pipeline step, writesstatus: "complete"as final action) - Consumers:
/cautoR-004 resumption (reads manifest to detect truncation and report missed steps),/cstatusR-009 (reads manifest to report incomplete pipeline) - Invariant: Pipeline manifest is ephemeral — not committed during consolidation. Covered by Step 8.2 unstage guard (
.correctless/artifacts/exclusion). Manifest without"status": "complete"indicates pipeline truncation. - Enforced at:
skills/cauto/SKILL.md(sole writer, R-001/R-002/R-003 instructions), Step 8.2 belt-and-suspenders guard (prevents accidental commit) - Violated when: Manifest is committed to the branch; manifest
statusis set to"complete"before pipeline summary (Step 10) completes; a consumer other than/cautoor/cstatuswrites to the manifest - Test:
tests/test-pipeline-completeness-verification.sh - Guards against: PMB-009 (silent pipeline truncation)
ABS-032: Dashboard UI output contract (.correctless/dashboard/)
- What: Single self-contained HTML file at
.correctless/dashboard/index.htmlgenerated byscripts/build-dashboard.sh. Contains two views: Metrics (all existing dashboard sections) and Artifact Browser (sidebar navigation for specs, verifications, review findings, research briefs, architecture docs, QA findings, audit history). Uses marked.js + DOMPurify from CDN (SRI-pinned) for safe markdown rendering. All artifact data inlined as JSON in<script type="application/json">block with</escaped as<\/. - Invariant:
scripts/build-dashboard.shis the sole writer. The output directory.correctless/dashboard/is gitignored. CDN failure degrades to raw text display with visible notice. No raw HTML passthrough — all markdown rendered through DOMPurify (TB-003 mitigation). - Enforced at:
scripts/build-dashboard.sh(writer),skills/cdashboard/SKILL.md(invoker),.gitignore(output exclusion) - Violated when: A consumer other than build-dashboard.sh writes to
.correctless/dashboard/, markdown is rendered without DOMPurify sanitization, or the</escaping is bypassed - Test:
tests/test-project-dashboard.sh
ABS-033: Deferred findings backlog contract (.correctless/meta/)
- What: Advisory JSON file at
.correctless/meta/deferred-findings.jsoncentralizing non-blocking review findings deferred by the user. Schema:{"findings": [...], "schema_version": 1}with required per-entry fields:id(DF-NNN),source_file,finding_id,feature,severity(MEDIUM/LOW/ADVISORY only — PRH-003 excludes HIGH/CRITICAL),description,category,status(open/in-progress/resolved/wont-fix),deferred_at,resolved_at,resolution. Multi-writer:/creview-spec(writes on user “defer” disposition),/creview(same),/ctriage(bulk triage updates),scripts/sync-deferred-backlog.sh(re-derives from review artifacts). This is a justified deviation from the sole-writer convention used by other.correctless/meta/files — the backlog is advisory data, not safety-critical, and the review artifacts remain the source of truth. Coordination model: last-write-wins (BND-003); concurrent writes may lose one entry, but the finding persists in the originating review artifact and can be re-synced. Consumers:/cstatus(open count + severity breakdown + threshold warning at 20+),/cmetrics(trend: added/resolved in last 30 days, severity distribution, oldest open item),/cauto(backlog sweep between/cdocsand consolidation — surfaces all open findings as advisory, never blocks per PRH-001). The file lives under.correctless/meta/(gitignored, local-only);scripts/sync-deferred-backlog.shreconstructs it from committed review artifacts on any machine. Won’t-fix items persist permanently with rationale (PRH-002). - Invariant: Only the four named writers may modify the file. Consumers (cstatus, cmetrics, cauto sweep, cprune) are read-only. Severity is restricted to MEDIUM/LOW/ADVISORY (PRH-003 — HIGH/CRITICAL must be fixed during review). The backlog never gates any pipeline phase transition (PRH-001). File absence triggers dormant behavior in all consumers (PAT-019). The sync script is idempotent — dedup by
source_file+finding_idpair. - Enforced at:
skills/creview-spec/SKILL.md(writer, allowed-tools),skills/creview/SKILL.md(writer, allowed-tools),skills/ctriage/SKILL.md(writer, allowed-tools),scripts/sync-deferred-backlog.sh(writer + re-derivation backstop),skills/cstatus/SKILL.md(consumer),skills/cmetrics/SKILL.md(consumer),skills/cauto/SKILL.md(consumer — sweep step),skills/cprune/SKILL.md(consumer — stale finding detection per INV-010, read-only per PRH-004) - Violated when: A consumer writes to the backlog file; a finding with HIGH/CRITICAL severity enters the backlog; the backlog gates a pipeline phase transition; a consumer errors when the file is absent instead of degrading silently; the sync script creates duplicate entries on re-run
- Test:
tests/test-deferred-findings-backlog.sh - Guards against: Invisible accumulation of deferred review findings across features
ABS-034: Probe results artifact contract (.correctless/artifacts/)
- What: JSON artifact at
.correctless/artifacts/probe-results-{branch-slug}.jsoncapturing adversarial probe round results from/ctdd. Schema: array of probe objects with fields for probe description, target invariant, outcome (survived/killed), and evidence. The probe round runs between QA and mini-audit in the/ctddpipeline — it attempts to violate spec invariants through adversarial inputs and boundary conditions. Sole writer:/ctddorchestrator (probe round step). Consumers: none initially (future:/cmetricsfor probe survival rates). The artifact is committed to the repository via a TB-004c allowlist exception in/cautoStep 8.1. - Invariant: Only the
/ctddorchestrator may write this file. File absence triggers dormant degradation in future consumers (PAT-019 — no error, no blocking, graceful no-op). The artifact path usesbranch_slugfrom ABS-001 for consistent naming. Future consumers must treat probe results as advisory data (TB-003 pattern). - Enforced at:
skills/ctdd/SKILL.md(writer — probe round step),skills/cauto/SKILL.md(TB-004c allowlist — committed on push) - Violated when: A consumer other than
/ctddwrites to the file; a consumer errors when the file is absent instead of degrading silently; the file is not included in the TB-004c consolidation allowlist - Test: Structural — probe-results file written during
/ctddprobe round - Guards against: Loss of adversarial testing evidence; inability to measure probe effectiveness over time
ABS-035: Workflow-advance module contract (scripts/wf/)
- What:
hooks/workflow-advance.shis decomposed into a thin dispatcher that sources 3 module files fromscripts/wf/:transitions.sh(phase transition commands),utility.sh(operational commands),metadata.sh(state modification commands). The dispatcher contains argument parsing, module sourcing, the dispatch table, and all shared helper functions. Command function bodies (cmd_*) live exclusively in the modules. The dispatcher setsSCRIPT_DIRbefore sourcing — modules use$SCRIPT_DIRfor path resolution, neverBASH_SOURCE[0]. - Invariant: No
cmd_*function body in the dispatcher; no shared helper function defined in a module; noBASH_SOURCE[0]usage in module code; no function defined in more than one module. Module files are protected byhooks/sensitive-file-guard.shon the Edit/Write tool-path only (Bash-mediated writes are accepted non-goals, AP-040/ABS-045); the surviving runtime leg is thetest-workflow-advance-decomp.shstructural tests (test-time, not write-time). - Enforced at:
hooks/sensitive-file-guard.sh(DEFAULTS),tests/test-workflow-advance-decomp.sh(structural tests),setup(installsscripts/wf/to.correctless/scripts/wf/) - Violated when: A command function body appears in the dispatcher; a helper function is defined in a module; a module uses
BASH_SOURCE[0]; a function is duplicated across modules - Test:
tests/test-workflow-advance-decomp.sh— 253 assertions covering INV-001 through INV-017 and PRH-001 through PRH-003 - Guards against: DA-002 complexity concern — single-file growth making the state machine unmaintainable
ABS-036: Lens recommendation artifact (.correctless/artifacts/)
- What: JSON artifact at
.correctless/artifacts/lens-recommendations-{branch_slug}.jsoncarrying review-phase lens recommendations and mini-audit outcomes. Writers:/creview-spec(high+ intensity),/creview(standard intensity)./ctddupdates with outcomes after mini-audit. Consumers:/ctdd(reads recommendations),/cmetrics(lens coverage),/cwtf(auditability). Branch-scoped by filename (branch-scoped state pattern). Gitignored. Ephemeral. - Invariant: File absence triggers dormant degradation (PAT-019) in all consumers. Never gates any pipeline phase transition (PRH-003). Outcome recording is best-effort, non-blocking.
- Enforced at:
skills/creview-spec/SKILL.md(writer),skills/creview/SKILL.md(writer),skills/ctdd/SKILL.md(consumer + outcome writer),skills/cmetrics/SKILL.md(consumer),skills/cwtf/SKILL.md(consumer),scripts/wf/transitions.sh(non-blocking warning incmd_done) - Violated when: a consumer errors on absent artifact; artifact gates a phase transition;
/ctddcreates outcomes-only artifact when no recommendations exist - Test:
tests/test-review-driven-lenses.sh - Guards against: opaque mini-audit lens selection (lost review context between phases)
ABS-037: Cross-feature intelligence brief (.correctless/meta/)
- What: JSON artifact at
.correctless/meta/cross-feature-intel.jsonproduced byscripts/cross-feature-intel.sh. Aggregates 6 data sources (deferred findings, devadv reports, overrides, lens recommendations, debug investigations, workflow effectiveness) into a single brief filtered by file scope and recency. The script is the sole writer and is stateful (occurrence counts accumulate across regenerations) while remaining deterministic (same inputs + same prior state = same output). Consumers:/cspec(reads during brainstorm, advisory only),/cstatus(reads brief metadata for health reporting),/creview-spec(reads the brief file directly via jq-basedoccurrences >= 3filtering — pure consumer, not regeneration trigger),/creview(reads the brief file directly via jq-basedoccurrences >= 3filtering — pure consumer, not regeneration trigger). Review skills read the brief file directly (jq-based, no script invocation) — they are pure consumers, not regeneration triggers. Project-level, gitignored under.correctless/meta/. - Invariant:
scripts/cross-feature-intel.shis the sole writer./cspecis read-only — never writes, modifies, or deletes the brief. Review skills (/creview-spec,/creview) read the brief file directly and never invoke the script. The brief is advisory — never gates any phase transition or blocks any skill. Consumers handle missing/malformed briefs via dormant degradation (PAT-019). - Enforced at:
scripts/cross-feature-intel.sh(writer),skills/cspec/SKILL.md(consumer),skills/cstatus/SKILL.md(consumer),skills/creview-spec/SKILL.md(consumer),skills/creview/SKILL.md(consumer) - Violated when: a skill other than the script writes to the brief;
/cspectreats brief content as constraints rather than context; the brief gates a phase transition; a consumer errors when the brief is absent; a review skill invokes the script instead of reading the file - Test:
tests/test-cross-feature-intel.sh,tests/test-review-intel-consumer.sh - Guards against: cross-feature amnesia — pipeline forgetting what prior runs discovered
ABS-038: Archive file contract (.correctless/)
- What: Three archive files —
.correctless/ARCHITECTURE_DEPRECATED.md(architecture entries),.correctless/antipatterns-archived.md(antipatterns),.correctless/CLAUDE_LEARNINGS_ARCHIVED.md(CLAUDE.md learnings). Committed to the repo (not gitignored). Each file has a header comment explaining its purpose, created on first use (BND-001). - Sole writer:
/cprune(via the SKILL.md orchestrator, not the scanner script — the scanner only detects candidates, the skill executes the archive operations). - Invariant: Only
/cprunewrites to archive files. Archived entries retain their original IDs. New entries in the active file must increment past the highest ID ever used (active + archived). Archive files are SFG-protected on the Edit/Write tool-path only (Bash-mediated writes are accepted non-goals, AP-040/ABS-045); the surviving leg against non-/cprunewriters is the sole-writer convention (prose), not a runtime gate — accepted residual (INV-016). - Enforced at:
skills/cprune/SKILL.md(writer),hooks/sensitive-file-guard.sh(SFG protection),tests/test-cprune.sh(behavioral tests) - Violated when: a tool other than
/cprunewrites to an archive file; an archived entry’s ID is reused for a new active entry; an archive file is gitignored - Test:
tests/test-cprune.sh— INV-004, INV-016, BND-001 - Guards against: AP-005 (stale docs — the archive preserves context), AP-022 (dead code in security paths — SFG protection)
ABS-039: Slug-type classification mapping (scripts/prune-scan.sh)
- What:
_classify_artifact_patterninscripts/prune-scan.shis the sole authority that maps each pattern in the scanner’sartifact_patternslist to exactly one of four slug-type enum members:branch-slug,task-slug,session-slug, orunclassified. The classification determines which live-slug set the safety belt consults: branch-slug patterns match against the live-branch-slug set (computed viabranch_slug()fromscripts/lib.sh); task-slug patterns match against the live-task-slug set (derived frombasename(.spec_file, ".md")for eachworkflow-state-*.jsonwhose.branchis in the live branch set — no.taskfallback per EA-003); session-slug patterns are never live-prunable; unclassified patterns are skipped with an observable JSONskipped_unclassifiedentry and stderr advisory. The scanner emits a wrapped JSON object{candidates: [...], skipped_unclassified: [...], protection_set: {...}, protection_status: {...}}. Pattern-to-slug-type mapping is cross-referenced against an explicit producer-pattern table maintained in.correctless/specs/prune-scan-slug-aware.md(the authoritative table — everyartifact_patternsentry must have a row, every table row must have anartifact_patternsentry, and the classification must agree). Consumers:skills/cprune/SKILL.mdandskills/cstatus/SKILL.md(read.candidatesfrom the wrapped object — never the top-level value as an array). - Invariant:
scripts/prune-scan.sh’s_classify_artifact_patternis defined exactly once and is total overartifact_patterns(no member returns empty or any value outside the four-enum set). The producer-pattern table is the sole source of truth for the (pattern → slug-type) mapping; the structural test (INV-008 in the spec) parses both the table and theartifact_patterns=assignment line directly via sed (no prose-grep, no source-and-read) and asserts bidirectional coverage with an allowlist cap of 5. Slug-match comparisons use bash[[regex with delimited-token boundaries ([-.]or string-edge) — substring primitives (grep -F "$slug", unquoted[[ $f =~ $slug ]],case "$f" in *"$slug"*)) are prohibited and detected by theprune-scan-substring-matchrule inscripts/antipattern-scan.sh check_shell(). Slug values are validated by_slug_is_safeat extraction boundaries AND ERE metacharacters are escaped by_escape_ere_metacharsbefore being interpolated into regex — the dual defense-in-depth ensures malformed slugs are rejected at the boundary AND that any slug that slips through cannot exploit ERE metachar interpretation (MA-001 mitigation). Workflow-state identity for race detection uses content-based equality onstarted_at(primary) → compositetask|branch(fallback) →sha256(file)(last resort) — extending the ABS-029 content-based-match convention to cross-worktree scenarios where the same logical workflow-state may be observed at different paths. The scanner enumerates artifacts viafind -printunderset -f(no glob expansion,dotglobneutralized) instead of a*shell glob — defends against caller-cwd glob misroute when the scanner is invoked from a fixture or test directory (MA2-004 mitigation). - Enforced at:
scripts/prune-scan.sh(writer;_classify_artifact_pattern,_slug_is_safe,_escape_ere_metachars,_workflow_state_identity,_build_live_slug_sets),scripts/antipattern-scan.sh(structuralprune-scan-substring-matchrule),skills/cprune/SKILL.md(consumer — reads.candidates),skills/cstatus/SKILL.md(consumer — reads.candidates),.correctless/specs/prune-scan-slug-aware.md(producer-pattern table — source of truth),tests/test-prune-scan-slug-aware.sh(61 structural + integration assertions covering INV-001..INV-018, PRH-001..002, BND-001..002) - Violated when: a pattern is added to
artifact_patternswithout a_classify_artifact_patterncase;_classify_artifact_patternreturns a value outside the four-enum set; two function definitions of_classify_artifact_patternexist; the producer-pattern table drifts fromartifact_patternsin either direction; substring primitives appear inscripts/prune-scan.shfor slug matching; the scanner emits a bare JSON array instead of the wrapped object; a consumer reads the top-level value as an array instead of.candidates; slug values are interpolated into regex without validation+escape; workflow-state race detection falls back to mtime instead of content-based identity; the scanner uses*glob over artifact-directory contents instead offind -printunderset -f - Test:
tests/test-prune-scan-slug-aware.sh(61 assertions);tests/test-antipattern-scan.sh(rule registration) - Guards against: AP-032 instance recurrence (literal-path-only resolution / extraction-correct-but-resolution-incomplete); AP-031 producer/parser format divergence (via spec-anchored producer-pattern table); silent data loss in autonomous
/cprunewhen slug-type classification fails for a pattern that protects live work
ABS-040: Prune-pattern baseline manifest (.correctless/meta/)
- What: JSON file at
.correctless/meta/prune-pattern-baseline.jsonrecording the pattern set known to the scanner at the last operator-acknowledged baseline. Schema:{"patterns": [...], "updated_at": "{ISO}", "schema_version": 1}. Sole writer:scripts/prune-scan.shinvoked with the explicit--update-baselineflag. The scanner does NOT update the baseline as a side effect of scanning — autonomous/cpruneruns,/cstatusruns, and default-mode/cpruneruns all leave the baseline untouched. Baseline update happens only when/cpruneSKILL.md invokes the scanner with--update-baselineafter interactive human confirmation. Consumed byscripts/prune-scan.sh(read at scan start to detect newly-added patterns). Gitignored. - Invariant:
scripts/prune-scan.sh --update-baselineis the sole writer./cpruneautonomous mode (mode: autonomousprompt context) NEVER passes--update-baselineto the scanner — structural assertion via grep onskills/cprune/SKILL.mdautonomous code path. For any pattern present in currentartifact_patternsbut absent from the baseline, candidates emitted via that pattern carryrisk: "medium"(interactive-only) with reason textNewly added pattern '{pattern}' — first scan after upgrade; review before deletion— preventing auto-promotion of newly-added patterns tolowrisk without human review. Baseline file is SFG-protected on the Edit/Write tool-path only (Bash-mediated writes are accepted non-goals, AP-040/ABS-045); the surviving leg is the--update-baselinesole-writer + the autonomous-no-update structural assertion (about who/how writes), not runtime out-of-band-write prevention — accepted residual. - Enforced at:
scripts/prune-scan.sh(sole writer via--update-baseline, sole reader),skills/cprune/SKILL.md(invokes--update-baselineonly after interactive human confirmation; never in autonomous mode),hooks/sensitive-file-guard.sh(DEFAULTS — protects the baseline file),tests/test-prune-scan-slug-aware.sh(INV-011 — five scenarios: absent baseline, lagging baseline, matching baseline, baseline-update gating, autonomous-mode no-update assertion) - Violated when: the scanner updates the baseline without the explicit
--update-baselineflag;/cprunepasses--update-baselinein autonomous mode; a newly-added pattern emits alow-risk candidate before baseline acknowledgement; the baseline file is missing/corrupt and the scanner proceeds as if baseline equaled current set (must fail-closed to all-medium per INV-011a); any tool other thanprune-scan.sh --update-baselinewrites the file - Test:
tests/test-prune-scan-slug-aware.sh(INV-011-a/a-stderr/c/d/e/f);tests/test-sensitive-file-guard.sh(SFG-protection coverage) - Guards against: RS-012 first-run-after-upgrade silent data loss; RS-027 silent pattern-correction surprise; auto-promotion of newly-added patterns to
lowrisk before human review
ABS-041: SFG lift-and-restore sentinel + final-state backstop
- What: The
.correctless/.sfg-lift-activecommitted sentinel file plus thescripts/check-no-pending-sfg-lift.shfinal-state backstop together implement the AP-037 lift-and-restore contract for SFG-protected deliverables. When a feature’s primary deliverable is itself in thehooks/sensitive-file-guard.shDEFAULTS list (e.g.,agents/fix-diff-reviewer.md), the lift commit removes the deliverable’s DEFAULTS line and ADDS the sentinel; the restore commit re-adds the DEFAULTS line and REMOVES the sentinel. The sentinel.correctless/.sfg-lift-activeis itself in SFG DEFAULTS (RS-018) so the guard’s own disable-switch is guarded. During iteration the sentinel makestests/test-fix-diff-reviewer-agent.shSKIP its lift-state assertion (keepingcommands.testand /cauto consolidation unblocked); the dedicated backstop script — deliberately OUTSIDE thetests/test-*.shglob — fails unconditionally when the sentinel is present, EXCEPT it NO-OPs (exit 0) when the deliverable is no longer in DEFAULTS (RS-028 self-deactivation). Invoked from four sites: the CIsfg-lift-checkjob,/cautoStep 8 (installed path), the operator rule.claude/rules/sfg-deliverable.md, and backstopped by thecmd_donegate inhooks/workflow-advance.sh. - Invariant:
scripts/check-no-pending-sfg-lift.shis the sole final-state checker. Thecmd_donetransition gate inhooks/workflow-advance.shrefuses thedonetransition while.correctless/.sfg-lift-activeexists AND (CS-019) requires a HEAD-SHA-pinned full-suite test-success sentinel (content-matched on HEAD SHA per the ABS-029 content-based-gate convention).agents/fix-diff-reviewer.mdand.correctless/.sfg-lift-activeare both present in the DEFAULTS of bothhooks/sensitive-file-guard.shand its synced mirror at the branch tip. The lift and restore are real tree changes — the sentinel cannot be local-only. - Enforced-at:
scripts/check-no-pending-sfg-lift.sh(final-state checker, self-deactivating),hooks/workflow-advance.sh(cmd_donegate — sentinel refusal + HEAD-SHA test-success sentinel),.github/workflows/ci.yml(dedicatedsfg-lift-checkjob, unconditional, intest-suiteneeds),skills/cauto/SKILL.mdStep 8 (installed-path invocation + sentinel in staging allowlist),skills/cstatus/SKILL.md(stranded-sentinel detector),hooks/sensitive-file-guard.sh+ mirror (sentinel + agent path in DEFAULTS),.claude/rules/sfg-deliverable.md(operator procedure),sync.sh(downstream propagation),tests/test-fix-diff-reviewer-agent.sh(CS-012/CS-012a/CS-018/CS-019 structural + behavioral) - Violated-when: the backstop script is missing or stops self-deactivating; the
cmd_donegate does not refuse on sentinel-present or omits the HEAD-SHA test-success sentinel; the CIsfg-lift-checkjob is cosmetic (falsyif:,continue-on-error: true, buried in the test-*.sh loop or a matrix) or absent fromtest-suite’s ownneeds:; the sentinel is not in SFG DEFAULTS;/cautoStep 8 does not invoke the installed backstop path or omits the sentinel from staging; a lift commit ships without its restore commit - Test:
tests/test-fix-diff-reviewer-agent.sh(CS-012, CS-012a, CS-018(a/b/c/d/behavioral), CS-019);tests/test-architecture-drift.sh(ABS-041 coverage) - Guards against: AP-037 (protected asset is the deliverable — guard has no legitimate-edit affordance); AP-022 (dead-code-in-security-paths — the self-deactivation no-op prevents a permanently-passing backstop); silent ship of an un-restored lift state
ABS-042: Sole-writer external-review producer (scripts/external-review-run.sh)
- What:
scripts/external-review-run.shis the sole writer of.correctless/meta/external-review-history.jsonand the run_id-keyed codex output file under.correctless/artifacts/. The producer invokes codex read-only against the whole spec (on stdin), validates the config-sourced invocation as a closed allowlist (INV-017), parse-gates + bounds + neutralizes the untrusted codex output (INV-002/009/019), and records the run (record) in the SAME execution as the codex call (invocation-coupling). It self-seeds{"reviews":[]}when the history file is absent (RS-009) and appends via the ABS-003locked_update_filepattern (RS-012)./creview-specnever writes the history file — the directWritegrant was removed (INV-013). The lift-and-restore affordance for this SFG-protected deliverable is generalized to N deliverables (INV-020 / ABS-041). - Invariant: the producer is the single writer of the history file and the codex output file; the append is invocation-coupled (
recordin the same exec as the codex call), ABS-003-locked, self-seeding, and run_id-keyed (the--output-last-messagepath embeds the full run_id so concurrent runs never TOCTOU-collide).external-review-run.shandconfig-update.share both present in the SFG DEFAULTS ofhooks/sensitive-file-guard.shand its synced mirror in all three path forms. - Deviation note (RS-020): chooses invocation-coupling over the ABS-029
cmd_*phase-transition gate — justified because no phase transition depends on the history file; thependingsubcommand is the surfacing mechanism. Documented explicitly per PAT-018, like ABS-033 documents its deviation. - Enforced-at:
scripts/external-review-run.sh(producer),hooks/sensitive-file-guard.sh+ mirror (three-form DEFAULTS for both writers),skills/creview-spec/SKILL.md(Step 3 invokes the producer; no direct history write),tests/test-external-review.sh(INV-007/008/019 behavioral),tests/test-sensitive-file-guard.sh(INV-010 three-form + live-guard) - Violated-when: a second writer touches the history file; the append is decoupled from the codex call; raw jq-to-file replaces
locked_update_file; the codex output path omits the run_id; either privileged writer is missing from SFG DEFAULTS - Test:
tests/test-external-review.sh(INV-007/INV-008/INV-019);tests/test-sensitive-file-guard.sh(INV-010/INV-020);tests/test-architecture-drift.sh(ABS-042 coverage) - Guards against: AP-026 (advisory-prose write contract), PMB-005 (sole-writer omission), AP-022 (dead-code-in-security-paths), AP-037 (protected deliverable)
ABS-043: Chore-run manifest contract (.correctless/artifacts/)
- Artifact:
.correctless/artifacts/chore-run-{branch_slug}.json— written as/cchores’s FIRST action ({selected_issue, expected_steps, expected_end_state, status: "in_progress", started_at}) and finalized as its LAST action tostatus: "complete" | "aborted" (+ abort_reason) | "noop". Distinct from ABS-031’s/cautopipeline-manifest — no sole-writer conflict (different filename, different writer). - Sole writer:
/cchores. Consumers:/cstatus(truncation surfacing — a manifest leftin_progressdenotes a truncated run, reported exactly as ABS-031 pipeline-manifests are), the INV-016 run-report step. - Invariant: ephemeral, gitignored, excluded from PR staging;
in_progressdenotes truncation;branch_slugis derived vialib.sh branch_slug()(verified to handle thechore/prefix). - Enforced-at:
skills/cchores/SKILL.md(writer),skills/cstatus/SKILL.md(consumer wiring) - Violated-when: the run stops mid-pipeline with the manifest
in_progressand no abort recorded; the manifest is committed to the PR; nothing consumes it (dead artifact) - Test:
tests/test-cchores.sh,tests/test-cchores-infra.sh,tests/test-architecture-drift.sh(ABS-043 coverage) - Guards against: AP-030 (pipeline truncation), PMB-009 (silent truncation), AP-022 (dead-code-in-security-paths)
ABS-044: Cross-run re-selection store (.correctless/meta/)
- Artifact:
.correctless/meta/cchores-attempted.json— schema{"schema_version": 1, "attempts": [{"issue": N, "branch_slug": "…", "outcome": "aborted|abandoned", "reason": "…", "recorded_at": "ISO"}]}. The authoritative loop-prevention store, load-bearing for INV-002 selection and INV-011 abort. - Sole writer:
/cchoresvialib.sh locked_update_file(ABS-003 advisory lock — concurrent-write safe). Consumers (read-only): INV-002 selection filter (skip any issue with anabortedattempt),/cstatus. Never the public comment. - Invariant: gitignored, cross-run/cross-branch durable (under
.correctless/meta/per the ABS-033 precedent — the branch-scoped ABS-030 JSONL cannot persist across branches), never committed/pushed; re-selection suppression is recorded HERE before the public comment in the INV-011 abort order, so it survives a comment-post failure. - Prune behavior:
.correctless/meta/-durable (NOT artifact-slug-scoped) —/cprunedoes NOT auto-delete it; staleresolvedattempts may be pruned via/ctriage, not autonomous/cprune. - Enforced-at:
skills/cchores/SKILL.md(writer), INV-002 selection,skills/cstatus/SKILL.md - Violated-when: re-selection suppression depends solely on the public comment; the store is committed; a non-
/cchoreswriter touches it; concurrent writes corrupt it (must uselocked_update_file) - Test:
tests/test-cchores.sh,tests/test-cchores-infra.sh,tests/test-architecture-drift.sh(ABS-044 coverage) - Guards against: re-selection loops, evidence loss on comment-post failure
ABS-045: sensitive-file-guard capability boundary (write-target guardrail)
- What:
hooks/sensitive-file-guard.sh(the guard) is a Claude Code PreToolUse hook and therefore a write-target guardrail / speedbump for accidental and naively-injected writes (PMB-020 / AP-040). This entry is the single authoritative statement of the guard’s capability boundary; every other ABS entry whose enforcement leans on the guard (ABS-012, ABS-016, ABS-027, ABS-029, ABS-030, ABS-035, ABS-038, ABS-040, ABS-041, ABS-042) scopes its clause to “Edit/Write tool-path only; Bash-mediated writes are accepted non-goals (AP-040)” and See-links here. - Capability (what the guard DOES cover): the
Edit/Write/MultiEdit/NotebookEdit/CreateFilefile_pathtool-target path ONLY. The hook matchestool_input.file_path(and MultiEditedits[].file_path) against the canonicalizedDEFAULTS/custom_patternslist and blocks (exit 2) on a match, EXCEPT the narrow conditional-allow carve-out (ABS-049, cchores-protected-affordance): an Edit/Write to an# affordance-tagged DEFAULTS path is allowed (exit 0) when a fully-verified, branch- AND file-scoped authorization marker binds the currentchore/issue-<N>-*branch AND names the specific path — and every failure/ambiguity path (no/unparseable marker, wrong branch, non-numeric issue, run_id mismatch, out-of-scope path,# secret-floor/custom_patterns/untagged) stays exit-2 (fail-closed, PAT-001 clause 5). ForBash, the hook fast-pathsexit 0before sourcing lib.sh or reading config — Bash commands are never inspected. - Conditional-allow carve-out (ABS-049) — See-linked sole-writer entries: the sole-writer script entries ABS-029 (
audit-record.sh), ABS-030 (autonomous-decision-writer.sh), ABS-042 (external-review-run.sh/config-update.sh), ABS-047 (meta-record.sh), and the relevant parts of ABS-027/ABS-035 each See-link this carve-out. Under the conservative eligible set those scripts are tagged# other-floor(NOT# affordance), so in practice their conditional relaxation on a chore branch is “none” — the exception applies ONLY to the# affordancenon-security infra (prune-scan.sh,harness-fingerprint.sh, and peers). State-file entries (ABS-012/016/038/040/041) gain NO exception. - Accepted non-goals (what the guard does NOT cover, by construction): ALL Bash-mediated writes — output redirects (
> .env,>>,tee), writer commands (cp/mv/install/ln/sed -i/perl -i/dd of=/truncate), interpreter-mediated writes (bash -c "…",python -c "…",base64 -d), and git working-tree restores (git checkout -- <file>,git restore). A directecho x > <protected>Bash command is no longer blocked. A motivated agent or a crafted injection can trivially route around the guard (name the directory not the file, route through an interpreter, use a Bash redirect). Modeling any Bash-mediated write as covered is the AP-040 category error. Bash-redirect structural leg removed 2026-06 by sfg-edit-write-only; residual accepted (advisory/owner-scaffolded files, surviving Edit/Write leg). - Posture: on the Edit/Write path the hook matches canonical-form targets against canonical-form patterns. There are now TWO non-strict behaviors, both deliberate and documented: (1) the pre-existing
custom_patternsconfig degradation — an unparsableworkflow-config.jsondrops to DEFAULTS-only matching (built-in protected patterns still enforced; user-addedcustom_patternslapse) — never fully open; and (2) the ABS-049 conditional-allow affordance — an# affordanceDEFAULTS path is exit-0 under a fully-verified authorization marker (deny-by-default:# secret-floor,# other-floor,custom_patterns, and untagged lines are never relaxed). The hook’s input-parse path (malformed stdin JSON) still fails CLOSED (INV-006/INV-008). Where a sole-writer contract needs coverage against Bash-mediated writes, that coverage must come from acmd_*phase-transition gate (ABS-029 pattern), not from this hook — the guard catches only the agent’s naive Edit/Write tool call. - Enforced-at:
hooks/sensitive-file-guard.sh(Edit/Write tool-path matcher; LC_ALL=C +set -fat hook scope),.claude/rules/hooks-pretooluse.md(DEFAULTS-only-on-config-failure narrow exception),tests/test-sensitive-file-guard.sh(Edit/Write tool-path corpus + the “Bash is never blocked” INV-001 corpus) - Violated-when: a doc, comment, or future spec frames the guard as covering ANY Bash-mediated write (redirect, writer command, interpreter, git) or as anything stronger than an Edit/Write tool-path guardrail; the hook reintroduces Bash-command inspection (PRH-001); a sole-writer contract relies on this hook for runtime out-of-band-write prevention
- Test:
tests/test-sensitive-file-guard.sh - Guards against: AP-040 (mechanism-capability mismatch — over-strong framing on a guardrail), AP-022 (the guard must still fire on real Edit/Write tool targets — not become dead code)
ABS-046: Audit-trail JSONL producer/consumer contract (.correctless/artifacts/)
- What: Per-branch JSONL files at
.correctless/artifacts/audit-trail-{branch-slug}.jsonl. Modeled on ABS-006. Mixed record shapes by design:hooks/audit-trail.sh(PostToolUse) writes hook-edit entries with an ISO-8601tsfield (date -u +%FT%TZ) plusphase,tool,file,branchand — since the instructionsloaded-hook feature (2026-07-01) — an additivesession_idfield (harness stdinsession_id, JSONnullwhen empty);/cautowrites orchestration entries to the same file with atimestampfield (notts) plustype,skill,elapsed_ms;/cauditwrites forensic records (no.file). Consumers must handle both time shapes via.ts // .timestamp. - Invariant: Additive-only schema evolution — new fields are safe because every consumer extracts specific fields via
jq, never assumes a fixed key set, neverjq -sslurps (AP-014), never counts keys. Consumers: filename-only (scripts/prune-scan.sh,scripts/wf/utility.sh— glob/rm by path) and content-parsing (scripts/compute-session-cost.shreads.phase/.timestamp;/cmetrics,/csummary;/cwtfreads.session_idand.ts // .timestamp, and identifies hook-edits by a write-tool entry — Edit/Write/MultiEdit/NotebookEdit/CreateFile/Bash — whose.filecanonicalizes under a Correctless hook root).session_idempty/null is shown as such, never treated as a match key (display-alignment aid, not a machine join — PRH-005 of the spec). The log this ABS is analogous to (.correctless/meta/instructions-loaded.jsonl, written byhooks/instructions-loaded.sh) is unbounded local telemetry: O(1) append, gitignored, accepted linear growth — there is currently no/cprunereaper for it (DD-005 of the spec named/cprunebutprune-scan.shdoes not yet cover it; accepted as gitignored/local, follow-up to optionally wire trimming). -
Enforced at: hooks/audit-trail.sh (producer — session_id additive), skills/cauto/SKILL.md (orchestration-entry producer), skills/cwtf/SKILL.md (consumer — .ts // .timestamp, write-tool + canonicalized-hook-root filter, fromjson? objects), scripts/compute-session-cost.sh + skills/cmetrics + skills/csummary (content consumers) - Violated when: a consumer
jq -sslurps the log, fails on a malformed line instead of skipping, assumes a fixed key set, reads only.ts(droppingtimestamp-shaped entries) or only.timestamp(dropping hooktsentries), or treats an empty session_id as a join match - Test: tests/test-audit-trail.sh (INV-015 session_id additive, ts ISO-8601 format), tests/test-instructions-loaded-cwtf.sh (INV-008 consumer contract — mixed shapes, write-tool gate, canonicalized hook-root filter, no jq -s), tests/test-architecture-drift.sh (ABS-046 coverage)
ABS-047: Sanctioned sole-writer for SFG-protected meta artifacts (scripts/meta-record.sh)
- What:
scripts/meta-record.sh(+ the.correctless/scripts/mirror, PAT-006) is the single sanctioned Bash-invoked writer for the three.correctless/meta/*.jsonartifacts whose documented producer skill is otherwise blocked by the sensitive-file-guard’s Edit/Write guard (the AP-037 class — “the protected asset is the deliverable, the guard has no legitimate-write affordance”). Three registered operations, each with a hardcoded destination (PRH-005):calibration-append(append one object tointensity-calibration.json’scalibration_entries[], stdin JSON — replaces the old/cverifydirect write, closing #189),pat001-set-created-at <sha>(setcreated_at_commitonpat001-measurement-due.jsononly when present and literallynull— replaces the old/cdocsblanket-scan Edit, closing #192/#226), andbaselines-write <model>|<version>(key-merge one baseline intomodel-baselines.jsonpreserving all sibling keys +schema_version— replaces the old/cmodelupgradedirect write, closing the model-baselines AP-037 instance). A CI/test-only registryscripts/sanctioned-meta-writers.tsv(which the writer does NOT runtime-read — DD-007) maps every SFG-protected meta json to its(writer, operation); the INV-006 class-closure test asserts the mapping is total (zero unbacked protected meta files). - Invariant:
meta-record.shis the sanctioned cooperative-loop write path for these three files;/cverify,/cdocs,/cmodelupgradereach them ONLY viabash .correctless/scripts/meta-record.sh <op>(discrete argv / piped stdin, neverbash -cinterpolation — TB-001), never via Write/Edit. Tri-state exit contract:0+success line (write applied);0+no change: <reason>(intended INV-009 no-op, no bytes rewritten); non-zero + the mechanicalmeta-record: FAILED <file>: <reason>stdout token (rejected/failed — the skills echo it verbatim so failure is provably surfaced, RS-005). It reuses the ABS-003 lock helpers (_acquire_state_lock/_release_state_lock) and hand-rolls only the tri-state read-validate-decide-atomic-rename body (PRH-006/DD-008) — never a bespoke lock, neverlocked_update_file(two-state; would deadlock). Append-only preservation (deep-equal prior entries, INV-001), permissive-unknown-field schema validation under the lock (INV-002), 64 KB stdin cap viawc -cwith payload passed by stdin/temp-file never argv (INV-010/AP-039), and a fail-closedrealpath/readlink -fsymlink verdict on the destination + nearest existing parents before any mkdir/temp and re-checked beforemv(INV-010/EA-004). SFG is a cooperative-loop guardrail, not a security boundary (AP-040/PMB-020): it blocks the naive agent Edit/Write but does not inspect Bash, so out-of-band Bash writes to the meta files or the writer are accepted non-goals; “sole writer” means the sanctioned/expected path, enforced against Edit/Write by SFG and against wrong content by the writer’s validation + the append-only/key-merge tests. - Enforced at:
scripts/meta-record.sh(writer) +.correctless/scripts/meta-record.sh(mirror),scripts/sanctioned-meta-writers.tsv(registry),hooks/sensitive-file-guard.sh(DEFAULTS — writer + three target meta files Edit/Write-blocked),skills/cverify/SKILL.md/skills/cdocs/SKILL.md/skills/cmodelupgrade/SKILL.md(rewired producers),scripts/lib.sh(reused lock helpers),.claude/rules/sfg-deliverable.md(AP-037 lift-and-restore affordance for the writer),tests/test-meta-record.sh+tests/test-sensitive-file-guard.sh - Violated when: any of the three skills writes a target meta file via Write/Edit; the writer invents a bespoke lock, wraps
locked_update_file, routes the payload through argv, uses the lexicalcanonicalize_pathfor the symlink verdict, exits 0 after an attempted-but-unlanded write (PRH-004), reorders/drops a prior calibration entry or a sibling baseline key (PRH-001), setscreated_at_commiton an absent field or a non-target file (#192/#226), or a SFG-protected meta json has no registry row (class not closed) - Test:
tests/test-meta-record.sh(INV-001..010, PRH-001..006, BND-001, exit-code table, INV-006 class closure, INV-007 concurrent no-lost-update),tests/test-sensitive-file-guard.sh(INV-005 writer Edit/Write-blocked, PRH-003 target meta files stay blocked),tests/test-allowed-tools-check.sh+tests/test-harness-fingerprint.sh(EXT-003 cmodelupgrade grant flip)
ABS-048: Generated test-count artifact (deliberately NOT a sole-writer)
- What:
tests/test-inventory.json({"schema_version": 1, "test_file_count": N}) is the authoritative test-file count for thetests/test-ap031-fixture-divergence.shR-006(c) gate and the.correctless/AGENT_CONTEXT.mdTests-row figure, decoupling that count from the/cchoresINV-010-protected prose docs (#219). Its writer/reader isscripts/gen-test-inventory.sh(+ thecorrectless/scripts/mirror) exposing the single shared count command (INV-002): acountsubcommand (prints the integer only) and awritesubcommand (atomically regenerates the artifact). “Actual” is computed over the git index (git ls-files --cached -z -- 'tests/test*.sh', direct children only, NUL-delimited end-to-end,env -iclear-and-allowlist so no ambientGIT_*var can redirect it, pinned to the repo root resolved from the script’s own${BASH_SOURCE[0]}via a marker-confirmed two-layout discriminator — never$PWD, nevergit rev-parse --show-toplevel), so R-006(c) and the writer can never drift. R-006(c) assertstest_file_count == count(exact==, no tolerance band). - Writers:
/ctdd,/cchores,/cdocs, humans, CI — any actor (multi-writer, last-write-wins; single-file, no lock). Regeneration is consumer-scoped: it runs only where the R-006(c) consumer markertests/test-ap031-fixture-divergence.shexists (a generator-side no-op guardgen-test-inventory: no consumer — skippedPLUS per-skill wiring guarded on the same marker), so a downstream install with no consumer never creates or stages an orphan artifact. - Consumer:
tests/test-ap031-fixture-divergence.shR-006(c) — the only consumer; it obtains “actual” ONLY viagen-test-inventory.sh count, never a re-implementedfind/wc/grep -c. - Invariant: byte-pinned deterministic serialization (fixed
printftemplate, no timestamp, not jq-formatted → identical bytes across jq 1.7/1.8 and platforms); idempotent regen (a no-op rewrites no bytes — same inode/mtime, printsno change); atomic glob-safe write (dotfilemktempintests/+mv -f, trap cleanup on the full fatal-signal set); tri-state fail-loud exit contract borrowed frommeta-record.sh(ABS-047) —0+success /0+no change/ non-zero + a mechanicalgen-test-inventory: FAILED <reason>stdout token the callers echo verbatim.tests/test-inventory.jsonis tracked, unprotected, and PR-reaching (never under a gitignored//cchores-stripped path). INV-010 is left entirely unchanged. - Deviation note (LOAD-BEARING — do not “fix”): this abstraction deliberately diverges from the sanctioned sole-writer family (ABS-029/030/042/047). It is NOT a sole-writer (any actor writes it), has no lock (last-write-wins is acceptable for a single derived integer), and is deliberately NOT in
hooks/sensitive-file-guard.shDEFAULTS — it must stay unprotected so every actor can regenerate it freely. Adding SFG protection or sole-writer enforcement re-introduces the #219 deadlock (INV-005): the entire point is that a/cchoresnet-new-test fix can regenerate + stage the count without touching an edit-restricted doc. It borrows ONLY the tri-stateFAILED-token exit discipline frommeta-record.sh, not the lock or the protection. A future audit MUST NOT flag the missing sole-writer/SFG protection as a defect — the absence is the design. - Enforced at:
scripts/gen-test-inventory.sh(writer/reader) +correctless/scripts/gen-test-inventory.sh(mirror),tests/test-ap031-fixture-divergence.shR-006(c) (consumer),skills/cchores/SKILL.md/skills/ctdd/SKILL.md/skills/cdocs/SKILL.md(consumer-scoped regeneration wiring +allowed-tools),.correctless/AGENT_CONTEXT.md(informational~N test scriptsfigure + authoritative-source pointer, INV-007) - Violated when: the artifact carries a timestamp/nondeterministic field or a no-op regen rewrites bytes (churn); the generator or R-006(c) computes “actual” via a divergent command or resolves a different
tests/dir; the artifact is added to SFG DEFAULTS or made a sole-writer (re-arms #219); it lands under a gitignored/stripped path; a downstream (non-consumer) run creates or stages the artifact; or a caller ignores a non-zero generator exit - Test:
tests/test-gen-test-inventory.sh(INV-001 idempotency, INV-002 shared-command + 4-context resolver + index universe +env -ipin, INV-003 consumer-guard/atomic/tri-state, INV-004 R-006(c) validation matrix, BND-001..003, MA-H1 NUL-safety, MA-M2 discriminator),tests/test-test-inventory-wiring.sh(INV-005 SFG-unprotected + INV-010 unchanged, INV-006 #219 mechanism repro + consumer-scope + staging order, INV-007 no-scrape + row-converted, INV-008 mirror parity, INV-009 allowed-tools covers-invocation),tests/test-ap031-fixture-divergence.shR-006(c)
ABS-049: Branch- and file-scoped SFG conditional-allow allowlist + per-run authorization marker (/cchores affordance)
- What: the
cchores-protected-affordancefeature (PRH-003 v2) adds a narrow, mode-gated conditional-allow carve-out tohooks/sensitive-file-guard.sh(See-link ABS-045, the capability boundary). An Edit/Write to an# affordance-tagged DEFAULTS path is allowed (exit 0) iff a per-run authorization marker (.correctless/artifacts/chores-protected-authorized.json, schema{branch, issue, run_id, allowed_paths, authorized_at}) binds the target’s own git worktree branch (byte-exactmarker.branch, numericchore/issue-<issue>-*), itsrun_idequals the chore-run manifest’srun_id, and the canonical target ∈marker.allowed_paths. Every failure/ambiguity path stays exit-2 (fail-closed, INV-011). The DEFAULTS block carries a single-source 3-way classification tag on every line —# affordance/# secret-floor/# other-floor— from whichis_secret_floor()/is_affordance_eligible()derive (deny-by-default: only# affordanceis eligible;# secret-flooris a hard floor never reachable via a naive Edit/Write). Conservative eligible set = non-security infra only (prune-scan.sh,harness-fingerprint.sh, and peers); security/sole-writer guards,lib.sh, state artifacts, and the marker are# other-floor. - Invariant: the marker is written ONLY by the sanctioned
scripts/chores-authorize.sh write --issue <N>(INV-014 sole-writer; marker + writer three-form in DEFAULTS,# other-floor; registered inscripts/sanctioned-chores-writers.tsv; excluded from /cchores’Write(.correctless/artifacts/*)grant viadisallowed-tools). The affordance is active only in explicit-issue mode (INV-001, PRH-001); no-arg mode is unchanged v1. The mode-aware pre-selection + post-cdebug diff gates live in the codedscripts/cchores-diff-check.sh(INV-006/007;# secret-floor+ shared-doc legs are marker-independent/authoritative; the classification-immutability set-equality check is INV-009 leg a). Mechanism honesty (PMB-020/AP-040): this is a cooperative-loop guardrail, not a perimeter — a Bash-mediated write is the accepted residual, contained by INV-007 + never-merge (PRH-003) + outbound redaction + the INV-010 PR banner. - Enforced at:
hooks/sensitive-file-guard.sh(+ mirror) (tagged DEFAULTS,is_secret_floor/is_affordance_eligible,_sfg_affordance_allows, main-guard),scripts/chores-authorize.sh(+ mirror) (writer +check-capabilityprobe),scripts/cchores-diff-check.sh(+ mirror) (mode/diff/classification gates),scripts/cchores-emit.sh(INV-010 banner flags),scripts/sanctioned-chores-writers.tsv(registry),skills/cchores/SKILL.md(mode-gating, allowed/disallowed-tools),.claude/rules/hooks-pretooluse.md(second carve-out),.claude/rules/sfg-deliverable.md(AP-037 deliverable enumeration) - Violated when: a marker/branch/mode/unclassified-drift makes a
# secret-floor,# other-floor,custom_patterns, or untagged pattern writable; the affordance activates in no-arg mode; a hook code path on the affordance branch exits non-0/non-2; the marker is written by anything other thanchores-authorize.sh; a chore fix alters the DEFAULTS classification/allowlist/marker-check logic (INV-009 leg a) - Test:
tests/test-cchores-protected-affordance.sh(INV-001..015, PRH-001..003)
ABS-050: Design Contract lens registry — primary-SSOT for the /creview-spec Design Contract Checker lenses
- What:
agents/design-contract-lenses.tsv(4-column TSV:lens_id,keyword,source_pmb,summary) is the single source of truth for the PMB-derived lenses of the/creview-specDesign Contract Checker. Each row (DCL-NNN) is a documented implementation-pinning bug-class lens (DCL-001 cardinality/PMB-013 … DCL-008 mechanism-capability-mismatch/PMB-020). The consumeragents/review-spec-design-contract.mdcarries a## PMB-derived lensessection with one bullet per registry row, each tagged with itsDCL-NNNid + the registrykeyword+ a concrete condition, alongside the retained generic lens. Unlike the ABS-047 sole-writer family (where the registry is derived and a completeness test enumerates a primary set), here the registry is the source of truth and the agent is derived from it — same completeness-test technique, opposite authority direction (DD-005). The registry is source-only:sync.shmirrors onlyagents/*.md, so the.tsvnever ships to user installs — they receive the lens bodies inline in the agent.md, and only the correctless self-test reads the registry (DD-001). - Invariant: registry↔agent is bound by set-equality — every registry
lens_idis referenced by itsDCL-NNNtoken in the agent (INV-001 completeness) and everyDCL-NNNin the agent maps to a registry row (INV-002 no-orphans), catching both documented-not-implemented and orphan/typo ids (DD-003). Each agent bullet additionally binds the registrykeywordverbatim + a directive term{BLOCKING,flag}+ a condition token{when,if}+ a ≥24-char post-strip body (INV-005 anti-gaming), and the substance loop iterates the live registry, not a hard-coded seed, so future rows are covered. The registry is well-formed (INV-003: LF-only, no UTF-8 BOM, exactly 4 tab-fields per row, unique^DCL-[0-9]{3}$ids,^PMB-[0-9]{3}$pmbs). Neither the agent nor the/creview-specpreamble file-load list readsCLAUDE.mdto obtain the lens set (INV-007 / PRH-001 / PRH-002) — the CLAUDE.md→registry link is a prompt-level/cpostmortemStep-3 convention (DD-004), never a prose-scan (which would re-introduce AP-031/AP-036, the origin class). The registry is absent from the distribution mirror (INV-009). - Enforced at:
agents/design-contract-lenses.tsv(registry / SSOT),agents/review-spec-design-contract.md(+correctless/agents/review-spec-design-contract.mdmirror — the derived consumer),skills/cpostmortem/SKILL.mdStep 3 (the prompt-level CLAUDE.md→registry convention, DD-004),tests/test-design-contract-lens-sync.sh(set-equality + well-formedness + anti-gaming + self-scan enforcement) - Violated when: a registry row’s
lens_idis not referenced in the agent (documented-but-unwired — the DA-001/AP-036 gap this abstraction exists to close); the agent references aDCL-NNNwith no registry row (orphan/typo); a bullet drops its keyword, directive, or condition token or is padded below the body floor; the substance loop reverts to a hard-coded seed so a futureDCL-009+row escapes the substance checks; the agent or preamble is wired to readCLAUDE.md; or the.tsvis committed anywhere undercorrectless/. - Test:
tests/test-design-contract-lens-sync.sh(INV-001..010, PRH-001/002, BND-001 — set-equality via two shared-regex extractors, 8 anchored seed rows, live-registry substance loop, 15-case malformed-registry rejection suite, PRH-001 self-scan whitelist, mirror parity, source-only find-check)