Skip to main content

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

  • pi-orchestrator.jsWP1 injectsverifier numericcore: MC_TASK_ID044c0d30e7d7e973009991d91e42b8c2f77371ce into 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 emits cc.verification_requested through the durable event bus.
  • The existing com.john.event-dispatcher invokes the independent cc-verifier.js handler. No second daemon exists.
  • Verdict evidence is stored under the task and exact revision in the system evidence tree.
  • Rollout remains observe; enforcement remains disabled. 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: ad28aa306350b44d568aa9c75f4502ec04634bfb
  • IndependentWP3 Proveosystem verdict:source: PASS2cee8e17b1cbea58ba9320015e9c8cca793cb56d
  • ProveoHook report: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: ddfed7f124480220b260933891be9189a02088c42b58a5261efcb1d4693ed2d7
  • Canonical 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, PID 20592 for this pilot.
  • Real Claude CLI process: PID 41787, child of orchestrator PID 20592.
  • Runtime process-environment probe while Claude wasCorrect live returned:
    • MC_TASK_ID_PRESENT=yes
    • MC_TASK_ID_MATCH_105501=yes
  • Claude session ID: 0bc54f92-2335-4e8c-b2fa-f1e48d7cfda9
  • Session transcript: /Users/makinja/.claude/projects/-Users-makinja-system--claude-worktrees-codecraft-105501/0bc54f92-2335-4e8c-b2fa-f1e48d7cfda9.jsonl
  • Transcript SHA-256: 718723ac8926f11ac2c3dd2f64c744e2b2d424794753e948a9cc7c53aebe7a52
  • Session worktree: /Users/makinja/system/.claude/worktrees/codecraft-105501
  • Session worktree HEAD: ad28aa306350b44d568aa9c75f4502ec04634bfb
  • Session 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: 103785
  • Event type: cc.verification_requested
  • Aggregate/task: 105501
  • Publisher: cc-verifier-stop-hook
  • Created: 2026-07-13 12:00:11
  • Processed: 2026-07-13 12:01:18
  • Status: completed
  • Retry count: 1
  • Idempotency key: cc-verify:105501:ad28aa306350b44d568aa9c75f4502ec04634bfb
  • Payload repository: /Users/makinja/system/.claude/worktrees/codecraft-105501
  • Payload revision: ad28aa306350b44d568aa9c75f4502ec04634bfb
  • Stop-hook log source: task_source:"env-mc-task-id"
  • Stop-hook telemetry outcome: emit_ok, task 105501, event 103785, exact remediation revision.
  • First handler attempt recorded spawnSync git ETIMEDOUT; the durable retry succeeded and persisted PASS. The completed row retains the prior error_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 259a800b021cce4cad26d35f5fede71f3460912da41cb0d31e04527b0a4e5a01MC_TASK_ID, or demonstrably session-owned metadata.
  • Verdict:Unsafe, PASSconflicting, ambiguous, stale/global-marker-only, non-positive, or larger-than-Number.MAX_SAFE_INTEGER IDs are rejected before MC database or revision resolution.
  • BoundVerdicts task:are 105501
  • bound
  • Sourceto event:an 103785
  • exact
  • Boundimmutable revision:revision. ad28aa306350b44d568aa9c75f4502ec04634bfb
  • Any
  • Verifierlater identity:commit cc-verifier-2e313ff0-c936-4fd5-9dfb-1f7817d32630
  • Checks: checkout integrity plus four Node syntax checks, allinvalidates PASS.
  • CombinedVerification checkcommands and evidence SHA-256:roots bd44841bf28e57aa5cad801a50f8cd7001b65d5b7680c408ba47a536c769c95acome from trusted policy; builder-provided commands and arbitrary evidence paths are never executed or trusted.
  • 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:

  1. Confirm the task has at least one verdict_status: "PASS" audit entry at its current revision (Section 2).
  2. Confirm no unexplained digest_mismatch/verdict_field_tampered entries exist for that task in the audit log.
  3. 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 allowlist falls through to action: 'audit').

enforce-new — blocks only tasks whose MC created_at is strictly after gate_enforce_new_watermark_epoch_ms. Before setting the watermark:

  1. Run the pilot in pilot-enforce for a representative set of tasks first and confirm no false-positive blocks in the audit log.
  2. Set gate_enforce_new_watermark_epoch_ms to an epoch-ms value at or after the promotion decision time — this must be a second, independent watermark from the WP2 event-ingestion activation_watermark_epoch_ms; do not reuse or overwrite that field.
  3. 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 returns DEFAULT_GATE_CONFIG (observe, empty allowlist, null watermark).
  • Unknown gate_rollout_mode value — loadGateConfig() ignores it (keeps the default observe); if an unrecognized mode value somehow reaches decide() 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 returns null; resolveTrustedRevision() then returns no_trusted_project_path, which is a normal ok:false finding (still subject to the mode's decision logic — in observe this only audits).
  • Audit log write failure — appendAudit() swallows the error; it never affects finding/decision.
  • No commit/task/repo resolvable — evaluate() returns invalid_task_id or revision_undeterminable; these are ordinary findings, not exceptions, and flow through the same mode-gated decide() 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) — otherwise attestation_missing;
  • its verdict_sha256 matches the SHA-256 of the on-disk verdict.json bytes — otherwise attestation_mismatch;
  • its verifier_identity matches the verdict's own verifier_identity field — otherwise attestation_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.

in
    Section
  • Matching7, eventhand-craft count before replay:an 1attestations
  • Matchingrow eventwith count after replay:a 1verdict_sha256
  • Reusedthat eventmatches ID:a hand-edited 103785verdict.json
  • Hook log:exactly as it could previously hand-edit duplicate:trueverdict.json, task_source:"env-mc-task-id",alone. sameThe exactstore's revision.
  • value
is

Safetynarrower statethan aftera pilot

same-user
    attacker:
  • Globalit verifierconverts Stop-hooka registrationone-file count:edit 1
  • into
  • Settingsa two-artifact forgery (matching JSON SHA-256:bytes d98f55c455843fc32e6855bbfc362dffebbd689a31a7349e059b965df237600f
  • and
  • Pilota mode:matching observe
  • DB
  • Enforcement:row), disabled
  • which
  • Activationis watermark:enough 1783936171000
  • to
  • Nocatch newaccidental daemon/LaunchAgent,drift, nopartial MC ready/done gate changes, no merge/deploy,edits, and nonon-adversarial automatictooling taskbugs, completionbut occurred.
  • MC #105501 being paused by the generic no-file-change quality gateit is annot orchestrationa artifactdefense 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 is separatenot fromclaimed to be mitigated by it.

    7.2 Integrity-mismatch recovery and reverification

    attestation_missing, attestation_mismatch, and attestation_identity_mismatch are ordinary ok:false findings — they flow through the successfulsame Stop-hook handoff evidence.

Adjudication

The earlier misbound eventmode-gated 103707decide() path as 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):

pilot
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:

  1. Confirm the store file itself is intact — re-run the health check in Section 7 read-only eventquery. A 103785SqliteError/ENOENT there (as opposed to individual row mismatches) points to filesystem/disk trouble, a moved or deleted database file, or a botched migration, not tampering.
  2. Confirm CC_VERIFIER_ATTESTATION_DB is 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.db default).
  3. Once the store itself is confirmed healthy, mismatches are per-task and require the standard recovery above (fresh cc-verifier run) 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:

  1. Run the Section 7 health check and confirm it returns a row count without error.
  2. Confirm the candidate task's own audit log entries (Section 2) show PASS outcomes with no attestation_* reasons at the current revision — a healthy store but a stale/never-attested verdict for this specific task still means "re-run ad28aa306350b44d568aa9c75f4502ec04634bfbcc-verifier.

    first," not "promote anyway."
  3. If the store is unhealthy, fix it (Section 7.2) and obtain a fresh attested PASS before 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.