CC Verifier Sidecar and Ready Gate — Observe Rollout (MC #105472)
CC Verifier Sidecar and Ready Gate — Observe Pilot and Identity BindingRollout (MC #105472)
PurposeCurrent state
The persistent CC verifier sidecar and exact-revision MC ready gate are installed in observe-only mode. Enforcement remains disabled; the pilot allowlist is empty and the enforcement watermark is unset. This page recordsdoes thenot independentlyauthorize reviewedpilot-enforce WP2or design and correctly attributed live Claude Code Stop-hook pilot. The verifier remains an independent, read-only trust domain. It binds verdicts to an exact MC task and immutable repository revision, rejects ambiguous identity, never executes builder-provided commands, and currently runs in observe mode with enforcement disabled.enforce-new.
ArchitectureAccepted implementation
WP1pi-orchestrator.jsinjectsverifiernumericcore:MC_TASK_ID044c0d30e7d7e973009991d91e42b8c2f77371ceinto each spawned Claude CLI environment and strips stale inherited values.The Stop hook accepts agreeing explicit hook/env identity or an exact session-owned marker; unsafe global-derived PID fallback is deprecated.The hook emitscc.verification_requestedthrough the durable event bus.The existingcom.john.event-dispatcherinvokes the independentcc-verifier.jshandler. No second daemon exists.Verdict evidence is stored under the task and exact revision in the system evidence tree.Rollout remainsobserve;enforcementremainsdisabled. WP3 gate activation requires separate implementation, exact-SHA review, and rollout approval.
Live pilot evidence
CC Verifier WP2 — Correctly Attributed Live Stop-Hook Pilot
Date: 2026-07-13
Parent MC: #105472
WP2 MC: #105479
Live pilot task: #105501
Superseded non-Claude probe: #105500 (local eval/Ollama; no Stop event; excluded)
Verdict
PASS for WP2 live-pilot identity/handoff DoD. A real orchestrator-spawned Claude Code session inherited the explicit task identity MC_TASK_ID=105501; its installed global Stop hook emitted one post-watermark event for MC #105501 at exact revision ad28aa306350b44d568aa9c75f4502ec04634bfb. The durable dispatcher retried one transient git timeout, completed the event, and persisted a PASS verdict. A deliberate replay reused the same event id and did not create a second event.
This report does not enable WP3 enforcement and does not claim parent MC #105472 complete.
Reviewed remediation integrated before pilot
Exact remediation commit:remediation:ad28aa306350b44d568aa9c75f4502ec04634bfbIndependentWP3Proveosystemverdict:source:PASS2cee8e17b1cbea58ba9320015e9c8cca793cb56dProveoHookreport:candidate:c0871b8689da0063bd2d6cca26322f702a55c0d1- Live system commit containing accepted WP3 bytes:
18e8ef5d257e34ebfdcb91ff806e7a377010a874 - Live hook commit:
6c52ef129dd68008551e355ef88c97b94fe2202b
The live commits do not share normal ancestry with the isolated review branches, so acceptance is based on direct byte equivalence of candidate-owned files, not inferred ancestry.
Independent evidence
- WP1 review:
/Users/makinja/system/evidence/105474/proveo-independent-review.md - WP2 review:
/Users/makinja/system/evidence/105479/proveo-review-identity-remediation/REPORT.md Proveo report SHA-256:ddfed7f124480220b260933891be9189a02088c42b58a5261efcb1d4693ed2d7Canonical syntax/test rerun: verifier suites 36/36 PASS; orchestrator identity suite 4/4 PASS.
Real live-session identity evidence
Orchestrator LaunchAgent was reloaded after canonical integration, PID20592for this pilot.Real Claude CLI process: PID41787, child of orchestrator PID20592.Runtime process-environment probe while Claude wasCorrect livereturned:MC_TASK_ID_PRESENT=yesMC_TASK_ID_MATCH_105501=yes
Claude session ID:0bc54f92-2335-4e8c-b2fa-f1e48d7cfda9Session transcript:/Users/makinja/.claude/projects/-Users-makinja-system--claude-worktrees-codecraft-105501/0bc54f92-2335-4e8c-b2fa-f1e48d7cfda9.jsonlTranscript SHA-256:718723ac8926f11ac2c3dd2f64c744e2b2d424794753e948a9cc7c53aebe7a52Session worktree:/Users/makinja/system/.claude/worktrees/codecraft-105501Session worktree HEAD:ad28aa306350b44d568aa9c75f4502ec04634bfbSession worktree status after completion: clean.Session final report explicitly identified MC #105501, observe mode, disabled enforcement, exact HEAD, syntax PASS, identity tests 4/4, and no edits.
Durable event and telemetry evidence
Event ID:103785Event type:cc.verification_requestedAggregate/task:105501Publisher:cc-verifier-stop-hookCreated:2026-07-13 12:00:11Processed:2026-07-13 12:01:18Status:completedRetry count:1Idempotency key:cc-verify:105501:ad28aa306350b44d568aa9c75f4502ec04634bfbPayload repository:/Users/makinja/system/.claude/worktrees/codecraft-105501Payload revision:ad28aa306350b44d568aa9c75f4502ec04634bfb- Stop-hook
log source:task_source:"env-mc-task-id" Stop-hook telemetry outcome:emit_ok, task105501, event103785, exact remediation revision.First handler attempt recordedspawnSync git ETIMEDOUT; the durable retry succeeded and persisted PASS. The completed row retains the priorerror_message, so completion is established by status, retry count, final telemetry, and persisted verdict rather than by treating that field as empty.Final handler telemetry:outcome:"verdict_persisted",verdict:"PASS",task_id:"105501",revision:"ad28aa...",check_count:5,reused:false.
Persisted verdict
Path:pilot:/Users/makinja/system/evidence/105479/live-pilot-105501/REPORT.md- Final WP3 exact-SHA review:
/Users/makinja/system/evidence/105472/proveo-wp3-system-2cee8e17/REPORT.md - Updated post-install acceptance:
/Users/makinja/system/evidence/105472/WP3-HOOK-ACCEPTANCE-UPDATE.md - Independent post-install review:
/Users/makinja/system/evidence/105472/proveo-wp3-postinstall/REPORT.md - Trusted exact-revision verdict:
/Users/makinja/system/evidence/105505/cc-verifier/ad28aa306350b44d568aa9c75f4502ec04634bfb/2cee8e17b1cbea58ba9320015e9c8cca793cb56d/verdict.json
Final post-install validation passed 82/82 focused tests, 14/14 adjacent Stop-hook tests, and the shell integration smoke. The ready hook blocks only intentional verifier exit code 2 and fails open to the existing legacy gates on infrastructure errors.
Identity and trust boundaries
SHA-256:Task identity must come from agreeing explicit trusted input, orchestrator-injected numeric, or demonstrably session-owned metadata.259a800b021cce4cad26d35f5fede71f3460912da41cb0d31e04527b0a4e5a01MC_TASK_IDVerdict:Unsafe,PASSconflicting, ambiguous, stale/global-marker-only, non-positive, or larger-than-Number.MAX_SAFE_INTEGERIDs are rejected before MC database or revision resolution.BoundVerdictstask:are105501bound Sourcetoevent:an103785exact Boundimmutablerevision:revision.ad28aa306350b44d568aa9c75f4502ec04634bfbAny Verifierlateridentity:commitcc-verifier-2e313ff0-c936-4fd5-9dfb-1f7817d32630Checks: checkout integrity plus four Node syntax checks, allinvalidates PASS.CombinedVerificationcheckcommands and evidenceSHA-256:rootscome from trusted policy; builder-provided commands and arbitrary evidence paths are never executed or trusted.bd44841bf28e57aa5cad801a50f8cd7001b65d5b7680c408ba47a536c769c95a- Durable SQLite attestation detects accidental drift and one-artifact tampering, but is not a cryptographic boundary against a same-OS-user privileged process.
IdempotencyPromotion proofboundary
Promotion to an enforcing mode requires a separate explicit approval, a healthy attestation store, fresh attested PASS at the candidate revision, and review of observe telemetry. Historical ready_for_review backlog must never be enrolled. Rollback is configuration-only: set gate_rollout_mode to observe to remove blocking while preserving audit telemetry, or off to disable gate decisions.
title: CC Verifier Ready-Gate — Operator Runbook mc_task: 105505 parent_mc_task: 105472 status: observe (audit-only, no enforcement) project_path: /Users/makinja/system
CC Verifier Ready-Gate — Operator Runbook
Covers tools/cc-verifier-gate.ts (WP3 of MC #105472): the exact-revision check
that runs before mc.js ready. This is the operator guide for the rollout
lifecycle — activation, telemetry, invalidation, promotion, rollback, and
fail-open behavior. It does not restate the WP3 design (see the header
comment in tools/cc-verifier-gate.ts and specs/cc-verifier-sidecar-105472-plan.md).
1. Observe activation
The samegate trustedships hook envelope was replayed oncelive with :MC_TASK_ID=105501config/cc-verifier-pilot.json
"gate_rollout_mode": "observe",
"gate_pilot_enforce_task_ids": [],
"gate_enforce_new_watermark_epoch_ms": null
In observe mode (decide() in tools/cc-verifier-gate.ts:363-365), every
failing finding is recorded to the audit log but the decision is always
{ action: 'audit', enforced: false } — it can never block mc.js ready.
observe is the only mode this pilot should run in until a human explicitly
promotes it (Section 4). No code change is required to activate observe —
it is the config default and the live-safe fallback if the config file is
missing or unparseable (loadGateConfig() falls back to
DEFAULT_GATE_CONFIG, which is also observe/empty/null).
To confirm the live config is still in the safe state:
node -e "const c=require('./config/cc-verifier-pilot.json'); console.log(c.gate_rollout_mode, c.gate_pilot_enforce_task_ids, c.gate_enforce_new_watermark_epoch_ms)"
# expected: observe [] null
2. Audit telemetry
Every checkReadiness() call appends one JSON line to the audit log
(~/system/state/cc-verifier-gate-audit.jsonl in production, overridable via
CC_VERIFIER_GATE_AUDIT_LOG for tests), regardless of mode or outcome:
{"ts":"...","component":"cc-verifier-gate","task_id":"...","dry_run":false,"finding":{...},"decision":{...}}
finding.reason carries the machine-readable failure code (e.g.
revision_mismatch, digest_mismatch, verdict_field_tampered,
stale_revision, no_verdict_found) so audit volume can be triaged by
failure class. Audit writes are best-effort and wrapped in try/catch
(appendAudit()) — a logging failure never changes the gate decision.
To review recent audit activity:
tail -n 50 ~/system/state/cc-verifier-gate-audit.jsonl | node -e "process.stdin.on('data',d=>d.toString().trim().split('\n').forEach(l=>{const e=JSON.parse(l);console.log(e.task_id, e.decision.mode, e.decision.action, e.finding.reason)}))"
Before promoting past observe, review the audit log for the candidate
task id(s) and confirm the only findings are expected ones (e.g. a
deliberately stale revision), not systemic issues like schema drift or a
misconfigured MC_DB_PATH.
3. Exact-revision invalidation
A verdict is only valid for the exact revision it was computed against.
evaluate() resolves the trusted current revision from --repo/--commit,
explicit --repo + live git rev-parse HEAD, or (default) the MC task's
tasks.project_path + live HEAD — never from the verdict itself
(resolveTrustedRevision(), tools/cc-verifier-gate.ts:162-206). If the
stored verdict's revision does not match the current HEAD, the finding is
revision_mismatch and ok:false.
If no verdict exists at the current revision but one exists at a prior
revision, the reason is stale_revision (as opposed to no_verdict_found,
which means the task was never verified at all) — this is the expected
signal after any new commit lands on a previously-passing task; a fresh
cc-verifier run is required before the gate can pass again at the new
revision.
Every evidence file hash and the top-level verdict field are also
independently recomputed from the checks array and re-read evidence
bytes — a hand-edited verdict.json (e.g. flipping PARTIAL to PASS,
or editing a stdout/stderr file after the fact) is caught as
verdict_field_tampered or digest_mismatch even when the stored
revision field is untouched.
4. Promotion prerequisites (pilot-enforce / enforce-new)
Both enforcing modes are opt-in per config field and require a human edit
to config/cc-verifier-pilot.json — there is no code path that
auto-promotes.
pilot-enforce — blocks only task ids explicitly listed in
gate_pilot_enforce_task_ids. Before adding a task id:
- Confirm the task has at least one
verdict_status: "PASS"audit entry at its current revision (Section 2). - Confirm no unexplained
digest_mismatch/verdict_field_tamperedentries exist for that task in the audit log. - Add the task id as a string to
gate_pilot_enforce_task_ids(e.g.["105505"]). Tasks not in the list continue to audit-only (decide()line 371:not in pilot allowlistfalls through toaction: 'audit').
enforce-new — blocks only tasks whose MC created_at is strictly
after gate_enforce_new_watermark_epoch_ms. Before setting the watermark:
- Run the pilot in
pilot-enforcefor a representative set of tasks first and confirm no false-positive blocks in the audit log. - Set
gate_enforce_new_watermark_epoch_msto an epoch-ms value at or after the promotion decision time — this must be a second, independent watermark from the WP2 event-ingestionactivation_watermark_epoch_ms; do not reuse or overwrite that field. - Any task created at or before the watermark, or when the watermark is
null/non-finite, is never eligible for blocking (decide()lines 374-381) — this is the live-safe default and must remain the fallback for any config read error.
Both modes still only ever block when finding.ok === false AND the
task is enrolled/eligible; a passing finding always resolves to
action: 'allow' regardless of mode (decide() line 359-361).
5. Rollback to off
To disable the gate entirely (including audit-only behavior), set:
"gate_rollout_mode": "off"
decide() line 355-357 short-circuits before evaluating the finding:
off always returns { action: 'allow', enforced: false }. Note
evaluate() still runs and is still written to the audit log by
checkReadiness() — off only changes the decision, not whether the
check executes or is recorded. To roll back from pilot-enforce or
enforce-new without fully disabling audit visibility, set
gate_rollout_mode back to observe instead — this preserves telemetry
while immediately removing all blocking.
Rollback is a config-only change; no deploy, restart, or code change is
required, since loadGateConfig() reads the file fresh on every
checkReadiness() call.
6. Fail-open incident handling
The gate is fail-open by design at every layer that is not the explicit enforcing-mode decision:
- Missing/unparseable config file —
loadGateConfig()catches the read/parse error and returnsDEFAULT_GATE_CONFIG(observe, empty allowlist, null watermark). - Unknown
gate_rollout_modevalue —loadGateConfig()ignores it (keeps the defaultobserve); if an unrecognized mode value somehow reachesdecide()anyway, the final fallback branch returns{ action: 'audit', enforced: false, reason: 'unknown_mode_fail_open_audit' }. - MC db missing/locked —
queryTaskRow()catches the error and returnsnull;resolveTrustedRevision()then returnsno_trusted_project_path, which is a normalok:falsefinding (still subject to the mode's decision logic — inobservethis only audits). - Audit log write failure —
appendAudit()swallows the error; it never affectsfinding/decision. - No commit/task/repo resolvable —
evaluate()returnsinvalid_task_idorrevision_undeterminable; these are ordinary findings, not exceptions, and flow through the same mode-gateddecide()path as any other failure.
If an incident is suspected (e.g. unexpected blocks reported by a builder), the immediate mitigation is:
node -e "const fs=require('fs');const p='config/cc-verifier-pilot.json';const c=JSON.parse(fs.readFileSync(p));c.gate_rollout_mode='observe';fs.writeFileSync(p, JSON.stringify(c,null,2))"
This reverts to audit-only without needing to identify root cause first. Then inspect the audit log (Section 2) for the affected task id(s) to determine whether the block was a correct enforcement (real tamper/stale revision) or a gate defect, before re-promoting.
7. Durable attestation store health
tools/cc-verifier.js (F1) writes one row per (task_id, revision) to a
SQLite store at ~/system/databases/cc-verifier-attestations.db
(overridable via CC_VERIFIER_ATTESTATION_DB for tests), recording the
SHA-256 of the exact verdict.json bytes it just persisted, the
verifier_identity, and written_at. This happens inside
writeAtomicJSON() immediately after the verdict file is written, so a
verdict and its attestation are never observed out of order by a reader.
The gate (tools/cc-verifier-gate.ts) reads this store read-only
(cc.getAttestation()) and requires all three to hold before treating a
verdict as genuine:
- an attestation row exists for
(task_id, revision)— otherwiseattestation_missing; - its
verdict_sha256matches the SHA-256 of the on-diskverdict.jsonbytes — otherwiseattestation_mismatch; - its
verifier_identitymatches the verdict's ownverifier_identityfield — otherwiseattestation_identity_mismatch.
cc-verifier.js's own idempotency cache (readAttestedVerdict()) applies
the same first two checks before it will reuse a cached verdict instead of
re-running checks — a verdict.json with no matching attestation is
treated as absent, not as a cache hit.
To check store health directly:
node -e "
const Database = require('better-sqlite3');
const db = new Database(process.env.CC_VERIFIER_ATTESTATION_DB
|| require('os').homedir() + '/system/databases/cc-verifier-attestations.db',
{ readonly: true, fileMustExist: true });
console.log(db.prepare('SELECT COUNT(*) AS n FROM attestations').get());
db.close();
"
If this fails (file missing, not a database, or table missing), the store
is unhealthy — see Section 7.2. writeAttestation() itself never throws
(all failures are caught and swallowed): a write failure does not crash
verify()/finalizeBlocked(), it only means that verdict will show as
attestation_missing to the gate until the store is repaired and a fresh
verify run is performed. This is deliberate fail-closed-for-the-gate,
fail-open-for-the-builder-pipeline behavior — verification work is never
blocked by attestation-store trouble, but the gate will not silently trust
an unattested verdict either.
7.1 Same-OS-user trust boundary
The attestation store raises the bar for forging a verdict; it does not
change the trust boundary. Both verdict.json and
cc-verifier-attestations.db are ordinary files writable by whichever OS
user runs cc-verifier.js/cc-verifier-gate.ts (no setuid, no separate
service account, no OS-level ACL beyond normal file permissions). Any
process running as that same user can, with better-sqlite3 and the
sameschema session/repository/revision.
- Section
Matching7,eventhand-craftcount before replay:an1attestationsMatchingroweventwithcount after replay:a1verdict_sha256ReusedthateventmatchesID:a hand-edited103785verdict.jsonHook—log:exactly as it could previously hand-editduplicate:trueverdict.json,task_source:"env-mc-task-id",alone.sameTheexactstore'srevision.value
Safetynarrower statethan aftera pilot
same-user - attacker:
GlobalitverifierconvertsStop-hookaregistrationone-filecount:edit1into Settingsa two-artifact forgery (matching JSONSHA-256:bytesd98f55c455843fc32e6855bbfc362dffebbd689a31a7349e059b965df237600fand Pilotamode:matchingobserveDB Enforcement:row),disabledwhich
Activationiswatermark:enough1783936171000to Nocatchnewaccidentaldaemon/LaunchAgent,drift,nopartialMC ready/done gate changes, no merge/deploy,edits, andnonon-adversarialautomatictoolingtaskbugs,completionbutoccurred.MC #105501 being paused by the generic no-file-change quality gateit isannotorchestrationaartifactdefense against a same-user, same- privilege adversary with full filesystem access. Cross-user tampering (a different OS user, or a user without write access to~/system/databases/) is out of scope for this store and isseparatenotfromclaimed to be mitigated by it.7.2 Integrity-mismatch recovery and reverification
attestation_missing,attestation_mismatch, andattestation_identity_mismatchare ordinaryok:falsefindings — they flow through thesuccessfulsameStop-hook handoff evidence.
Adjudication
The earlier misbound eventmode-gated path as 103707decide()revision_mismatch or
digest_mismatch (Section 3) and, in observe mode, only audit. They do
not indicate gate malfunction by themselves; they indicate the on-disk
verdict is not attested at the store's current state. Recovery is always
the same: re-run cc-verifier for unrelatedthe MCtask #105489at remainsthe quarantinedcurrent revision. There
is no repair path that edits or re-derives an attestation row in place —
getAttestation() never invents or fixes a row, and invalidthere is no supported
"resync" operation; a fresh, trusted verify pass is the only way to
re-establish attestation for acceptance.a Thegiven accepted(task_id, :liverevision)
node tools/cc-verifier.js replay --task <id> --revision <sha>
If mismatches appear across many unrelated tasks at once (rather than one task after a targeted hand-edit), treat it as a store-health incident, not a per-task tamper event:
- Confirm the store file itself is intact — re-run the health check in
Section 7 read-only
eventquery. A/103785SqliteErrorENOENTthere (as opposed to individual row mismatches) points to filesystem/disk trouble, a moved or deleted database file, or a botched migration, not tampering. - Confirm
CC_VERIFIER_ATTESTATION_DBis not accidentally pointed at a test/tmp path in the production environment (it is test-override-only; production should always resolve to the~/system/databases/cc-verifier-attestations.dbdefault). - Once the store itself is confirmed healthy, mismatches are per-task and
require the standard recovery above (fresh
cc-verifierrun) rather than any store-wide remediation.
7.3 Hard prerequisite for promotion
Sections 4's pilot-enforce/enforce-new promotion steps assume the
attestation store is healthy. This is a hard, non-negotiable prerequisite,
not an additional nice-to-have check: evaluate() treats
attestation_missing/attestation_mismatch/attestation_identity_mismatch
as ordinary ok:false findings, and in an enforcing mode ok:false for an
enrolled/eligible task blocks mc.js ready (Section 4, last paragraph).
If the attestation store is unhealthy (missing, corrupt, or unreachable)
at the moment pilot-enforce/enforce-new is enabled, every verdict —
including genuinely correct, untampered ones — will read as
attestation_missing and be blocked. Before adding any task id to
105501gate_pilot_enforce_task_ids/or exactsetting
gate_enforce_new_watermark_epoch_ms:
- Run the Section 7 health check and confirm it returns a row count without error.
- Confirm the candidate task's own audit log entries (Section 2) show
PASSoutcomes with noattestation_*reasons at the current revision — a healthy store but a stale/never-attested verdict for this specific task still means "re-runad28aa306350b44d568aa9c75f4502ec04634bfbcc-verifier.first," not "promote anyway." - If the store is unhealthy, fix it (Section 7.2) and obtain a fresh
attested
PASSbefore promoting — never promote to an enforcing mode "around" a known-unhealthy store on the theory that observe-mode telemetry looked fine; observe mode never blocks, so it cannot surface this failure mode the way an enforcing mode will.