PHASE-ISR-INDEPENDENT-SECOND-REVIEWER.md markdown
866 lines 43.9 KB
Raw
sha256:6abcf1fa82a7a621ccbc945f19acdba5bc0db54569599404a1452fb4a096a199 fix(ISR): default require_independent_second_reviewer to require Human minor ⚠ breaking 5 hours ago

Phase ISR — Independent second reviewer (Thinking freeze)

Status: Reviewed → pass (ISR-r4). ISR-a is spec-only and now frozen; no code, no skill edit, and no honesty schema change land in this phase. ISR-b (Auto) is cleared to build mechanically against this contract.

phase: ISR
outputs:
- id: isr-independent-second-reviewer
  path: docs/archive/phases/PHASE-ISR-INDEPENDENT-SECOND-REVIEWER.md
  frozen: true
frozen_inputs:
- id: kit-spec
  path: docs/OVERSEER-KIT-SPEC.md
- id: k5-freeze-reviewer
  path: docs/archive/phases/PHASE-K5-FREEZE-REVIEWER-CONTRACT.md
- id: k9a-honesty
  path: docs/archive/phases/PHASE-K9A-L1-L2-MODULE-FREEZE.md
- id: p-evidence
  path: docs/archive/phases/PHASE-TRACK-P-P-EVIDENCE.md
- id: p-deploy
  path: docs/archive/phases/PHASE-TRACK-P-P-DEPLOY.md
- id: p-route
  path: docs/archive/phases/PHASE-TRACK-P-P-ROUTE-MODEL-ROUTING.md
- id: lt-loop-tightening
  path: docs/archive/phases/PHASE-LT-LOOP-TIGHTENING.md
- id: ons-operator-next
  path: docs/archive/phases/PHASE-ONS-OPERATOR-NEXT-SURFACING.md
- id: kh1-relay
  path: docs/archive/phases/PHASE-KH1-HANDOVER-RELAY-STANDARD.md
- id: check-ok
  path: docs/archive/phases/PHASE-CHECK-OK.md
- id: build-verification-skill
  path: cursor/skills/build-verification-review/SKILL.md
- id: build-verification-rule
  path: cursor/rules/build-verification-required.mdc
- id: honesty-types
  path: tools/honesty/types.py
- id: honesty-status
  path: tools/honesty/status.py
- id: honesty-validate
  path: tools/honesty/validate.py
- id: honesty-config
  path: adapters/config.py
- id: verification-evidence-gate
  path: tools/verification_evidence_gate/surface.py
- id: governance-hygiene-engine
  path: tools/governance_hygiene/engine.py
- id: status-exit
  path: cli/commands/status.py
- id: layered-honesty-vision-l0
  path: docs/archive/thinking/OVERSEER-KIT-LAYERED-HONESTY-VISION.md
- id: test-tiers
  path: policy/test-tiers.yaml
- id: model-labels
  path: policy/model-labels.yaml
- id: kit-boundary
  path: AGENTS.md
review_stamp:
  reviewed_at: '2026-09-02T12:24:04Z'
  verdict: pass
  reviewer_mode: agent
  reviewer_model: thinking-high
  reviewer_provider: local
  kit_version: 0.1.0
  artifact_digest: sha256:e6284150fc755facb5a008ce33eb425d55081e798096a0373d5e54715f5b3bba

Downstream edge: ISR-b treats this document as ground truth without re-deriving it (SPEC §6 mandatory reviewed freeze). The second chat / separate verifier runtime performs the review; the kit only records and optionally gates the verdict. The kit never dispatches, hosts, or calls a second model.

Review record (§6.2): every freeze-review finding MUST cite file+line. Uncited findings are invalid and are discarded. Fixes are Tier 1 on the feature branch. Merge to main is Tier 3 and is never part of this loop.

Round Reviewer Verdict Resolution
ISR-r1 Freeze-review loop (checklist + thinking, thinking-high) findings Semantic R1-M1–M2 + R1-N1. Fixed in-tree before CLI stamp.
ISR-r2 Freeze-review loop (checklist + thinking, thinking-high) findings R2-M1 governance-sync wiring named ReadFailure (coverage path) instead of the LT Mode B engine-footer path. R2-N1 validate helper “or equivalent”; R2-N2 live skill dests vs ok sync. Fixed in-tree.
ISR-r3 Freeze-review loop (CLI checklist + thinking, thinking-high) findings R3-C4 CLI blocked on a C4 false positive (gate-token equals-sign). Rephrased.
ISR-r4 Freeze-review loop (CLI checklist + thinking, thinking-high) pass R1–R3 confirmed RESOLVED. Boundary held (kit records/gates; no model dispatch). Mode D resolution + engine-footer wiring frozen. Seven-tier matrix complete. Stamp written by ok review --freeze.

Freeze-review findings ledger (ISR-r1)

ID Severity Category Citation Message
R1-M1 MAJOR consistency :575-586 (pre-fix) V9 required an ISR ledger line as part of BV, but §ISR.7.3 appended that line only after BV pass — Auto could not order the steps. Frozen: V1–V8 then append then DONE-unlock V9.
R1-M2 MAJOR completeness :648 (pre-fix) Exit constant lived in status.py or honesty_status.py. Frozen to tools/honesty/status.py only.
R1-N1 MINOR completeness :457 (pre-fix) Mode D --json on usage/38/4 and _usage_result extension were implied, not frozen. Now explicit.
R2-M1 MAJOR completeness :510 (pre-fix) Governance-sync ReadFailure("independent-second-reviewer") pointed Auto at reads.py (coverage). LT Mode B fails in engine.py after the footer. Frozen to engine-footer + status _exit_code_from_conditions.
R2-N1 MINOR completeness :247 (pre-fix) find_matching_* “or equivalent” weasel. Frozen to tools/honesty/validate.py.
R2-N2 MINOR consistency :596 (pre-fix) “Amend live .cursor/ / .claude/ copies” vs required ok sync --yes. Frozen: edit cursor/ source only.
R3-C4 BLOCKER security :541 (pre-fix) Checklist C4 matched a gate-token equals-sign as a secret assignment. Rephrased to “the gate token is …”.

Citation discipline: every review finding in this artifact must include path:line so the operator can verify — never trust uncited review output (§6.2 / K5).


§ISR.0 — Simple summary

After an Auto build, the same chat that wrote the code can run /build-verification-review, type DONE, and walk away. The build-verification rule already asks for an independent verifier and a thinking-high model — "if possible." That weasel is the hole. Loop tightening (LT) recorded the hole and explicitly deferred it: no second chat, no different session id.

Independent second reviewer freezes the missing gate: before an Auto slice may be claimed DONE, a second chat or separate verifier — not the agent that built — must record a passing verdict. The kit records and optionally gates that verdict on the honesty ledger. The kit does not run another model, does not open a new IDE chat, and does not call any reviewer API for this gate.

Technical summary: add ledger kind independent_second_review (additive to the K9a / P-evidence enum); require producer_session_idactor_session_id at append time; add honesty.require_independent_second_reviewer: off|warn|require (default off; HONESTY_KEYS + HonestyConfig field); add honesty-status Mode D (--independent-second-review PHASE_ID, shared optional --producer-session / --frozen-spec); add exit 38 + token missing_independent_second_review; add an LT-shaped active-slice surface on ok status / governance-sync; vendor a portable paste doc + twin skill; amend build-verification V9 + the always-on BV rule. Portable CLI + docs are complete without Cursor. No model dispatch. No ISR-b code in this Thinking phase.


§ISR.1 — Verified problem (do not redesign)

Fact Evidence
BV skill says independent verifier "if possible" — not a gate cursor/skills/build-verification-review/SKILL.md Model section
Always-on BV rule requires /build-verification-review before DONE; it does not require a different session cursor/rules/build-verification-required.mdc
LT explicitly deferred second chat / different agent_id docs/archive/phases/PHASE-LT-LOOP-TIGHTENING.md §LT.2 non-goal + §LT.5.3
K9a session inequality exists only for Mode A verdict hooks, and only when --producer-session is supplied PHASE-K9A §K9.8; tools/honesty/status.py _match_verdicts
Mode B (verification_evidence) matches last bv_verdict=pass — no producer/session compare P-evidence §PE.6.1; find_matching_verification_evidence
A producer may self-declare actor_role: verifier and append evidence P-evidence §PE.3 role rule is role-string only
Kit is rule-holder, never model executor P-route §PR.0; AGENTS.md; SPEC §6 reviewer is a separate freeze surface, not a dispatcher for DONE
Honesty Mode A/B/C + _resolve_mode already treat --producer-session as a Mode A partial tools/honesty/status.py _resolve_mode (P-deploy §PD.5.0)
Active-slice Mode B surface already exists (warn/require, no historical retro-fail) tools/verification_evidence_gate/surface.py §LT.5.2
Used honesty / CLI exits include 3337 P-evidence 33, P-deploy 34, workspace 35, PLS 36, ONS 37
Cursor cannot be required; ONS/LT: CLI + docs primary PHASE-ONS; PHASE-LT §LT.4.1 / §LT.7

§ISR.2 — Scope

In scope (ISR-a freezes; ISR-b implements):

  1. Ledger kind independent_second_review + append/validate rules (§ISR.3).
  2. Config honesty.require_independent_second_reviewer + HONESTY_KEYS / HonestyConfig wiring (§ISR.4).
  3. Honesty-status Mode D flags, mode-resolution amendment, JSON, error token (§ISR.5).
  4. Active-slice status / governance-sync surface (§ISR.6).
  5. Portable CLI + docs + twin skill + BV V9 / rule amendment (§ISR.7).
  6. Exit code 38 and reuse of existing codes (§ISR.8).
  7. Boundary + rejection table — governance, not runtime (§ISR.9).
  8. SPEC §5 additive row (§ISR.10).
  9. Seven-tier matrix (§ISR.11).
  10. Definition of Done (§ISR.12).

Out of scope (explicit non-goals):

Non-goal Why rejected now
Kit dispatches / hosts / calls a second model P-route + AGENTS.md boundary. Runtime / operator opens the second chat.
Automate Cursor "new chat" / tab open No honest host command. Print instructions. Same family as tab-reload reject.
Require ISR for every consumer Default off. Kit dogfood starts at warn (ISR-b writes; not this phase).
require as Auto v1 dogfood High friction. Promote later.
Replace build-verification or K5 freeze review ISR who certified DONE; BV what matches the spec; K5 before Auto starts.
Apply ISR to Thinking freeze-review DONE "Not the same agent that built." Thinking stays K5. ISR = Auto slices only.
New Mode A verdict hook or SHA co-requirement Different layer. Do not overload verdict.
Reuse / extend verification_evidence with session fields That kind is artifact hashes (V8). ISR is independence of the certifier.
Make --producer-session required on Mode D Lost token must not make the check unusable. Pin when supplied; entry still stores both ids.
Cryptographic proof of chat identity as git-only baseline P0 provenance remains optional / soft under git-only. Kit records the claim.
Session-type bookends (idle / Operator+Auto) Separate backlog row. ISR skips non-Auto active slices.
KH2 remask, tab reload, auto-enable hooks Unchanged backlog.
Wire ok review --freeze to Mode D Wrong gate: freeze is pre-Auto. Same as LT Mode B.
New top-level CLI verb (ok isr, ok isr-status) Check = Mode D on ok honesty-status; record = ok ledger append. No second engine.
Block historical DONE rows Active slice only (LT §LT.5.2 posture).
MuseHub-only identity / signature as the gate K7: baseline on git-only.
ISR-b Auto implementation in this Thinking phase SD-3 split.
Tier-3 merge, staging push, live posture flips Never authorized here.
Secrets, API keys, or model endpoints in config / ledger Names and opaque session strings only.

§ISR.3 — Ledger kind independent_second_review (frozen schema)

Additive amendment of the K9a / P-evidence entry-kind enum: add exactly one new value. Every prior kind's required fields and semantics stay byte-identical. Auto must extend ENTRY_KINDS in tools/honesty/types.py and the matching validate_append_body branches — no second ledger, no renumbering.

independent_second_review

Envelope (unchanged K9a / P0 rules): every non-genesis entry carries v: 1, kind, ts (server may fill), prev_hash / entry_hash (server fills), actor_role, actor_session_id, and optional provenance.

Role rule (frozen): actor_role MUST be verifier. Any other role → exit 23.

Independence invariant (frozen, enforced at append):

  1. producer_session_id is a required opaque non-empty string (the builder session / separate-producer nonce the operator supplied).
  2. actor_session_id is the second reviewer's session (envelope; required on every non-genesis kind).
  3. If actor_session_id == producer_session_id → exit 2 (malformed; not a role violation). The builder cannot honestly append their own ISR pass.
  4. The kit does not invent, scrape, or infer either id. The caller supplies both. The kit records the claim.

Kind-specific required fields (frozen):

Field Type Rule
phase_id non-empty string Opaque phase / slice id (same family as P-evidence).
frozen_spec non-empty string Opaque path-shaped string. Append checks non-empty string only — no must-exist, no path-confine of the stored string (same as §PE.3).
round integer ≥ 1 ISR review round. < 1 or non-int → exit 2.
isr_verdict string enum Closed vocabulary: pass | findings | blocked (lowercase). Any other value → exit 2.
producer_session_id non-empty string Builder / producer session nonce. Empty / missing / non-string → exit 2.

Optional fields (frozen):

Field Type Rule
producer_agent_id string Opaque. When both producer_agent_id and verifier_agent_id are present and equal → exit 2. One present, one absent → allowed.
verifier_agent_id string Opaque. Inequality rule above. Does not require P0 provenance.
bound_verification_evidence_hash string Optional entry_hash of a verification_evidence line. Auto v1 does not require or resolve this at match time (Mode B remains a separate gate). If present: non-empty string; no live ledger lookup at append.
notes string Advisory; never a substitute for independence or for BV.
provenance object Optional; identical to P0 rules.

Example (normative shape, illustrative ids):

{
  "v": 1,
  "kind": "independent_second_review",
  "ts": "2026-09-02T12:00:00Z",
  "actor_role": "verifier",
  "actor_session_id": "verifier-chat-2",
  "phase_id": "ISR-b",
  "frozen_spec": "docs/archive/phases/PHASE-ISR-INDEPENDENT-SECOND-REVIEWER.md",
  "round": 1,
  "isr_verdict": "pass",
  "producer_session_id": "builder-chat-1",
  "notes": "second chat re-ran build-verification V1-V9",
  "prev_hash": "…",
  "entry_hash": "…"
}

Genesis must not carry any of these kind-specific keys. Extend the existing genesis forbid-list in validate_append_body to include phase_id, frozen_spec, round, isr_verdict, producer_session_id, producer_agent_id, verifier_agent_id, bound_verification_evidence_hash — additive to keys already refused. (phase_id / frozen_spec / round may already be forbidden from P-evidence; do not remove them.)

CLI record surface (frozen): reuse ok ledger append --kind independent_second_review (--file / --stdin). No new top-level verb. ok ledger verify / show treat the new kind like any other (hash continuity). Kind authority: CLI --kind remains authoritative vs body kind.

Pure helper Auto MUST add:

find_matching_independent_second_review(entries, *, phase_id, frozen_spec, producer_session) -> entry | None

Implemented beside find_matching_verification_evidence / find_matching_deploy_health in tools/honesty/validate.py. Must not open a network connection, must not call a model, must not read session ids from the IDE.


§ISR.4 — Config: honesty.require_independent_second_reviewer (frozen)

Extend the existing honesty: block with one additive key (default preserves current behavior):

honesty:
  enabled: false
  require_independent_second_reviewer: off   # off | warn | require — default off
Value Meaning
off (default) Mode D may still be invoked; the match is computed for JSON but never fails the exit code (0 on the match dimension even when no entry exists). Repos that never set the key stay inert.
warn Mode D on miss: stderr warning (warning: no matching independent_second_review entry) + exit 0. Active-slice status / governance-sync: reminder only (never fail).
require Mode D on miss: exit 38. Active-slice status --exit-code / governance-sync: reuse exit 2 (no new status exit).

Parse rules (frozen):

  1. Value MUST be one of off|warn|require (string); anything else → config exit 2.
  2. Auto MUST add require_independent_second_reviewer to HONESTY_KEYS in adapters/config.py (unknown-key fail-closed). Omitting that frozenset update would reject a valid config as unknown honesty keys.
  3. Default when the key is absent: off.
  4. The flag is meaningless when honesty.enabled: false (module short-circuit exit 4 on ledger / honesty-status paths remains unchanged).
  5. Auto MUST add require_independent_second_reviewer: str = "off" to the HonestyConfig dataclass and wire the parsed value into load_config / the returned config object (same pattern as require_verification_evidence / require_deploy_health).
  6. Unknown keys under honesty: remain fail-closed (existing rule).

Regime: regime-agnostic git-only baseline. No Muse dependency. Optional provenance on ISR entries follows P0 soft/hard rules independently.

Kit dogfood (ISR-b writes; not this phase): this repo's .overseer/config.yaml sets require_independent_second_reviewer: warn. Do not set require. Do not enable the key in ok init defaults or templates. Consumers stay off until they opt in.

Operator amend 2026-09-02 — default require (closed loop)

Authority: operator Tier-3 posture flip (Aaron). Premise: the independent second pass is the closed loop; default opt-out was portability, not optionality of the gate.

Was (ISR-r4) Now
Absent key → off Absent key → require
HonestyConfig default off default require
Kit dogfood warn Kit dogfood require
Tip when off: enable warn|require Tip when off: you opted out; set require to re-enable

Opt out per repo: honesty.require_independent_second_reviewer: off (or warn). Rejection row “Default require for all consumers” is superseded by this amend for shipped defaults; frozen Mode D / ledger / exit 38 mechanics are unchanged.


§ISR.5 — honesty-status Mode D (frozen)

§ISR.5.1 — Flags

Exact CLI flag names are frozen (no aliases in this slice):

Mode Required flags Forbidden together with
A — verdict co-requirement (existing) --hook HOOK + --artifact PATH --verification-evidence, --frozen-spec, --deploy-health, --independent-second-review
B — verification evidence (existing) --verification-evidence PHASE_ID --hook, --artifact, --producer-session, --deploy-health, --independent-second-review
C — deploy health (existing) --deploy-health PHASE_ID --hook, --artifact, --producer-session, --verification-evidence, --independent-second-review
D — independent second review (new) --independent-second-review PHASE_ID --hook, --artifact, --verification-evidence, --deploy-health

Shared optional flags:

  • --frozen-spec PATH_STRING — allowed with Mode B, C, or D (not more than one of those modes in one invocation). Opaque string; not a filesystem must-exist check. Forbidden with Mode A.
  • --producer-session ID — allowed with Mode A (existing optional pin) or Mode D (optional pin). Forbidden with Mode B and Mode C (unchanged).

Auto wiring (frozen — required for Mode D to be reachable):

  1. HonestyStatusOptions gains additive field independent_second_review: str | None = None.
  2. cli/main.py registers argparse --independent-second-review PHASE_ID on the honesty-status parser (metavar PHASE_ID).
  3. cli/commands/honesty_status.py passes independent_second_review=getattr(args, "independent_second_review", None) into HonestyStatusOptions and maps exit 38 → stderr text missing independent second review.
  4. HonestyStatusJson gains additive optional field independent_second_review: dict | None = None emitted only on Mode D responses (Mode A/B/C omit the key).

§ISR.5.2 — Mode-resolution algorithm (frozen; amends §PD.5.0)

Today _resolve_mode treats --producer-session as a Mode A partial and rejects it with Mode B/C / --frozen-spec. Auto must replace that logic with the following (normative). Pseudocode:

mode_a_core    = hook or artifact
mode_a_full    = hook and artifact
mode_b_full    = verification_evidence is set
mode_c_full    = deploy_health is set
mode_d_full    = independent_second_review is set
frozen         = frozen_spec is set
producer       = producer_session is set

# --producer-session is SHARED metadata for Mode A (optional) and Mode D (optional).
# It does NOT by itself imply Mode A when Mode D is selected.
mode_a_partial = mode_a_core or (producer and not mode_d_full)

if (mode_b_full + mode_c_full + mode_d_full) > 1:
    return None
if mode_a_partial and (mode_b_full or mode_c_full or mode_d_full or frozen):
    return None
if frozen and not mode_b_full and not mode_c_full and not mode_d_full:
    return None
if not mode_a_full and not mode_b_full and not mode_c_full and not mode_d_full:
    return None
if mode_d_full:
    return "mode_d"
if mode_c_full:
    return "mode_c"
if mode_b_full:
    return "mode_b"
return "mode_a"

Invariants (frozen):

  1. --independent-second-review PHASE --frozen-spec PATHmode_d.
  2. --independent-second-review PHASE --producer-session IDmode_d.
  3. --independent-second-review PHASE --producer-session ID --frozen-spec PATHmode_d.
  4. --independent-second-review PHASE alone → mode_d.
  5. --producer-session ID alone → usage 1 (unchanged).
  6. --verification-evidence PHASE --producer-session ID → usage 1 (unchanged).
  7. --deploy-health PHASE --producer-session ID → usage 1 (unchanged).
  8. --hook H --artifact P --independent-second-review PHASE → usage 1.
  9. When Mode D flags are absent, Mode A/B/C behavior is byte-identical to pre-ISR for every invocation that does not pass --independent-second-review.

Usage exit 1 when the algorithm returns None.

Mode D applies require_independent_second_reviewer per §ISR.4 (off → always 0 for the match dimension and still emits the JSON block; warn → stderr warning + 0 on miss; require38 on miss). Module disabled → existing 4 short-circuit before mode logic.

§ISR.5.3 — Mode D match rule (frozen)

A matching independent_second_review entry for inputs (phase_id, frozen_spec?, producer_session?) is an entry where all of:

  1. kind == independent_second_review
  2. actor_role == verifier
  3. isr_verdict == pass
  4. phase_id equals the requested phase id (exact string match)
  5. If the caller supplies frozen_spec, it equals the entry's frozen_spec; if omitted, any frozen_spec matches
  6. actor_session_id != producer_session_id (entry-internal; already append-enforced)
  7. If the caller supplies producer_session, it equals the entry's producer_session_id and actor_session_id != that same id; if omitted, rule 7 is skipped (last ISR pass for the phase still must satisfy 1–6)

Scan order: ledger file order; last matching entry wins.

findings / blocked entries never satisfy the gate. A later pass is a separate append (new round).

§ISR.5.4 — Mode D JSON + error token (frozen)

Extend HonestyErrorToken with exactly one additive value:

missing_independent_second_review

Used when Mode D exits 38. Do not overload missing_verdict, missing_verification_evidence, or missing_deploy_health.

Mode D JSON (nested key present only on Mode D responses):

{
  "ok": false,
  "exit_code": 38,
  "command": "honesty-status",
  "hook": null,
  "artifact": null,
  "artifact_sha256": null,
  "producer_session": "builder-chat-1",
  "matched_verdict_hash": null,
  "error": "missing_independent_second_review",
  "independent_second_review": {
    "phase_id": "ISR-b",
    "frozen_spec": null,
    "producer_session": "builder-chat-1",
    "require": "require",
    "matched_entry_hash": null
  }
}
Nested field Rule
phase_id Echo of --independent-second-review
frozen_spec Echo of --frozen-spec or null
producer_session Echo of --producer-session or null
require Effective config value off|warn|require
matched_entry_hash entry_hash of the last matching pass entry, or null

Top-level producer_session continues to echo --producer-session or null (existing Mode A field; Mode D reuses it).

On Mode D success (0): ok: true, error: null, matched_entry_hash set when a match exists; under off with no match, ok: true, matched_entry_hash: null, error: null. On warn miss: ok: true, error: null, matched_entry_hash: null, plus stderr warning.

Mode A/B/C responses must not emit independent_second_review. Mode D responses must not emit verification_evidence or deploy_health.

--json emits this object on every Mode D exit, including 1 / 4 / 38 / 0 (existing honesty-status always-emit pattern). Usage (1) that included --independent-second-review still echoes phase_id in the nested block when the phase argument was present; matched_entry_hash is null.

Auto MUST extend _usage_result in tools/honesty/status.py so it can carry independent_second_review= the same way it already carries verification_evidence= / deploy_health=.


§ISR.6 — Active-slice status / governance-sync surface (frozen)

Reuse the LT §LT.5.2 skip / claim posture. New helper module tools/independent_second_reviewer/ (IndependentSecondReviewerGateReport / build_independent_second_reviewer_gate) parallel to tools/verification_evidence_gate/.

When honesty.enabled is true and require_independent_second_reviewer is warn or require:

  1. Resolve the KH1.9 active slice (same scan as scan_governance_gates).
  2. If there is no active slice, or the active Model is not Auto and not the Auto half of a split ({step}b), skip.
  3. If the active Auto slice status is TODO or WIP and the handover does not claim BV pass / DONE, skip.
  4. If the handover or roadmap claims that active Auto slice is DONE or BV pass, run §ISR.5.3 with:
    • phase_id = the active phase id string from KH1.9
    • --frozen-spec: if the active ROADMAP deliverable cell contains exactly one path matching docs/archive/phases/PHASE-*.md (or consumer equivalent under docs/), pass that path; if zero or more than one, omit
    • producer_session omitted (status cannot know the builder chat)
Mode Missing match Status --exit-code governance-sync
off n/a unchanged unchanged
warn reminder line + JSON independent_second_reviewer_gate: {ok: true, mode: warn, matched: false} 0 (never fail) footer reminder only
require same JSON with ok: false + token missing_independent_second_review 2 engine footer + return 2 (not reads.py ReadFailure)

Do not invent a new ok status exit code. Token stays missing_independent_second_review.

Auto wiring (frozen — parallel to LT Mode B, not to footprint-coverage):

  1. tools/independent_second_reviewer/__init__.py exports IndependentSecondReviewerGateReport, build_independent_second_reviewer_gate, format_independent_second_reviewer_gate_line, independent_second_reviewer_gate_payload.
  2. cli/commands/status.py calls the builder (same handover/roadmap texts as Mode B), prints the human line, attaches JSON when the probe did not skip, and folds independent_second_reviewer_gate.ok into _exit_code_from_conditions as a new independent_second_reviewer_gate_ok argument (default True) on the existing 2 tier. Do not renumber 2 > 6 > 35 > 3 > 0.
  3. tools/governance_hygiene/engine.py calls the same builder beside the existing verification_gate block: emit the human line; when ok is false, mode is require, and the gate token is missing_independent_second_review, return 2. Do not add a ReadFailure in tools/governance_hygiene/reads.py (that path is for VCS / coverage / muse-sync reads, not honesty claim gates).

JSON key (when the probe did not skip): independent_second_reviewer_gate {ok, mode, matched} plus phase_id when known plus token when set.

Human line when a message exists: independent_second_reviewer_gate: <message>

Exact miss message (require): missing independent_second_review ledger entry for active Auto slice

Exact miss message (warn): warning: no matching independent_second_review entry for active Auto slice

Exact remediation string (require path / engine footer / status JSON): second chat: append independent_second_review then ok honesty-status --independent-second-review PHASE

ok review --freeze is not wired to this surface.

Do not fail old DONE rows. Do not mark ROADMAP DONE. Do not dispatch a model from status or governance-sync.


§ISR.7 — Portable primary (CLI + docs) + optional host niceties

§ISR.7.1 — Portable contract (complete without Cursor)

Action Command / doc
Record the second verdict ok ledger append --kind independent_second_review
Check the gate ok honesty-status --independent-second-review PHASE [--producer-session ID] [--frozen-spec PATH] [--json]
Print NEXT for the second chat ok next / ok governance-sync --print-next
Paste instructions (Copilot / any host) docs/INDEPENDENT-SECOND-REVIEWER.md

Claude Code, Copilot, and paste-only hosts stay on this table. Missing Cursor skills or hooks is not pass and not fail — degrade to CLI + paste doc.

The kit never opens a second chat. The operator / host runtime does.

§ISR.7.2 — Paste doc (ISR-b writes)

Path: docs/INDEPENDENT-SECOND-REVIEWER.md (kit docs; same family as docs/CHECK-OK.md / docs/PRINT-NEXT.md).

Auto must include, verbatim as the opening sentence:

The kit records and gates the second verdict. It does not run another model. Open a new chat or a separate verifier runtime; then use the CLI below.

The doc must tell the builder to invent or copy an opaque producer_session nonce and give it to the second chat (chat / composer id when the host exposes one; otherwise a human-chosen nonce). Auto v1 does not add a new handover HTML anchor for this nonce (GS-PASTE sole NEXT regen; do not reopen).

The doc must include the two CLI lines from §ISR.7.1 and state that a human counts as the second verifier.

§ISR.7.3 — Twin skill (ISR-b writes)

Source (Auto writes): cursor/skills/independent-second-reviewer/SKILL.md. Dest twins after the required ISR-b ok sync --yes: .cursor/skills/independent-second-reviewer/SKILL.md and .claude/skills/independent-second-reviewer/SKILL.md (existing dual glob).

Invoke: /independent-second-reviewer.

Process rules (frozen):

  1. Confirm this session is not the builder session. If it is, stop and tell the operator to open a second chat. Do not append a pass. Do not write ROADMAP DONE.
  2. Re-run /build-verification-review V1–V8 only against the frozen spec
    • diff (implementation honesty). V9 is not part of this step — it is the DONE-unlock check in §ISR.7.4 and cannot be satisfied before the append in step 3.
  3. On V1–V8 pass, append independent_second_review with isr_verdict: pass, this session as actor_session_id, and the builder nonce as producer_session_id.
  4. When honesty.require_verification_evidence is warn or require, the second session also appends / confirms Mode B verification_evidence (V8) before claiming DONE. ISR does not replace Mode B.
  5. ROADMAP DONE is allowed only after step 3 (and step 4 when it applies). That is when V9 holds if ISR is warn or require.
  6. When ISR config is off, the skill is advisory; Mode D / status do not fail.
  7. findings / blocked on V1–V8: do not append isr_verdict: pass. A findings ISR append is allowed but not required (same family as P-evidence rule 6).
  8. Never merge to main. Never call a model via the kit CLI for this gate (ok review --freeze remains the K5 spec reviewer, not the ISR dispatcher).

§ISR.7.4 — Build-verification skill + always-on rule (ISR-b amends)

Amend the source twin cursor/skills/build-verification-review/SKILL.md. Live .cursor/ / .claude/ copies update via the required ISR-b ok sync --yes — do not require a separate hand-edit of those dests.

Model section — replace "if possible":

Always thinking-high. When honesty.require_independent_second_reviewer is warn or require, this review must run in a second chat or separate verifier runtime — not the session that built. A builder-session BV pass does not unlock DONE. When the flag is off, prefer a second session but do not fail the kit gate (process honesty only).

Add V9 (DONE-unlock only — not a V1–V8 implementation check):

# Check Dishonesty signal
V9 When ISR is warn or require, ROADMAP/HANDOVER may claim DONE only after a matching independent_second_review pass exists for this Auto slice with actor_session_idproducer_session_id Builder chat wrote DONE; no ISR ledger line; equal session ids

V1–V8 meaning unchanged. V9 does not block a V1–V8 implementation pass and does not require the ISR line to exist before the second session appends it (§ISR.7.3 order: V1–V8 → ISR append → Mode B when it applies → DONE). V9 is N/A (not a fail) when the flag is off or honesty is disabled. A builder-session V1–V8 pass still does not unlock DONE when ISR is warn or require.

Amend cursor/rules/build-verification-required.mdc (alwaysApply) with one short paragraph: when ISR is warn or require, do not mark DONE in the builder chat; open a second chat or follow docs/INDEPENDENT-SECOND-REVIEWER.md. Do not add a new alwaysApply rule file in Auto v1 (coverage blast).

§ISR.7.5 — Other doc pointers (ISR-b writes)

Auto must add one pointer sentence (no new ceremony) to:

  • AGENTS.md
  • docs/consumers/scooling/OVERSEER-SETUP.md
  • docs/consumers/knowtation/OVERSEER-SETUP.md

Each sentence names docs/INDEPENDENT-SECOND-REVIEWER.md and states the kit does not run another model. No live consumer ok init.

Amend tools/optional_feature_tips/surface.py: when honesty.enabled is true and require_independent_second_reviewer is off, append one tip naming the config key and the paste doc. When honesty is off, extend the existing honesty tip to mention ISR as well as verification-evidence. Do not fail status on tips.

§ISR.7.6 — Tool neutrality (normative)

Host Record Check Instructions
Any (ok CLI) ledger append Mode D + status surface paste doc
Cursor same same skill + amended BV rule (if repo root)
Claude Code same same .claude/skills after sync
Copilot / paste same same docs/INDEPENDENT-SECOND-REVIEWER.md

No MuseHub-only behavior. Mode D and the status surface run on git-only.

Cursor hooks / Automations are not in scope and are not a DONE gate.


§ISR.8 — Exit codes (frozen)

Additive code; non-overlapping with existing 1, 2, 4, 5, 7, 8, 1011, 2026, 3037:

Code Meaning Where
38 Independent second review required but missing (no matching isr_verdict=pass under §ISR.5.3) honesty-status Mode D when require_independent_second_reviewer: require; JSON error = missing_independent_second_review

Constant name (frozen): EXIT_MISSING_INDEPENDENT_SECOND_REVIEW = 38 in tools/honesty/status.py. CLI and tests import that name. Do not reuse 33 / 34 / 20.

Reused (no renumbering):

Code Reuse
1 Usage — Mode D combined with A/B/C or other §ISR.5.2 None
2 Malformed schema (bad isr_verdict, round < 1, empty producer_session_id, same-session ids, both agent ids equal, …); and ok status --exit-code / governance-sync when the active-slice require probe misses
4 Honesty module disabled / config refuse
23 Role violation (actor_roleverifier)
25 / 26 Provenance (P0; unchanged)

38 is confined to honesty-status Mode D. It does not change status --exit-code precedence (2 > 6 > 35 > 3 > 0).


§ISR.9 — Boundary, capability, rejection (governance, not runtime)

The single most important frozen rule: the kit records and optionally gates the second-reviewer claim. It never runs another model, never opens a second chat, and never treats host UI as a control surface.

Concern Overseer Kit Operator / host runtime
Append independent_second_review Yes (validate + chain) Supplies JSON body + session ids
Gate Mode D / status / governance-sync Yes (off/warn/require) Decides when to enable
Open a second chat / composer Never Yes
Dispatch / host / call a reviewer model Never Yes (Cursor picker, Claude, human, CI agent)
Infer session ids from the IDE Never Supplies opaque strings
Run /build-verification-review thinking Never (skill is process; K5 ok review --freeze is a different gate) Second chat / verifier
Prove chat identity cryptographically Optional P0 only Muse / operator
Tier-3 merge authorization Never Operator

Capability tiers:

Capability git-only (baseline) muse+git-mirror / muse-only
Record + gate ISR on file ledger Full Full (identical)
Optional signed provenance Soft (unsigned OK) Hard when require_agent_signature: true (P0)
Dispatch a second model Not in the kit Not in the kit
Temptation Verdict
ok shells out to a reviewer model to "be the second reviewer" Reject
Cursor hook that clicks "New Chat" Reject — no honest API; not portable
Same session appends ISR with a made-up second id as a kit-blessed pass Reject as process; kit cannot stop a liar, only refuse equal ids
Default require for all consumers Reject this phase
Waive BV because ISR passed Reject — both gates; V1–V8 still apply
Waive ISR because Mode B evidence exists Reject — evidence ≠ independence
Apply ISR to this Thinking freeze before the kind exists Reject — K5 freeze-review is the ISR-a gate

§ISR.10 — SPEC §5 additive row (ISR-b writes)

Amend the existing ok honesty-status row. Do not add a new command row.

Additive clause (normative text Auto must include):

ISR additive: Mode D --independent-second-review PHASE_ID with optional --producer-session / --frozen-spec; exit 38 + missing_independent_second_review when require_independent_second_reviewer: require and no match. Frozen: docs/archive/phases/PHASE-ISR-INDEPENDENT-SECOND-REVIEWER.md.

ok status additive JSON key: independent_second_reviewer_gate (optional; absent when the probe skips). --exit-code folds independent_second_reviewer_gate.ok (require mode only) into the existing 2 tier. Frozen in this document §ISR.6.

SPEC §5 ok status row must gain an ISR additive clause naming that JSON key and the exit-2 fold.


§ISR.11 — Seven-tier matrix (ISR-b)

Tier Must prove
unit independent_second_review in ENTRY_KINDS; validate accepts a minimal valid body; rejects same actor_session_id/producer_session_id2; empty/missing producer_session_id2; non-verifier → 23; bad isr_verdict2; round < 12; both agent ids equal → 2; one agent id only → accept; genesis forbid-list includes new keys; frozen_spec opaque non-empty without must-exist; require_independent_second_reviewer parse (off\|warn\|require, default off, unknown → config 2); key in HONESTY_KEYS; HonestyConfig field default off; HonestyErrorToken includes missing_independent_second_review; _resolve_mode invariants §ISR.5.2 (1)–(8); find_matching_independent_second_review last-wins, pin when producer supplied, reject findings.
integration ledger append --kind independent_second_review writes a hash-chained line; ledger verify0; Mode D require + no match → 38 + JSON token; matching pass0 + matched_entry_hash; warn miss → 0 + stderr warning; off → not enforced; Mode D + Mode B flags → 1; Mode D + --hook1; Mode A without Mode D flags remains byte-identical; honesty.enabled: false4; fixture lock/status: Auto DONE claim + require + no ISR → status --exit-code 2 + JSON gate; after append, coverage of this gate no longer forces 2.
e2e git-only fixture: enable honesty + ISR warn + active Auto DONE without ledger → status exit 0 + warn payload; require → exit 2 + token; Mode D with --producer-session matching entry → 0; same session ids refused at append; ok next still extracts the same fence after docs pointer edit. Identical unsigned path under a Muse-regime fixture.
stress Ledger with ≥ 50 ISR entries; match scan last-wins; no unbounded walk.
data-integrity Canonical hash includes producer_session_id / isr_verdict / session ids (tamper breaks verify → 22); append does not embed chat transcripts; dry-run / validate-fail writes nothing; Mode D omit-producer vs pin-producer do not rewrite the ledger.
performance Append + Mode D match + active-slice probe finish in a bounded time (same order as ok status).
security Session ids / notes that look like URLs or shell metacharacters are opaque — never fetched, never executed; no network and no model call on append/verify/match/status paths (asserted); no secrets required in ISR fields; producer role cannot append (23); equal session ids cannot pass; exit 38 cannot be waived by omitting Mode D when CI invokes it under require; no absolute machine paths in JSON gate payload.

Exact test file prefix: test_isr_ (under tests/ seven-tier folders). Do not invent a second family name.


§ISR.12 — Definition of Done

ISR-a (this phase): this document reviewed → pass with a CLI stamp; ROADMAP ISR-a → DONE; NEXT → ISR-b; no Auto code; no main merge; no live posture flips; no secrets.

ISR-b: every in-scope deliverable exists at the path this freeze names; §ISR.11 green; kit dogfood require_independent_second_reviewer: warn written; ok sync --yes so new skill twins land in the lock (coverage stays ok); SPEC §5 + AGENTS + consumer stubs + paste doc + BV V9 + BV rule + optional tips updated; no consumer defaults; no model dispatch; no tab/new-chat claim; ok status --exit-code not failing this gate on this repo unless an active Auto slice falsely claims DONE; ROADMAP + HANDOVER updated together; /build-verification-reviewpass from a second chat before ISR-b DONE (dogfood the gate at warn). Merge remains Tier 3.


§ISR.13 — Operator paste for ISR-b (informational; GS-PASTE may regen)

Model: Auto. Build exactly against this file. Do not redesign. Do not dispatch a model. Do not enable ISR require for consumers. Do not claim the kit opened a second chat.


§ISR.14 — Cross-references

  • LT §LT.2 / §LT.5.3 — deferred this gate; active-slice skip posture reused
  • P-evidence — Mode B / V8; not replaced
  • P-deploy — Mode C / §PD.5.0 resolution pattern this phase amends
  • P-route — rule-holder, not executor
  • K5 / K9a / K10 — freeze review, roles, ledger
  • K7 — no MuseHub-only baseline
  • ONS — portable CLI + docs; no tab-reload claim
  • Check OK — ad-hoc honesty; not a substitute for ISR on Auto DONE
File History 2 commits
sha256:6abcf1fa82a7a621ccbc945f19acdba5bc0db54569599404a1452fb4a096a199 fix(ISR): default require_independent_second_reviewer to require Human minor 5 hours ago
sha256:d19ce6f1b9af7123f2952c16142303cf906a8141fca5d75f6824d4312dccbfe9 chore(governance): sync handover+roadmap to fc2ecb0 (drift:… Human 5 hours ago