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 with CORRECTLESS_LOCK_IMPL=ln): (1) flock — the default when the flock binary is present (Linux/CI). Kernel advisory locking on a persistent, never-deleted ${state_file}.flock file 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 — when flock is 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, then ln(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 -p runs inside the retry loop and the temp is a sibling of lock_dir so a releaser’s rmdir cannot starve a waiter; only a kill -0-dead holder is reclaimed (atomic mv). History: the lock moved mkdir → O_EXCL → ln → flock+ln — mkdir was 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}.flock file is persistent (never deleted — deleting it while waiters hold fds would reopen a double-hold window); release is flock -u + close fd, and any 2>/dev/null on the fd exec MUST wrap a { ...; } group (a redirection on exec itself is permanent and would silence later diagnostics). ln fallback: _release_state_lock is holder-owned ($$), tears down with rm -f "$lock_dir/pid" then a best-effort rmdir — never a blind rm -rf; the pid temp is a sibling of lock_dir and mkdir -p runs inside the loop so a releaser’s rmdir cannot starve a waiter. External reusers (e.g. scripts/meta-record.sh, ABS-047) call _acquire_state_lock/_release_state_lock directly 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/null is placed on an fd exec (permanently silences the shell’s stderr); in the ln fallback, mutual exclusion is gated on mkdir alone (or an empty-then-write pid create) rather than the atomic ln create-with-content, the pid temp is staged inside lock_dir, mkdir -p is hoisted out of the retry loop, or a release uses a blind rm -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=ln to 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’s register_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 generalized KNOWN_HOOK_TYPES map — currently PreToolUse, PostToolUse, and InstructionsLoaded (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 single KNOWN_HOOK_TYPES type→timeout map (PostToolUse=1000ms; all other types incl. Pre/InstructionsLoaded=5000ms) and emitted as timeout_msnot a hardcoded per-type case arm or a duplicated literal. Adding a new hook type requires only a KNOWN_HOOK_TYPES entry, no new registration statement (AP-024/PMB-003 avoidance).
  • What (mechanism): register_hooks() discovers hooks into a single loop gated by KNOWN_HOOK_TYPES and emits each via the shared _upsert_command_hook/_upsert_agent_hook helpers, 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/if arm 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-append writer (ABS-047), never via Write/Edit — intensity-calibration.json is 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}.jsonl recording 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, the total_cost_usd and 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 skill field derived from the workflow phase via a hardcoded mapping (R-001 in token-tracking-skill-field spec). Skills may also append entries with their own skill field. Consumers should use the skill field for category attribution when present, falling back to phase for historical entries without it. Consumers must handle malformed lines (skip, not fail) and missing files (default to 0). The total_tokens field 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 /cauto when 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: /cauto is 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 /cauto writes 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 /csetup from templates/preferences.md. Read by /cauto and all pipeline skills. Edited by the human. Contains codified judgment calls: QA finding triage, documentation scope, commit granularity, escalation sensitivity, PR creation mode.
  • Invariant: /csetup scaffolds the file (idempotent — never overwrites). /cauto and 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/*.md with YAML paths: 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 the paths: entries. Migrated PAT entries live here as full-body rules; .correctless/ARCHITECTURE.md retains 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 a paths: key, and the set of paths: entries for hooks-pretooluse.md is set-equal to the set of PreToolUse hooks discovered via HOOK_TYPE: PreToolUse headers (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, the paths: 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}.md in source and are propagated to correctless/agents/{name}.md by sync.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 frontmatter name: field. Skills invoke the agent via namespaced Task(subagent_type="correctless:{name}") — never inline blockquoted prompts, never bare subagent_type="{name}". Current consumers: skills/carchitect/SKILL.md (architecture-reviewer), skills/ctdd/SKILL.md Step 4 RED phase (ctdd-red), skills/ctdd/SKILL.md GREEN phase (ctdd-green), skills/caudit/SKILL.md step 6a (fix-diff-reviewer), skills/cauto/SKILL.md Tier 2 (decision-agent), skills/cauto/SKILL.md Tier 3 + review triage (supervisor), skills/cpr-review/SKILL.md Step 3 (architecture-compliance-reviewer), skills/creview-spec/SKILL.md Step 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.md Step 2 (cspec-research — network-read class).
  • Invariant: Single source of truth. No inline prompt duplication in any skills/*/SKILL.md file. The source file under agents/ and the distribution file under correctless/agents/ are byte-equal. Frontmatter name: equals filename basename. Frontmatter tools: 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, and tests/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}.md recording 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}.md written 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.sh adds 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}.md generated 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}.json written 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: /cauto orchestrator (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 /cauto runs 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 /cauto run 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.json defining 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 /csetup with 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_autonomously hardcoded 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.sh adds 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}.json recording 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_issuance reads .correctless/meta/overrides/*.json (last 10 by completed_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 including preserve_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 /cauto runs. Each file is a JSON metadata wrapper with task_slug, branch, completed_at, override_count, and overrides array. Sole writer: /cauto (via preserve_override_log in scripts/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 by preserve_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_log writes to this directory. Files follow the metadata wrapper schema. Cap enforced at write time. Malformed files (missing completed_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_log writes 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 setup after 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_freshness in scripts/lib.sh. Lifecycle: per-install local state, overwritten each setup run. Gitignored.
  • Invariant: Only setup writes the manifest. check_install_freshness is 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 setup writes 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.md as a fenced YAML block between <!-- correctless:entrypoints:start --> and <!-- correctless:entrypoints:end --> marker comments. Schema: list of objects with fields name (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, uses test_via for 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. The scope field remains a list of glob patterns; changing its type or matching semantics is a breaking change requiring a new field.
  • Invariant: Only /carchitect writes 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 /carchitect writes entrypoints YAML, or the extraction script performs semantic validation (enum checks, field presence), or the test_via or scope fields 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: /ctdd test auditor (verifies tests satisfy contracts during the test audit phase). Format: three fields per [integration] rule — Entry (entrypoint to use, derived from ABS-023 test_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/*.json defining PreToolUse or PostToolUse hooks with type: "agent". Unlike command hooks (bash scripts with HOOK_TYPE/HOOK_MATCHER metadata headers per ABS-004), agent hooks use JSON fields: hook_type (PreToolUse PostToolUse), 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/ with type: "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}.json containing 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_usd in calibration entries), /cmetrics (R-010 — ROI calculations with actual USD), hooks/statusline.sh (reads a lightweight cache subset via background compute-session-cost.sh --cache). Schema defined in session-cost-analysis spec R-005: includes total_cost_usd, by_phase, by_subagent, model_breakdown, pricing_used, unknown_models, warnings. The --cache mode 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.sh is the sole writer. All consumers handle missing artifacts gracefully (R-011) — dashboard falls back to token-log data, cverify omits actual_cost_usd, cmetrics falls back to token estimates, statusline omits cost display when cache is missing or total_cost_usd is 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.json recording the literal fingerprint string "{model_name}|{HARNESS_VERSION}" (no hashing — HI-1 round-2 disposition) plus harness_version (integer), model (string), and timestamp (ISO-8601) fields. Companion file at .correctless/meta/model-baselines.json (with schema_version: 1 from creation) stores per-{model+version} baseline metrics for /cmodelupgrade regression comparison. Per-feature granularity (per-skill deferred until upstream producers exist). Session-id used in flag-file paths is produced by get_current_session_id() in scripts/lib.sh (single source of truth — no per-skill derivation drift permitted).
  • Invariant: Sole writer of harness-fingerprint.json is scripts/harness-fingerprint.sh. Sole writer of model-baselines.json is /cmodelupgrade via the sanctioned scripts/meta-record.sh baselines-write writer (ABS-047) — as of the calibration-writer feature, /cmodelupgrade no longer holds a direct Write(model-baselines.json) grant; it key-merges through meta-record.sh, preserving all sibling baseline keys + schema_version and failing loud on a schema mismatch (EXT-002). Sole writer of HARNESS_VERSION constant is human commit (scripts/harness-fingerprint.sh is 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 no cmd_* 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.sh covers 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 /csetup Step 2.6 from templates/test-features/baseline.md (idempotent — never overwrites). Consumed by /cmodelupgrade --capture-baseline as the controlled-baseline reference feature run end-to-end through /cauto to 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 (no src/ directory, different language conventions) adapt the file paths but keep the spec invariant structure intact.
  • Invariant: /csetup is the sole scaffolder (template → destination copy, idempotent guard via [ ! -f ] check). Once scaffolded, the user is the sole editor. /cmodelupgrade --capture-baseline reads but never writes the file. Falls back to no-baseline mode if the file is absent.
  • Enforced at: skills/csetup/SKILL.md Step 2.6 (scaffolding), templates/test-features/baseline.md (template producer), sync.sh (template propagation to correctless/templates/test-features/), skills/cmodelupgrade/SKILL.md (consumer)
  • Violated when: a skill other than /csetup writes to .correctless/test-features/baseline.md, the scaffold step overwrites a user-edited file, or /cmodelupgrade --capture-baseline fails 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 with findings: [] and rejected: [] 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 timestamp YYYY-MM-DDTHH:MM:SSZ matching workflow state). Each findings[] entry MAY include an optional escape_type field (valid values: implementation, spec, non-escape, or null/absent for unclassified); audit-record.sh write-round validates 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-round and append-history subcommands; verify lives in the gate, not the script). Consumers: /cmetrics (last-Olympics staleness via max(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), /caudit itself on subsequent runs (recurring-pattern detection, prior-finding context for Round 1 specialists, per-finding escape_type classification at specialist submission time), /cdevadv (recurring-pattern referrals).
  • Invariant: scripts/audit-record.sh is the sole writer and is itself sensitive-file-guard protected (matches the harness-fingerprint.sh sole-writer-convention, AP-022 mitigation). cmd_audit_done in hooks/workflow-advance.sh refuses the transition to done unless at least one round-JSON exists whose started_at field equals the workflow state’s started_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 with findings: [] and rejected: [] — absence of the file is NOT evidence of “no findings.” /cmetrics MUST 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_done precondition + 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_done transitions phase to done without 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 /caudit invokes audit-record.sh write-round; cmd_audit_done adds an env-var or flag escape hatch; audit-record.sh constructs destination paths from config-derived input; the round-JSON path format diverges from audit-{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.sh script, invoked by /cauto OR /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, /cwtf accountability analysis
  • Invariant: the invoking orchestrator (/cauto or /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 via autonomous-decision-writer.sh.
  • Enforced at: skills/cauto/SKILL.md and skills/cchores/SKILL.md (writer-script invocation, JSONL growth check, R-013 confirmation gate), hooks/sensitive-file-guard.sh (Edit/Write tool-path guard for autonomous-decisions-*.jsonl; Bash-mediated writes are accepted non-goals per AP-040/ABS-045 — no cmd_* 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 /cauto or /cchores writes 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: /cauto orchestrator (writes manifest as first action after phase gate, updates completed_steps after each pipeline step, writes status: "complete" as final action)
  • Consumers: /cauto R-004 resumption (reads manifest to detect truncation and report missed steps), /cstatus R-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 status is set to "complete" before pipeline summary (Step 10) completes; a consumer other than /cauto or /cstatus writes 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.html generated by scripts/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.sh is 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.json centralizing 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 /cdocs and 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.sh reconstructs 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_id pair.
  • 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}.json capturing 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 /ctdd pipeline — it attempts to violate spec invariants through adversarial inputs and boundary conditions. Sole writer: /ctdd orchestrator (probe round step). Consumers: none initially (future: /cmetrics for probe survival rates). The artifact is committed to the repository via a TB-004c allowlist exception in /cauto Step 8.1.
  • Invariant: Only the /ctdd orchestrator may write this file. File absence triggers dormant degradation in future consumers (PAT-019 — no error, no blocking, graceful no-op). The artifact path uses branch_slug from 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 /ctdd writes 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 /ctdd probe 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.sh is decomposed into a thin dispatcher that sources 3 module files from scripts/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 sets SCRIPT_DIR before sourcing — modules use $SCRIPT_DIR for path resolution, never BASH_SOURCE[0].
  • Invariant: No cmd_* function body in the dispatcher; no shared helper function defined in a module; no BASH_SOURCE[0] usage in module code; no function defined in more than one module. Module files are protected by hooks/sensitive-file-guard.sh on the Edit/Write tool-path only (Bash-mediated writes are accepted non-goals, AP-040/ABS-045); the surviving runtime leg is the test-workflow-advance-decomp.sh structural tests (test-time, not write-time).
  • Enforced at: hooks/sensitive-file-guard.sh (DEFAULTS), tests/test-workflow-advance-decomp.sh (structural tests), setup (installs scripts/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}.json carrying review-phase lens recommendations and mini-audit outcomes. Writers: /creview-spec (high+ intensity), /creview (standard intensity). /ctdd updates 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 in cmd_done)
  • Violated when: a consumer errors on absent artifact; artifact gates a phase transition; /ctdd creates 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.json produced by scripts/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-based occurrences >= 3 filtering — pure consumer, not regeneration trigger), /creview (reads the brief file directly via jq-based occurrences >= 3 filtering — 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.sh is the sole writer. /cspec is 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; /cspec treats 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 /cprune writes 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-/cprune writers 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 /cprune writes 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_pattern in scripts/prune-scan.sh is the sole authority that maps each pattern in the scanner’s artifact_patterns list to exactly one of four slug-type enum members: branch-slug, task-slug, session-slug, or unclassified. The classification determines which live-slug set the safety belt consults: branch-slug patterns match against the live-branch-slug set (computed via branch_slug() from scripts/lib.sh); task-slug patterns match against the live-task-slug set (derived from basename(.spec_file, ".md") for each workflow-state-*.json whose .branch is in the live branch set — no .task fallback per EA-003); session-slug patterns are never live-prunable; unclassified patterns are skipped with an observable JSON skipped_unclassified entry 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 — every artifact_patterns entry must have a row, every table row must have an artifact_patterns entry, and the classification must agree). Consumers: skills/cprune/SKILL.md and skills/cstatus/SKILL.md (read .candidates from the wrapped object — never the top-level value as an array).
  • Invariant: scripts/prune-scan.sh’s _classify_artifact_pattern is defined exactly once and is total over artifact_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 the artifact_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 the prune-scan-substring-match rule in scripts/antipattern-scan.sh check_shell(). Slug values are validated by _slug_is_safe at extraction boundaries AND ERE metacharacters are escaped by _escape_ere_metachars before 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 on started_at (primary) → composite task|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 via find -print under set -f (no glob expansion, dotglob neutralized) 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 (structural prune-scan-substring-match rule), 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_patterns without a _classify_artifact_pattern case; _classify_artifact_pattern returns a value outside the four-enum set; two function definitions of _classify_artifact_pattern exist; the producer-pattern table drifts from artifact_patterns in either direction; substring primitives appear in scripts/prune-scan.sh for 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 of find -print under set -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 /cprune when 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.json recording 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.sh invoked with the explicit --update-baseline flag. The scanner does NOT update the baseline as a side effect of scanning — autonomous /cprune runs, /cstatus runs, and default-mode /cprune runs all leave the baseline untouched. Baseline update happens only when /cprune SKILL.md invokes the scanner with --update-baseline after interactive human confirmation. Consumed by scripts/prune-scan.sh (read at scan start to detect newly-added patterns). Gitignored.
  • Invariant: scripts/prune-scan.sh --update-baseline is the sole writer. /cprune autonomous mode (mode: autonomous prompt context) NEVER passes --update-baseline to the scanner — structural assertion via grep on skills/cprune/SKILL.md autonomous code path. For any pattern present in current artifact_patterns but absent from the baseline, candidates emitted via that pattern carry risk: "medium" (interactive-only) with reason text Newly added pattern '{pattern}' — first scan after upgrade; review before deletion — preventing auto-promotion of newly-added patterns to low risk 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-baseline sole-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-baseline only 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-baseline flag; /cprune passes --update-baseline in autonomous mode; a newly-added pattern emits a low-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 than prune-scan.sh --update-baseline writes 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 low risk before human review

ABS-041: SFG lift-and-restore sentinel + final-state backstop

  • What: The .correctless/.sfg-lift-active committed sentinel file plus the scripts/check-no-pending-sfg-lift.sh final-state backstop together implement the AP-037 lift-and-restore contract for SFG-protected deliverables. When a feature’s primary deliverable is itself in the hooks/sensitive-file-guard.sh DEFAULTS 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-active is itself in SFG DEFAULTS (RS-018) so the guard’s own disable-switch is guarded. During iteration the sentinel makes tests/test-fix-diff-reviewer-agent.sh SKIP its lift-state assertion (keeping commands.test and /cauto consolidation unblocked); the dedicated backstop script — deliberately OUTSIDE the tests/test-*.sh glob — 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 CI sfg-lift-check job, /cauto Step 8 (installed path), the operator rule .claude/rules/sfg-deliverable.md, and backstopped by the cmd_done gate in hooks/workflow-advance.sh.
  • Invariant: scripts/check-no-pending-sfg-lift.sh is the sole final-state checker. The cmd_done transition gate in hooks/workflow-advance.sh refuses the done transition while .correctless/.sfg-lift-active exists 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.md and .correctless/.sfg-lift-active are both present in the DEFAULTS of both hooks/sensitive-file-guard.sh and 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_done gate — sentinel refusal + HEAD-SHA test-success sentinel), .github/workflows/ci.yml (dedicated sfg-lift-check job, unconditional, in test-suite needs), skills/cauto/SKILL.md Step 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_done gate does not refuse on sentinel-present or omits the HEAD-SHA test-success sentinel; the CI sfg-lift-check job is cosmetic (falsy if:, continue-on-error: true, buried in the test-*.sh loop or a matrix) or absent from test-suite’s own needs:; the sentinel is not in SFG DEFAULTS; /cauto Step 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.sh is the sole writer of .correctless/meta/external-review-history.json and 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-003 locked_update_file pattern (RS-012). /creview-spec never writes the history file — the direct Write grant 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 (record in the same exec as the codex call), ABS-003-locked, self-seeding, and run_id-keyed (the --output-last-message path embeds the full run_id so concurrent runs never TOCTOU-collide). external-review-run.sh and config-update.sh are both present in the SFG DEFAULTS of hooks/sensitive-file-guard.sh and 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; the pending subcommand 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 to status: "complete" | "aborted" (+ abort_reason) | "noop". Distinct from ABS-031’s /cauto pipeline-manifest — no sole-writer conflict (different filename, different writer).
  • Sole writer: /cchores. Consumers: /cstatus (truncation surfacing — a manifest left in_progress denotes 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_progress denotes truncation; branch_slug is derived via lib.sh branch_slug() (verified to handle the chore/ 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_progress and 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: /cchores via lib.sh locked_update_file (ABS-003 advisory lock — concurrent-write safe). Consumers (read-only): INV-002 selection filter (skip any issue with an aborted attempt), /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) — /cprune does NOT auto-delete it; stale resolved attempts 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-/cchores writer touches it; concurrent writes corrupt it (must use locked_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/CreateFile file_path tool-target path ONLY. The hook matches tool_input.file_path (and MultiEdit edits[].file_path) against the canonicalized DEFAULTS/custom_patterns list 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 current chore/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). For Bash, the hook fast-paths exit 0 before 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 # affordance non-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 direct echo 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_patterns config degradation — an unparsable workflow-config.json drops to DEFAULTS-only matching (built-in protected patterns still enforced; user-added custom_patterns lapse) — never fully open; and (2) the ABS-049 conditional-allow affordance — an # affordance DEFAULTS 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 a cmd_* 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 -f at 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-8601 ts field (date -u +%FT%TZ) plus phase,tool,file,branch and — since the instructionsloaded-hook feature (2026-07-01) — an additive session_id field (harness stdin session_id, JSON null when empty); /cauto writes orchestration entries to the same file with a timestamp field (not ts) plus type,skill,elapsed_ms; /caudit writes 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, never jq -s slurps (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.sh reads .phase/.timestamp; /cmetrics, /csummary; /cwtf reads .session_id and .ts // .timestamp, and identifies hook-edits by a write-tool entry — Edit/Write/MultiEdit/NotebookEdit/CreateFile/Bash — whose .file canonicalizes under a Correctless hook root). session_id empty/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 by hooks/instructions-loaded.sh) is unbounded local telemetry: O(1) append, gitignored, accepted linear growth — there is currently no /cprune reaper for it (DD-005 of the spec named /cprune but prune-scan.sh does 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 -s slurps the log, fails on a malformed line instead of skipping, assumes a fixed key set, reads only .ts (dropping timestamp-shaped entries) or only .timestamp (dropping hook ts entries), 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/*.json artifacts 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 to intensity-calibration.json’s calibration_entries[], stdin JSON — replaces the old /cverify direct write, closing #189), pat001-set-created-at <sha> (set created_at_commit on pat001-measurement-due.json only when present and literally null — replaces the old /cdocs blanket-scan Edit, closing #192/#226), and baselines-write <model>|<version> (key-merge one baseline into model-baselines.json preserving all sibling keys + schema_version — replaces the old /cmodelupgrade direct write, closing the model-baselines AP-037 instance). A CI/test-only registry scripts/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.sh is the sanctioned cooperative-loop write path for these three files; /cverify, /cdocs, /cmodelupgrade reach them ONLY via bash .correctless/scripts/meta-record.sh <op> (discrete argv / piped stdin, never bash -c interpolation — 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 mechanical meta-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, never locked_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 via wc -c with payload passed by stdin/temp-file never argv (INV-010/AP-039), and a fail-closed realpath/readlink -f symlink verdict on the destination + nearest existing parents before any mkdir/temp and re-checked before mv (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 lexical canonicalize_path for 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), sets created_at_commit on 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 the tests/test-ap031-fixture-divergence.sh R-006(c) gate and the .correctless/AGENT_CONTEXT.md Tests-row figure, decoupling that count from the /cchores INV-010-protected prose docs (#219). Its writer/reader is scripts/gen-test-inventory.sh (+ the correctless/scripts/ mirror) exposing the single shared count command (INV-002): a count subcommand (prints the integer only) and a write subcommand (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 -i clear-and-allowlist so no ambient GIT_* 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, never git rev-parse --show-toplevel), so R-006(c) and the writer can never drift. R-006(c) asserts test_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 marker tests/test-ap031-fixture-divergence.sh exists (a generator-side no-op guard gen-test-inventory: no consumer — skipped PLUS 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.sh R-006(c) — the only consumer; it obtains “actual” ONLY via gen-test-inventory.sh count, never a re-implemented find/wc/grep -c.
  • Invariant: byte-pinned deterministic serialization (fixed printf template, 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, prints no change); atomic glob-safe write (dotfile mktemp in tests/ + mv -f, trap cleanup on the full fatal-signal set); tri-state fail-loud exit contract borrowed from meta-record.sh (ABS-047) — 0+success / 0+no change / non-zero + a mechanical gen-test-inventory: FAILED <reason> stdout token the callers echo verbatim. tests/test-inventory.json is 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.sh DEFAULTS — 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 /cchores net-new-test fix can regenerate + stage the count without touching an edit-restricted doc. It borrows ONLY the tri-state FAILED-token exit discipline from meta-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.sh R-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 scripts figure + 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 -i pin, 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.sh R-006(c)

ABS-049: Branch- and file-scoped SFG conditional-allow allowlist + per-run authorization marker (/cchores affordance)

  • What: the cchores-protected-affordance feature (PRH-003 v2) adds a narrow, mode-gated conditional-allow carve-out to hooks/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-exact marker.branch, numeric chore/issue-<issue>-*), its run_id equals the chore-run manifest’s run_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 which is_secret_floor()/is_affordance_eligible() derive (deny-by-default: only # affordance is eligible; # secret-floor is 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 in scripts/sanctioned-chores-writers.tsv; excluded from /cchores’ Write(.correctless/artifacts/*) grant via disallowed-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 coded scripts/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-capability probe), 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 than chores-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-spec Design 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 consumer agents/review-spec-design-contract.md carries a ## PMB-derived lenses section with one bullet per registry row, each tagged with its DCL-NNN id + the registry keyword + 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.sh mirrors only agents/*.md, so the .tsv never 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_id is referenced by its DCL-NNN token in the agent (INV-001 completeness) and every DCL-NNN in 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 registry keyword verbatim + 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-spec preamble file-load list reads CLAUDE.md to obtain the lens set (INV-007 / PRH-001 / PRH-002) — the CLAUDE.md→registry link is a prompt-level /cpostmortem Step-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.md mirror — the derived consumer), skills/cpostmortem/SKILL.md Step 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_id is not referenced in the agent (documented-but-unwired — the DA-001/AP-036 gap this abstraction exists to close); the agent references a DCL-NNN with 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 future DCL-009+ row escapes the substance checks; the agent or preamble is wired to read CLAUDE.md; or the .tsv is committed anywhere under correctless/.
  • 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)