Sanctioned Sole-Writer for SFG-Protected Meta Artifacts
One Bash-invoked writer (
scripts/meta-record.sh) for the three.correctless/meta/*.jsonfiles whose producer skill was silently blocked by the sensitive-file-guard. Spec:.correctless/specs/calibration-writer.md. Architecture: ABS-047 (amends ABS-005, ABS-027). Antipattern closed: AP-037. Resolves #189, #192, #226.
What It Does
Three SFG-protected meta artifacts had no sanctioned writer, so the documented skill that must write them was silently blocked by the sensitive-file-guard’s Edit/Write tool-path guard (AP-037 — “the protected asset is the deliverable, the guard has no legitimate-write affordance”):
intensity-calibration.json(#189):/cverifyis told to append a per-feature calibration row and even held aWrite(...calibration.json)grant, but SFG blocked the Edit/Write and the append silently no-op’d. The dataset feeding/cspecintensity recommendations,/cmetrics, and the dashboard was frozen while consumers read stale data and looked healthy (silent-telemetry-failure).pat001-measurement-due.json(#192/#226):/cdocs’s “Back-fill Deferred Meta Fields” step setcreated_at_commitvia Edit — also SFG-blocked. Worse, the old back-fill used ajq '.created_at_commit == null'test that also matches an absent key, and blanket-scanned all.correctless/meta/*.json, so a run for feature A polluted features B/C baselines with A’s merge-base (#226) and added spurious fields to files that never had one (#192).model-baselines.json:/cmodelupgradeheld aWrite(model-baselines.json)grant and wrote it directly — SFG blocked that too. A live, already-broken AP-037 instance of the same shape.
This feature adds a single general sanctioned sole-writer invoked via Bash (which SFG does not inspect post-sfg-edit-write-only), rewires the three skills onto it, and closes the AP-037 class structurally for .correctless/meta/*.json: a completeness test asserts every SFG-protected meta json maps to a registered sanctioned writer.
How It Works
graph LR
V["/cverify"] -->|"calibration-append<br/>(stdin JSON)"| W["bash meta-record.sh"]
D["/cdocs"] -->|"pat001-set-created-at <sha><br/>(present-null only)"| W
M["/cmodelupgrade"] -->|"baselines-write <model>|<ver><br/>(key-merge)"| W
W --> L["_acquire_state_lock<br/>(reused ABS-003 helpers)"]
L --> B{"tri-state body<br/>under the lock"}
B -->|"write applied"| S0["exit 0 + success line"]
B -->|"guarded no-op"| S1["exit 0 + 'no change: ...'<br/>(no bytes rewritten)"]
B -->|"rejected / failed"| S2["non-zero +<br/>'meta-record: FAILED <file>: ...'"]
W -.->|"hardcoded dest<br/>per op (PRH-005)"| C["intensity-calibration.json"]
W -.-> P["pat001-measurement-due.json"]
W -.-> BL["model-baselines.json"]
style W fill:#74c0fc,color:#000
style S0 fill:#51cf66,color:#000
style S1 fill:#ffd43b,color:#000
style S2 fill:#ff6b6b,color:#fff
One writer, three registered operations, each with a hardcoded destination (PRH-005 — the destination is never derived from input):
calibration-append— append one object tointensity-calibration.json’scalibration_entries[]from stdin. Deep-equal-preserving over prior entries (INV-001); schema-validated with a permissive unknown-field policy so forward-compatible producer growth never causes silent data loss (INV-002).pat001-set-created-at <sha>— setcreated_at_commitonpat001-measurement-due.jsononly when the field is present and literallynull(has("created_at_commit") and .created_at_commit == null). Absent or already-non-null is an intended no-op; any other file is never touched (fixes #192/#226).baselines-write <model>|<version>— key-merge one baseline intomodel-baselines.json, preserving all sibling keys and top-levelschema_version, failing loud on a schema mismatch rather than clobbering (EXT-002). Never a whole-file overwrite.
Tri-state exit contract (the mechanical seam that makes fail-loud provable, not prose):
| Exit | Signal | Meaning |
|---|---|---|
0 + success line | write applied | the mutation landed and the file is valid JSON afterward |
0 + no change: <reason> | intended no-op | a guarded outcome — no file bytes rewritten |
non-zero + meta-record: FAILED <file>: <reason> | rejected / failed | invalid input, or an attempted write that could not complete |
The meta-record: FAILED stdout token is echoed verbatim by the calling skill so a failure is surfaced to the user instead of silently swallowed — the fix for the silent-telemetry-failure class at the root of #189.
Design Notes
- Reuses locking, hand-rolls only the body (PRH-006 / DD-008): the writer calls
_acquire_state_lock/_release_state_lockfromlib.sh(ABS-003) directly. It does not call the two-statelocked_update_file(it cannot express the three-state exit contract and would deadlock if wrapped in a pre-lock). All read, validation, decision, and the atomic$dest.$$.tmp+mv-after-validate happen inside one critical section. - Lock re-gated to
flock(with an ln/mkdir fallback) (INV-007): mutual exclusion now uses kernelflockon a persistent${sf}.flockfd when available (the default on Linux/CI) — race-free and auto-released on process death — falling back to an atomiclnhard-link dir-lock whereflockis absent (macOS;CORRECTLESS_LOCK_IMPL=lnforces it). The journey was mkdir → O_EXCL → ln → flock:mkdirwas non-atomic on the sandbox overlay, the O_EXCL grace-loop raced on CI, and even thelncreate-with-content still flaked once under the jq-1.8 runner’s adversarial scheduling — file-creation locks with PID stale-detection can’t guarantee exclusion under extreme scheduling, so only kernel flock is truly race-free. QA-002 stress-tests the flock path; the deterministic R-tests cover the ln fallback. - Bounded, symlink-refusing input (INV-010): stdin is byte-capped at 64 KB counted with
wc -c(never${#var}), passed via stdin/temp-file never argv (ARG_MAX / AP-039). The destination and its nearest existing parents are checked for symlinks via a fail-closedrealpath/readlink -fprobe (_realpath_tool_available, PAT-020) before any mkdir/temp and re-checked beforemv— never the lexicalcanonicalize_path. - Class closure (INV-006):
scripts/sanctioned-meta-writers.tsv(a CI/test-only registry the writer does not runtime-read — DD-007) maps every SFG-protected meta json to its(writer, operation). A structural test extracts the DEFAULTS meta set with an anchored^\.correctless/meta/[^/]+\.json$regex (rejecting adversarial siblings likecredentials.json) and asserts every entry has a registry row. All five DEFAULTS meta files are script-backed → zero exemptions → the class is fully closed.
Honesty Note (Mechanism Capability)
SFG is a cooperative-loop guardrail, not a security perimeter (ABS-045, AP-040, PMB-020). It blocks the naive agent Edit/Write to a protected path; it does not and cannot inspect or stop a motivated Bash write. “Sole writer” therefore means the sanctioned/expected write path in the cooperative agent loop — enforced against agent Edit/Write by SFG and against wrong content by the writer’s validation plus the append-only / key-merge tests. Out-of-band Bash writes to the meta files or the writer script are an accepted non-goal (AP-040). The symlink/realpath guards are writer robustness inside that boundary, not a perimeter. No invariant claims a strength the cooperative-loop layer cannot deliver.
AP-037 Recursion
The writer is itself SFG-protected (INV-005) — so meta-record.sh is a deliverable that its own guard would block during development. The AP-037 lift-and-restore affordance (.claude/rules/sfg-deliverable.md, ABS-041) is the escape hatch: lift the protection with a tracked sentinel while iterating, restore it before push. The feature that closes the AP-037 class for meta artifacts had to use the AP-037 affordance to build its own deliverable.
Known Limitations
- Retroactive repair of existing #226/#192 pollution is out of scope.
scripts/meta-pollution-detect.sh(advisory, always exits 0, surfaced in/cstatus) detects and flags meta files whosecreated_at_commitdiverges from their own feature’s merge-base; a repair sweep is a follow-up. - Scope is
.correctless/meta/*.jsononly — the class closure does not generalize to non-meta SFG-protected artifacts (scripts, agents, config).