Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save anatoliyzalogin-png/2aeb42649d2c583a1c9310b4fc764033 to your computer and use it in GitHub Desktop.

Select an option

Save anatoliyzalogin-png/2aeb42649d2c583a1c9310b4fc764033 to your computer and use it in GitHub Desktop.
SmartSQL REVIEW-ROUTING-RESILIENCE-V1 Fable exact-head packet 7c7d857cc
{
"schema": "smartsql-fable-review-packet:v1",
"taskGoal": "Review REVIEW-ROUTING-RESILIENCE-V1 exact diff for equivalence carry-forward, independent fallback, and Fable packet completeness.",
"currentBase": "b0ee3a98f2363b125033584b453e56ab13e21607",
"currentHead": "7c7d857cc2b3db4de955dba43cd28209dabadae2",
"changedFileManifest": [
"docs/agent-governance/RISK_AND_REVIEW_MODES.md",
"scripts/agent-copilot.mjs",
"scripts/agent-copilot.test.mjs",
"scripts/agent-run",
"scripts/agents/committee.py",
"scripts/agents/committee_review_routing_test.py",
"scripts/lib/review-routing-resilience.mjs",
"scripts/review-routing-resilience.mjs",
"scripts/review-routing-resilience.test.mjs"
],
"executableDiffSha256": "68ff25660ee49f0bceb87a8d3f01705c2890c575aa5e7a2d392a73d9e6b80ab4",
"executableDiffChars": 55261,
"executableDiffChunks": [
{
"index": 1,
"sha256": "74bbcde501e5a431184782a0613a6d54ae03a1a43d8d60b33bc890b2a7763d98",
"content": "diff --git a/docs/agent-governance/RISK_AND_REVIEW_MODES.md b/docs/agent-governance/RISK_AND_REVIEW_MODES.md\nindex 933bfef32..a354fe787 100644\n--- a/docs/agent-governance/RISK_AND_REVIEW_MODES.md\n+++ b/docs/agent-governance/RISK_AND_REVIEW_MODES.md\n@@ -12,8 +12,8 @@ mode and review requirements; it does not redefine routing/provider policy.\n | --- | --- | --- | --- |\n | `R0` | tiny docs, metadata, typo, formatting | `skip` | deterministic checks and skip reason |\n | `R1` | normal docs/rules text with low operational risk | `skip` or `cheap-one` | deterministic checks plus recorded reason when skipped |\n-| `R2` | validators, governance behavior, delivery gates, platform code-impact tasks | `quorum` | DeepSeek + Qwen + Fable 5; if one lane is unavailable, exact blocker or owner adjudication depending scope |\n-| `R3` | deploy/release/security/env/CRM/DB rules or scripts | `mandatory-claude` or `mandatory-claude+deepseek` plus Fable 5 when code/rules changed | strict review logic; unavailable required lanes need exact blockers or owner adjudication |\n+| `R2` | validators, governance behavior, delivery gates, platform code-impact tasks | `independent-one` | deterministic checks, one GitHub-published PASS from a reviewer independent of the writer, and actual route/model identity |\n+| `R3` | deploy/release/security/env/CRM/DB rules or scripts | independent architecture/security review plus Fable/Claude or an explicit configured quorum | deterministic checks, rollback, and owner approval for irreversible action |\n | `R4` | actual production deploy | `mandatory-claude+deepseek` plus Fable 5 when code/rules changed | pre-deploy plan, payload, result/failure and production proof review; blocked/NEEDS_FIX/unavailable reviewer stops or needs GitHub owner adjudication |\n \n Every governance/rules/deploy final report records `risk level`, `review mode`,\n@@ -27,7 +27,7 @@ evidence only when it names the actual provider/model/route and exact blocker.\n | --- | --- | --- | --- |\n | `R0 advisory` | trivial docs, typo, comments, metadata-only, read-only/no-code answers outside protected paths | self-check and deterministic checks; independent review optional | yes, `PASS_WITH_REVIEW_SKIPPED` with reason |\n | `R1 standard` | normal code/tests/refactors and process docs that do not weaken safety | target one independent reviewer plus deterministic checks | yes, `PASS_WITH_REVIEW_DEGRADED` only after bounded retry/alternate route, passing checks, no protected paths, and no P0/P1 finding |\n-| `R2 strict` | auth/security, secrets, billing/payment, migrations/data integrity, external writes, deploy/stage/prod logic, privileged CI/branch protection, safety gate changes, governance/factory/preflight/routing changes that weaken or reinterpret safety | strict review/quorum; all required deterministic checks | no |\n+| `R2 strict` | auth/security, secrets, billing/payment, migrations/data integrity, external writes, deploy/stage/prod logic, privileged CI/branch protection, safety gate changes, governance/factory/preflight/routing changes that weaken or reinterpret safety | one independent model family, GitHub receipt and all deterministic checks | no |\n \n R2 is triggered by change type and risk. A final merge recommendation alone does\n not promote a harmless change to R2, but governance/factory/preflight/routing\n@@ -40,7 +40,7 @@ client-ready claims.\n \n - R0: optional; skip only with reason.\n - R1: usually one independent reviewer is enough.\n-- R2: strict review/quorum required; no degraded pass.\n+- R2: one independent reviewer is required; fixed provider availability is never an owner blocker.\n \n Cross-review is not quorum. A cross-review report may be sufficient for R1 and\n still not satisfy explicit quorum.\n@@ -54,13 +54,14 @@ Quorum is explicit stronger mode. It is triggered by owner phrases such as:\n - `Qwen + DeepSeek обязательно`\n - `проверить двумя агентами`\n \n-Default quorum is:\n+Default R2 review is one healthy independent family. Requested routes use\n+DeepSeek → Qwen → approved Fable/Claude → another approved independent family,\n+skipping unhealthy/cooldown routes. The receipt records requested and actual\n+reviewer, fallback reason, family independence and final verdict. A named\n+multi-member quorum is required only by an explicit owner request/policy\n+contract.\n \n-1. Qwen + DeepSeek + Fable 5 prompt/spec/TZ review.\n-2. Qwen + DeepSeek + Fable 5 code/diff review when changes exist.\n-3. Qwen + DeepSeek + Fable 5 final report/output review.\n-\n-Platform code-impact tasks require DeepSeek, Qwen and Fable 5. A task is\n+Platform code-impact tasks require the R2 independent-review contract. A task is\n platform code-impact when it touches or intends to touch backend/frontend code,\n scripts, validators, Buildkite/deploy helpers, factory/agent workflow code,\n `.agents` skills, `.cursor` rules, agent governance code, package scripts, or\n@@ -82,17 +83,20 @@ model id, task-specific verdict, economy reason, input packet type, and ledger\n record are known. Smoke-only output is not review evidence.\n \n Claude participates when a safe read-only Claude route is actually available.\n-If Claude is unavailable after registry/wrapper/CLI/health checks, record\n-`CLAUDE_REVIEW_ROUTE_UNAVAILABLE` with exact evidence and continue with the\n-required Qwen + DeepSeek quorum. This is not an alternate-quorum downgrade; it\n-is the owner-approved Claude-if-available policy. Reporting Claude as PASS while\n-unavailable is a hard failure.\n+If Fable/Claude is unavailable after registry/wrapper/CLI/health checks, record\n+`CLAUDE_REVIEW_ROUTE_UNAVAILABLE` with exact route evidence and continue the\n+applicable independent-family fallback for R2. R3 may use only an explicit\n+configured quorum as its substitute; unavailable Fable/Claude is never reported\n+as PASS.\n \n-MiniMax, Z.AI, and Ollama may be additional reviewers. They do not replace Qwen\n-+ DeepSeek + Fable 5 unless the owner explicitly approves an alternate quorum.\n-There is no silent downgrade"
},
{
"index": 2,
"sha256": "8143b39e88798a60323d48162ad26b99a65ff5a94a96ee5220408a26a895a8e8",
"content": ".\n+MiniMax, Z.AI, and Ollama may be additional reviewers only when their concrete\n+model family and route are explicitly marked `approved: true` in the route\n+health registry. The mechanical resolver is\n+`selectReviewerFallback()` in `scripts/lib/review-routing-resilience.mjs`; it\n+enforces the approved family/route table and writer-family exclusion. They never\n+silently impersonate a requested reviewer.\n \n-Each quorum lane must record:\n+Each required reviewer lane must record:\n \n ```text\n Reviewer role:\n@@ -110,14 +114,14 @@ Reviewed HEAD SHA:\n Factory, helper, cheap-agent, ProjectOps, telemetry, and generic provider routes\n do not count as reviewer quorum by themselves.\n \n-Quorum aggregation is unanimous for required roles and stages. A required Qwen,\n-DeepSeek, or Fable 5 `NEEDS_FIX`, `BLOCKED`, malformed response, stale hash,\n-missing model identity, or non-counting verdict blocks final authority for that\n-stage even if another reviewer returned `PASS`. The fix must be applied,\n-evidence hashes regenerated if affected, and the required reviewer must\n-re-review the new prompt/spec, diff, or final report before `READY`, `COMPLETE`,\n-`MERGE_READY`, `FACTORY_RECOVERY_FULLY_APPROVED`, or\n-`AGENT_RULES_V2_FULLY_APPROVED`.\n+Any P0/P1 finding, any `NEEDS_FIX`/`BLOCKED` verdict from the counting\n+independent lane, or any deterministic-check failure blocks authority. A disputed\n+severity is adjudicated by the parent agent and recorded in the GitHub receipt.\n+Provider unavailability, malformed response, stale SHA or incomplete packet\n+triggers fallback, equivalence validation, packet rebuild, or changed-delta\n+review; it never fabricates PASS. If no healthy independent family remains after\n+the bounded chain, the terminal machine state is `REVIEW_REQUIRED` with each\n+route-level error class; it is not an owner blocker by itself.\n \n For strict quorum, model identity is a tuple:\n \n@@ -139,7 +143,8 @@ contains `flash`, `lite`, generic/free aliases, or is missing while Pro is\n required. The mechanical resolver is `resolveDeepSeekQuorumRoute`; Flash never\n counts toward strict quorum without explicit owner-approved policy.\n \n-R2/quorum cannot degrade through soft defaults. If a required reviewer route is\n-unavailable, malformed, stale, or returns a non-PASS verdict, report\n-`REVIEW_REQUIRED` / `QUORUM_UNAVAILABLE` / `NEEDS_FIX` with exact evidence\n-instead of presenting a partial review as completion.\n+R2 cannot degrade to a same-family/self review. Before a Fable/Claude call the\n+packet must contain task goal, current base/head, full manifest, executable diff\n+or deterministic chunks, contracts, test/CI evidence, prior findings and\n+requested verdict. Incomplete packets are rebuilt before one bounded call; a\n+context-blocked response receives one rebuilt retry.\n"
},
{
"index": 3,
"sha256": "180ca36e14d3e1e7b0f2d3d3db7dbdf1099653e4e3191247067ed957bf163364",
"content": "diff --git a/scripts/agent-copilot.mjs b/scripts/agent-copilot.mjs\nindex 530f300c8..6f391af24 100644\n--- a/scripts/agent-copilot.mjs\n+++ b/scripts/agent-copilot.mjs\n@@ -1,5 +1,6 @@\n #!/usr/bin/env node\n import { execFileSync } from \"node:child_process\";\n+import { createHash } from \"node:crypto\";\n import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\n import { homedir } from \"node:os\";\n import path from \"node:path\";\n@@ -26,11 +27,16 @@ export const VALID_COPILOT_MODES = new Set([\"plan\", \"diff\", \"final\", \"architectu\n export const LEDGER_SCHEMA_VERSION = FABLE_ECONOMY_LEDGER_SCHEMA_VERSION;\n // Keep room for the fixed prompt wrapper and JSON request envelope under the advisor 8000-char message cap.\n export const MAX_ARTIFACT_CHARS = 7400;\n+// The advisor accepts an 8k request including the fixed JSON contract. Leave\n+// space for that wrapper and build a deterministic digest instead of slicing a\n+// diff invisibly at the boundary.\n+export const MAX_REVIEW_PACKET_CHARS = 6000;\n \n const EXIT = { PASS: 0, NEEDS_FIX: 10, BLOCKED: 11, ROUTE_OR_VALIDATION: 12, USAGE: 2 };\n const cap = (s) => String(s ?? \"\").length <= MAX_ARTIFACT_CHARS ? String(s ?? \"\") : `${String(s ?? \"\").slice(0, MAX_ARTIFACT_CHARS)}\\n\\n[TRUNCATED: original ${String(s ?? \"\").length} chars, limit ${MAX_ARTIFACT_CHARS}]`;\n const unfence = (s) => String(s || \"\").trim().replace(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/i, \"$1\").trim();\n const stamp = () => new Date().toISOString().replace(/[:.]/g, \"-\");\n+const digest = (value) => createHash(\"sha256\").update(String(value ?? \"\")).digest(\"hex\");\n export const isAcceptedFableActualModel = (actual) => String(actual) === FABLE_REQUESTED_MODEL || String(actual).startsWith(FABLE_ACTUAL_PREFIX);\n \n export function parseArgs(argv) {\n@@ -63,8 +69,117 @@ export function readCopilotInput(a) {\n }\n if (!a.diff) throw new Error(\"--diff is required for diff mode\");\n const ps = String(a.paths || \"\").split(\",\").map((p) => p.trim()).filter(Boolean);\n- if (a[\"full-files\"] === \"true\" && ps.length) return { mode, label: `${a.diff} -- working-tree full ${ps.join(\",\")}`, content: ps.map((p) => `# ${p}\\n${readFileSync(p, \"utf8\")}`).join(\"\\n\\n\") };\n- return { mode, label: ps.length ? `${a.diff} -- ${ps.join(\",\")}` : a.diff, content: execFileSync(\"git\", ps.length ? [\"diff\", a.diff, \"--\", ...ps] : [\"diff\", a.diff], { encoding: \"utf8\", maxBuffer: 8 * 1024 * 1024 }) };\n+ const label = ps.length ? `${a.diff} -- ${ps.join(\",\")}` : a.diff;\n+ const executableDiff = a[\"full-files\"] === \"true\" && ps.length\n+ ? ps.map((p) => `# ${p}\\n${readFileSync(p, \"utf8\")}`).join(\"\\n\\n\")\n+ : execFileSync(\"git\", ps.length ? [\"diff\", a.diff, \"--\", ...ps] : [\"diff\", a.diff], { encoding: \"utf8\", maxBuffer: 8 * 1024 * 1024 });\n+ const manifest = execFileSync(\"git\", ps.length ? [\"diff\", \"--name-only\", a.diff, \"--\", ...ps] : [\"diff\", \"--name-only\", a.diff], { encoding: \"utf8\" })\n+ .split(\"\\n\").map((item) => item.trim()).filter(Boolean);\n+ const baseRef = String(a.diff).split(\"...\", 1)[0] || \"origin/main\";\n+ const rev = (ref) => execFileSync(\"git\", [\"rev-parse\", ref], { encoding: \"utf8\" }).trim();\n+ const taskGoal = a[\"task-file\"] ? readFileSync(a[\"task-file\"], \"utf8\") : (a[\"task-goal\"] || `Independent review of ${label}`);\n+ const packet = buildFableReviewPacket({\n+ taskGoal,\n+ currentBase: rev(baseRef),\n+ currentHead: rev(\"HEAD\"),\n+ changedFileManifest: manifest,\n+ executableDiff,\n+ affectedContracts: ps.length ? ps : manifest,\n+ testEvidence: a[\"test-evidence\"] || \"not supplied by caller\",\n+ ciState: a[\"ci-state\"] || \"unknown\",\n+ priorFindings: a[\"prior-findings\"] || \"none\",\n+ requestedVerdict: a[\"requested-verdict\"] || \"PASS|NEEDS_FIX|BLOCKED\",\n+ });\n+ return { mode, label, content: formatFableReviewPacket(packet), packet };\n+}\n+\n+function splitDiffChunks(executableDiff) {\n+ const sections = String(executableDiff || \"\").split(/(?=^diff --git )/m).filter(Boolean);\n+ const source = sections.length ? sections : (executableDiff ? [String(executableDiff)] : []);\n+ const chunks = [];\n+ for (const section of source) {\n+ for (let offset = 0; offset < section.length || (section.length === 0 && offset === 0); offset += MAX_REVIEW_PACKET_CHARS) {\n+ const content = section.slice(offset, offset + MAX_REVIEW_PACKET_CHARS);\n+ chunks.push({ index: chunks.length + 1, sha256: digest(content), content });\n+ if (!section.length) break;\n+ }\n+ }\n+ return chunks;\n+}\n+\n+/**\n+ * Creates a complete, bounded Fable diff packet. The complete executable diff\n+ * is preserved in deterministic chunks; the model receives a bounded digest\n+ * plus the chunks that fit the provider request. This intentionally replaces\n+ * the old silent [TRUNCATED] tail.\n+ */\n+export function buildFableReviewPacket(input = {}) {\n+ const required = [\"taskGoal\", \"currentBase\", \"currentHead\", \"changedFileManifest\", \"executableDiff\", \"affectedContracts\", \"testEvidence\", \"ciState\", \"priorFindings\", \"requestedVerdict\"];\n+ const missing = required.filter((key) => !input[key] || (Array.isArray(input[key]) && !input[key].length));\n+ const chunks = splitDiffChunks(input.executableDiff);\n+ if (!chunks.length && !missing.includes(\"executableDiff\")) missing.push(\"executableDiff\");\n+ return {\n+ schema: \"smartsql-fable-review-packet:v1\",\n+ taskGoal: String(input.taskGoal || \"\"),\n+ currentBase: String(input.currentBase || \"\"),\n+ currentHead: String(input.currentHead || \"\"),\n+ changedFileManifest: Array.isArray(input.changedFileManifest) ? input.changedFileManifest : [],\n+ executableDiffSha256: digest(input.executableDiff || \"\"),\n+ executableDiffChars: String(input.executableDiff || \"\").length,\n+ executableDiffChunks: chunks,\n+ affectedContracts: Array.isArray(input.affectedContracts) ? input.affectedContracts : [String(input.affectedContracts || \"\")].filter(Boolean),\n+ testEvidence: String(input.testEvidence || \"\"),\n+ ciState: String(input.ciState || \"\"),\n+ pr"
},
{
"index": 4,
"sha256": "08d33f4ebf25123cf770549e9690c2f2f9426b6f92057f1655e6ba6014d61db8",
"content": "iorFindings: String(input.priorFindings || \"\"),\n+ requestedVerdict: String(input.requestedVerdict || \"\"),\n+ complete: missing.length === 0,\n+ missing,\n+ };\n+}\n+\n+export function formatFableReviewPacket(packet, { maxChars = MAX_REVIEW_PACKET_CHARS, compact = false } = {}) {\n+ const manifest = packet.changedFileManifest || [];\n+ const chunks = packet.executableDiffChunks || [];\n+ const header = {\n+ schema: packet.schema,\n+ task_goal: packet.taskGoal,\n+ current_base: packet.currentBase,\n+ current_head: packet.currentHead,\n+ changed_file_manifest: manifest,\n+ executable_diff: { sha256: packet.executableDiffSha256, chars: packet.executableDiffChars, chunk_count: chunks.length },\n+ affected_contracts: packet.affectedContracts,\n+ test_evidence: packet.testEvidence,\n+ ci_state: packet.ciState,\n+ prior_findings: packet.priorFindings,\n+ requested_verdict: packet.requestedVerdict,\n+ };\n+ let content = `${JSON.stringify(header, null, compact ? 0 : 2)}\\n\\navailable_file_chunks:\\n`;\n+ // Every declared chunk gets a deterministic payload excerpt. This is a\n+ // digest, not a misleading claim that the advisor received the whole diff;\n+ // preserving all chunk identities and payload samples lets it ask for a\n+ // specific bounded follow-up when a hunk needs deeper review.\n+ const labels = chunks.map((chunk) => compact\n+ ? `\\n# chunk ${chunk.index}/${chunks.length} sha256=${chunk.sha256} chars=${chunk.content.length}\\n`\n+ : `\\n--- chunk ${chunk.index}/${chunks.length} sha256=${chunk.sha256} chars=${chunk.content.length} ---\\n`);\n+ const excerptBudget = Math.floor((maxChars - content.length - labels.reduce((total, label) => total + label.length + 1, 0)) / Math.max(chunks.length, 1));\n+ if (chunks.length && excerptBudget < 24) throw new Error(\"PACKET_INCOMPLETE:packet header leaves no chunk payload budget\");\n+ for (const chunk of chunks) {\n+ const source = chunk.content;\n+ const marker = \"\\n…[deterministic excerpt]…\\n\";\n+ const excerpt = source.length <= excerptBudget ? source : (\n+ excerptBudget <= marker.length ? source.slice(0, excerptBudget)\n+ : `${source.slice(0, Math.ceil((excerptBudget - marker.length) / 2))}${marker}${source.slice(-Math.floor((excerptBudget - marker.length) / 2))}`\n+ );\n+ content += `${labels[chunk.index - 1]}${excerpt}\\n`;\n+ }\n+ if (content.length > maxChars) throw new Error(\"PACKET_INCOMPLETE:bounded digest exceeds provider budget\");\n+ return content;\n+}\n+\n+export function isContextBlockedResponse(response) {\n+ const content = parseAdvisorContent(response?.content ?? response?.message ?? response?.text ?? response?.output ?? response?.result);\n+ if (String(content.verdict || \"\").toUpperCase() !== \"BLOCKED\") return false;\n+ return /context|packet|truncat|incomplete/i.test(JSON.stringify(content));\n }\n \n export function parseAdvisorContent(v) {\n@@ -101,7 +216,7 @@ export function validateFableCopilotEvidence({ advisorResponse, workId, mode })\n accepted_for_consensus: !error && verdict === \"PASS\", verdict: verdict || null, error_class: error };\n }\n \n-export function buildLedgerRecord({ workId, mode, validation, rawOutputPath, architectureArtifacts = null }) {\n+export function buildLedgerRecord({ workId, mode, validation, rawOutputPath, architectureArtifacts = null, packet = null }) {\n const v = validation;\n const record = { schema_version: LEDGER_SCHEMA_VERSION, timestamp: new Date().toISOString(), work_id: workId, mode, risk_level: v.risk_level,\n owner_forced_fable: v.owner_forced_fable, fable_call_reason: v.fable_call_reason, input_packet_type: v.input_packet_type, requested_model: v.requested_model,\n@@ -114,6 +229,14 @@ export function buildLedgerRecord({ workId, mode, validation, rawOutputPath, arc\n record.architecture_decision_hash = architectureArtifacts.hash;\n record.architecture_packet_path = architectureArtifacts.packetPath;\n }\n+ if (packet) {\n+ record.packet_schema = packet.schema;\n+ record.packet_complete = packet.complete;\n+ record.packet_missing = packet.missing;\n+ record.packet_chunk_count = packet.executableDiffChunks?.length || 0;\n+ record.packet_rebuilt = Boolean(packet.rebuilt);\n+ record.packet_context_retry_count = Number(packet.contextRetryCount || 0);\n+ }\n return record;\n }\n \n@@ -207,10 +330,31 @@ async function main() {\n const a = parseArgs(process.argv.slice(2)), workId = a[\"work-id\"], riskLevel = riskLevelFromArgs(a),\n ownerForcedFable = ownerForcedFableFromArgs(a), i = readCopilotInput(a), dir = path.join(\"reports\", \"agentops\", workId);\n mkdirSync(dir, { recursive: true });\n+ if (i.packet) {\n+ const packetPath = path.join(dir, `fable-review-packet-${stamp()}.json`);\n+ writeFileSync(packetPath, JSON.stringify(i.packet, null, 2) + \"\\n\");\n+ i.packet.artifact = packetPath;\n+ }\n const raw = path.join(dir, `fable-copilot-${i.mode}-${stamp()}.json`);\n- const res = loadAdvisorResponse(a, workId, i.mode) ?? await callAdvisor({ workId, ...i });\n+ let res = i.packet && !i.packet.complete\n+ ? { http_status: 0, error: \"PACKET_INCOMPLETE\", message: i.packet.missing.join(\",\") }\n+ : (loadAdvisorResponse(a, workId, i.mode) ?? await callAdvisor({ workId, ...i }));\n+ // A provider may still declare a bounded packet insufficient. Rebuild the\n+ // digest once with a smaller selected chunk budget; never loop or fabricate\n+ // a PASS when the second bounded call remains blocked.\n+ if (i.packet && isContextBlockedResponse(res)) {\n+ i.packet.rebuilt = true;\n+ i.packet.contextRetryCount = 1;\n+ i.content = formatFableReviewPacket(i.packet, { maxChars: MAX_REVIEW_PACKET_CHARS, compact: true });\n+ res = await callAdvisor({ workId, ...i });\n+ }\n writeFileSync(raw, JSON.stringify(res, null, 2) + \"\\n\");\n const validation = validateFableCopilotEvidence({ advisorResponse: res, workId, mode: i.mode });\n+ if (i.packet && !i.packet.complete) {\n+ validatio"
},
{
"index": 5,
"sha256": "fdec9f837aab73054a6fe1529108ef5d4152c1232ce7fb37a95c0153a6e3f5b4",
"content": "n.error_class = \"PACKET_INCOMPLETE\";\n+ validation.accepted_for_consensus = false;\n+ validation.task_specific_verdict = false;\n+ }\n validation.risk_level = riskLevel;\n validation.owner_forced_fable = ownerForcedFable;\n validation.fable_call_reason = fableCallReasonFromArgs(a, { mode: i.mode, ownerForcedFable });\n@@ -229,7 +373,7 @@ async function main() {\n }\n }\n \n- const record = buildLedgerRecord({ workId, mode: i.mode, validation, rawOutputPath: raw, architectureArtifacts });\n+ const record = buildLedgerRecord({ workId, mode: i.mode, validation, rawOutputPath: raw, architectureArtifacts, packet: i.packet });\n appendFileSync(path.join(dir, \"model-run-ledger.jsonl\"), JSON.stringify(record) + \"\\n\");\n console.log(JSON.stringify(record, null, 2));\n process.exit(validation.error_class ? EXIT.ROUTE_OR_VALIDATION : (EXIT[validation.verdict] ?? EXIT.ROUTE_OR_VALIDATION));\n"
},
{
"index": 6,
"sha256": "4060fce75408ebbb9ff6d6dd76c4b6ff8fb877736c55dd05806a0a30b69e552a",
"content": "diff --git a/scripts/agent-copilot.test.mjs b/scripts/agent-copilot.test.mjs\nindex d64436500..277dc2540 100644\n--- a/scripts/agent-copilot.test.mjs\n+++ b/scripts/agent-copilot.test.mjs\n@@ -6,8 +6,11 @@ import test from \"node:test\";\n \n import {\n buildLedgerRecord,\n+ buildFableReviewPacket,\n FABLE_PROVIDER_ROUTE,\n+ formatFableReviewPacket,\n FABLE_REQUESTED_MODEL,\n+ isContextBlockedResponse,\n LEDGER_SCHEMA_VERSION,\n MAX_ARTIFACT_CHARS,\n ownerForcedFableFromArgs,\n@@ -218,6 +221,31 @@ test(\"Fable copilot prompt truncates large artifacts below advisor request cap\",\n assert.match(prompt, /TRUNCATED/);\n });\n \n+test(\"large diff packet is rebuilt into deterministic chunks before a bounded Fable call\", () => {\n+ const packet = buildFableReviewPacket({\n+ taskGoal: \"review routing resilience\",\n+ currentBase: \"base\", currentHead: \"head\", changedFileManifest: [\"a.mjs\", \"b.py\"],\n+ executableDiff: `diff --git a/a.mjs b/a.mjs\\n${\"a\".repeat(5000)}\\ndiff --git a/b.py b/b.py\\n${\"b\".repeat(5000)}`,\n+ affectedContracts: [\"reviewer routing\"], testEvidence: \"node --test\", ciState: \"pending\",\n+ priorFindings: \"none\", requestedVerdict: \"PASS|NEEDS_FIX|BLOCKED\",\n+ });\n+ const formatted = formatFableReviewPacket(packet);\n+ assert.equal(packet.complete, true);\n+ assert.ok(packet.executableDiffChunks.length >= 2);\n+ assert.ok(formatted.length <= 6000);\n+ assert.doesNotMatch(formatted, /\\[TRUNCATED/);\n+ assert.match(formatted, /executable_diff/);\n+ assert.equal((formatted.match(/--- chunk /g) || []).length, packet.executableDiffChunks.length);\n+ const compactRetry = formatFableReviewPacket(packet, { compact: true });\n+ assert.ok(compactRetry.length <= 6000);\n+ assert.equal((compactRetry.match(/# chunk /g) || []).length, packet.executableDiffChunks.length);\n+});\n+\n+test(\"only a context-caused BLOCKED response gets one packet rebuild retry\", () => {\n+ assert.equal(isContextBlockedResponse({ content: JSON.stringify({ verdict: \"BLOCKED\", reason: \"packet context incomplete\" }) }), true);\n+ assert.equal(isContextBlockedResponse({ content: JSON.stringify({ verdict: \"BLOCKED\", reason: \"safety finding\" }) }), false);\n+});\n+\n test(\"Fable copilot risk and usage metadata are normalized\", () => {\n assert.equal(riskLevelFromArgs({ \"risk-level\": \"r3\" }), \"R3\");\n assert.throws(() => riskLevelFromArgs({}), /--risk-level must be/);\n"
},
{
"index": 7,
"sha256": "8d6832ca62137242744be716c343dea2e0c9b33065b5f85d1bcd0b5db475743a",
"content": "diff --git a/scripts/agent-run b/scripts/agent-run\nindex fda4a085b..93b8f6864 100755\n--- a/scripts/agent-run\n+++ b/scripts/agent-run\n@@ -668,6 +668,7 @@ def build_parser() -> argparse.ArgumentParser:\n p_committee.add_argument(\"--task-id\", default=None)\n p_committee.add_argument(\"--run-id\", default=None)\n p_committee.add_argument(\"--pr-number\", default=None)\n+ p_committee.add_argument(\"--writer-family\", default=\"\", help=\"Writer model family excluded from independent review\")\n p_committee.add_argument(\"--disable-provider\", default=\"\")\n p_committee.add_argument(\"--mock\", action=\"store_true\", help=\"Use structured mock reviewers for drills/tests\")\n \n@@ -822,6 +823,9 @@ def main(argv: list[str] | None = None) -> int:\n parser.add_argument(\"--risk\", default=\"normal\", choices=[\"low\", \"normal\", \"high\", \"advisory\", \"standard\", \"strict\", \"r0\", \"r1\", \"r2\", \"auto\"])\n parser.add_argument(\"--output\", default=None)\n parser.add_argument(\"--task-id\", default=None)\n+ parser.add_argument(\"--run-id\", default=None)\n+ parser.add_argument(\"--pr-number\", default=None)\n+ parser.add_argument(\"--writer-family\", default=\"\")\n parser.add_argument(\"--disable-provider\", default=\"\")\n parser.add_argument(\"--mock\", action=\"store_true\")\n ns = parser.parse_args(argv[1:])\n"
},
{
"index": 8,
"sha256": "8bd7a5de3868bf0758de35467a65994451518823b2d5ee318f9c002c4e657fde",
"content": "diff --git a/scripts/agents/committee.py b/scripts/agents/committee.py\nindex a30964e07..f70d94c24 100644\n--- a/scripts/agents/committee.py\n+++ b/scripts/agents/committee.py\n@@ -34,7 +34,9 @@ RISK_POLICY = {\n \"strict\": {\n \"aliases\": {\"r2\", \"high\", \"strict\"},\n \"mode\": \"STRICT\",\n- \"min_reviewers\": 2,\n+ # R2 requires one independently identified reviewer plus deterministic\n+ # evidence. Availability of any named model is never a quorum seat.\n+ \"min_reviewers\": 1,\n \"strict\": True,\n },\n }\n@@ -86,6 +88,7 @@ REASON_CODES = {\n \"ENDPOINT_UNREACHABLE\",\n \"MODEL_UNAVAILABLE\",\n \"MALFORMED_RESPONSE\",\n+ \"PACKET_INCOMPLETE\",\n \"ADAPTER_ERROR\",\n }\n \n@@ -100,7 +103,7 @@ def run_committee(args: argparse.Namespace) -> dict[str, Any]:\n packet_hash = review_packet_hash(head_sha, task_text, diff_text)\n disabled = {item.strip() for item in (args.disable_provider or os.getenv(\"AGENT_RUN_DISABLE_PROVIDER\", \"\")).split(\",\") if item.strip()}\n mock = bool(args.mock or os.getenv(\"AGENT_RUN_MOCK_REVIEWERS\"))\n- requested = select_reviewers(registry, risk, disabled)\n+ requested = select_reviewers(registry, risk, disabled, str(getattr(args, \"writer_family\", \"\") or \"\"))\n completed: list[dict[str, Any]] = []\n skipped: list[dict[str, Any]] = []\n findings = synthetic_findings(diff_text, risk)\n@@ -108,7 +111,10 @@ def run_committee(args: argparse.Namespace) -> dict[str, Any]:\n quorum_needed = int(policy[\"min_reviewers\"])\n \n for agent in requested:\n- result = run_reviewer(agent, diff_range, mock=mock, reviewed_head_sha=head_sha, packet_hash=packet_hash, work_id=str(getattr(args, \"task_id\", \"\") or \"\"))\n+ result = run_reviewer(\n+ agent, diff_range, mock=mock, reviewed_head_sha=head_sha, packet_hash=packet_hash,\n+ work_id=str(getattr(args, \"task_id\", \"\") or \"\"), task_file=str(getattr(args, \"task_file\", \"\") or \"\"),\n+ )\n if result.get(\"counts_toward_quorum\"):\n completed.append(result)\n findings.extend(result.get(\"findings\", []))\n@@ -116,7 +122,9 @@ def run_committee(args: argparse.Namespace) -> dict[str, Any]:\n skipped.append(result)\n if findings:\n continue\n- if risk != \"strict\" and committee_quorum(completed, quorum_needed, strict=bool(policy[\"strict\"])) == \"MET\":\n+ # R2 now needs one verified independent reviewer; once met, further\n+ # provider calls only add cost/latency and can recreate an outage loop.\n+ if not findings and committee_quorum(completed, quorum_needed, strict=bool(policy[\"strict\"])) == \"MET\":\n break\n \n r2_precheck = None\n@@ -512,7 +520,7 @@ def write_and_validate_r2_evidence_packet(\n return result, str(packet_path.relative_to(repo_root()))\n \n \n-def select_reviewers(registry: dict[str, Any], risk: str, disabled: set[str]) -> list[dict[str, Any]]:\n+def select_reviewers(registry: dict[str, Any], risk: str, disabled: set[str], writer_family: str = \"\") -> list[dict[str, Any]]:\n candidate_limit = max(int(RISK_POLICY[risk][\"min_reviewers\"]), 1)\n if risk == \"standard\":\n candidate_limit = 2\n@@ -521,8 +529,16 @@ def select_reviewers(registry: dict[str, Any], risk: str, disabled: set[str]) ->\n selected: list[dict[str, Any]] = []\n used_independence: set[str] = set()\n disabled_lower = {item.lower() for item in disabled}\n- for agent in sorted(reasoning_agents(registry), key=lambda item: int(item.get(\"review_priority\", 999))):\n+ writer_family = writer_family.lower().strip()\n+ def priority(item: dict[str, Any]) -> tuple[int, int]:\n+ canonical = str(item.get(\"canonical_agent_id\") or \"\")\n+ preferred = {\"openrouter-deepseek-v4-pro\": 0, \"openrouter-qwen-coder\": 1, \"fable5-copilot\": 2}\n+ return (preferred.get(canonical, 99), int(item.get(\"review_priority\", 999))) if risk == \"strict\" else (0, int(item.get(\"review_priority\", 999)))\n+\n+ for agent in sorted(reasoning_agents(registry), key=priority):\n agent_id = str(agent[\"canonical_agent_id\"])\n+ if writer_family and str(agent.get(\"model_family\") or \"\").lower() == writer_family:\n+ continue\n names = {agent_id.lower(), *(str(alias).lower() for alias in agent.get(\"aliases\", []))}\n if \"all\" in disabled_lower or names.intersection(disabled_lower):\n continue\n@@ -704,6 +720,7 @@ def run_reviewer(\n reviewed_head_sha: str,\n packet_hash: str,\n work_id: str | None = None,\n+ task_file: str | None = None,\n ) -> dict[str, Any]:\n agent_id = str(agent[\"canonical_agent_id\"])\n started = time.time()\n@@ -711,6 +728,51 @@ def run_reviewer(\n return reviewer_result(agent, \"FAIL\", \"mock reviewer unavailable\", started, reviewed_head_sha=reviewed_head_sha, packet_hash=packet_hash)\n if mock:\n return reviewer_result(agent, \"PASS\", \"mock structured reviewer\", started, reviewed_head_sha=reviewed_head_sha, packet_hash=packet_hash)\n+ if agent_id == \"fable5-copilot\":\n+ diff_text = get_diff_text(diff_range)\n+ # agent:copilot preflights the complete packet and turns large diffs\n+ # into a deterministic bounded digest. Only an actually empty diff is\n+ # incomplete; a large diff is not silently rejected before rebuild.\n+ if not diff_text:\n+ return reviewer_result(\n+ agent, \"FAIL\", \"Fable packet is incomplete; executable diff is empty\", started,\n+ lane_outcome=\"UNAVAILABLE\", review_verdict=\"NOT_REVIEWED\", reason_code=\"PACKET_INCOMPLETE\",\n+ reviewed_head_sha=reviewed_head_sha, packet_hash=packet_hash,\n+ )\n+ command = [\n+ \"npm\", \"run\", \"agent:copilot\", \"--\", \"--mode\", \"diff\", \"--work-id\", work_id or \"agent-committee\",\n+ \"--diff\", diff_range, \"--risk-level\", \"R2\", \"--owner-forced-fable\", \"false\",\n+ \"--fable-call-reason\", \"critical_final_decision\", \"--input-packet-type"
},
{
"index": 9,
"sha256": "4b7c8eba9ca3d45e6543d7a6f22e99427a086a71ab137c41f2eaa0dd301e975d",
"content": "\", \"compressed\",\n+ ]\n+ if task_file:\n+ command.extend([\"--task-file\", task_file])\n+ proc = subprocess.run(command, cwd=repo_root(), capture_output=True, text=True, timeout=60, check=False)\n+ try:\n+ record = json.loads(proc.stdout)\n+ except json.JSONDecodeError:\n+ record = {}\n+ if proc.returncode == 2 and \"PACKET_INCOMPLETE\" in (proc.stderr or \"\"):\n+ return reviewer_result(\n+ agent, \"FAIL\", \"Fable packet remains incomplete after automatic rebuild\", started,\n+ lane_outcome=\"UNAVAILABLE\", review_verdict=\"NOT_REVIEWED\", reason_code=\"PACKET_INCOMPLETE\",\n+ reviewed_head_sha=reviewed_head_sha, packet_hash=packet_hash,\n+ )\n+ if record.get(\"error_class\"):\n+ return reviewer_result(\n+ agent, \"FAIL\", redact(str(record.get(\"error_class\"))), started,\n+ lane_outcome=\"UNAVAILABLE\", review_verdict=\"NOT_REVIEWED\", reason_code=\"MODEL_UNAVAILABLE\",\n+ reviewed_head_sha=reviewed_head_sha, packet_hash=packet_hash,\n+ )\n+ verdict = str(record.get(\"verdict\") or \"NOT_REVIEWED\")\n+ mapped = {\"PASS\": \"PASS\", \"NEEDS_FIX\": \"CHANGES_REQUIRED\", \"BLOCKED\": \"BLOCKED_SAFETY\"}.get(verdict, \"NOT_REVIEWED\")\n+ return reviewer_result(\n+ agent, \"PASS\" if mapped == \"PASS\" else \"FAIL\", f\"Fable {verdict}\", started,\n+ lane_outcome=\"COMPLETED\" if mapped != \"NOT_REVIEWED\" else \"FAILED\", review_verdict=mapped,\n+ reason_code=\"NONE\" if mapped == \"PASS\" else (\"SUBSTANTIVE_FINDINGS\" if mapped == \"CHANGES_REQUIRED\" else \"SAFETY_BLOCK\"),\n+ reviewed_head_sha=reviewed_head_sha, packet_hash=packet_hash, actual_model=str(record.get(\"actual_model\") or \"\"),\n+ resolved_model=str(record.get(\"resolved_model\") or \"\"), fallback_used=record.get(\"fallback_used\"),\n+ task_specific_verdict=record.get(\"task_specific_verdict\") is True,\n+ )\n if agent_id in CANONICAL_REVIEWERS:\n diff_text = get_diff_text(diff_range)\n if not diff_text:\n@@ -992,7 +1054,9 @@ def independence_ok(completed: list[dict[str, Any]], required: int, strict: bool\n # operators/auditors even though it no longer blocks quorum.\n groups = {item.get(\"independence_group\") for item in completed}\n model_families = {item.get(\"model_family\") for item in completed}\n- min_model_families = 2 if strict else required\n+ # A strict R2 review is independent from the writer (validated by the\n+ # routing receipt) rather than an inflexible two-provider ceremony.\n+ min_model_families = required\n if len(groups) < required:\n return False\n if len(model_families) < min_model_families:\n"
},
{
"index": 10,
"sha256": "1ab8ca1c569d24682293556bd87b4486d50d4f9fbee6e22d4ef59c3b820b38ef",
"content": "diff --git a/scripts/agents/committee_review_routing_test.py b/scripts/agents/committee_review_routing_test.py\nnew file mode 100644\nindex 000000000..040a9ade8\n--- /dev/null\n+++ b/scripts/agents/committee_review_routing_test.py\n@@ -0,0 +1,25 @@\n+import unittest\n+from types import SimpleNamespace\n+from unittest.mock import patch\n+\n+from scripts.agents import committee\n+\n+\n+class CommitteeReviewRoutingTests(unittest.TestCase):\n+ def test_fable_oversized_diff_is_sent_to_canonical_packet_rebuilder(self):\n+ agent = {\n+ \"canonical_agent_id\": \"fable5-copilot\", \"provider\": \"OpenRouter\", \"model\": \"~anthropic/claude-fable-latest\",\n+ \"required_model_id\": \"~anthropic/claude-fable-latest\", \"model_family\": \"claude-fable\",\n+ \"invocation_mode\": \"advisor/complete\", \"independence_group\": \"claude-fable\", \"credential_boundary\": \"shared\",\n+ }\n+ route_unavailable = SimpleNamespace(returncode=12, stdout='{\"error_class\":\"BLOCKED_MODEL_ACCESS\"}', stderr=\"\")\n+ with patch.object(committee, \"get_diff_text\", return_value=\"x\" * 7401), patch.object(committee.subprocess, \"run\", return_value=route_unavailable) as run:\n+ result = committee.run_reviewer(agent, \"origin/main...HEAD\", False, \"a\" * 40, \"b\" * 64, \"OPS-W-FACTORY-RULES-V2\")\n+ self.assertEqual(result[\"reason_code\"], \"MODEL_UNAVAILABLE\")\n+ self.assertEqual(result[\"lane_outcome\"], \"UNAVAILABLE\")\n+ run.assert_called_once()\n+ self.assertIn(\"agent:copilot\", run.call_args.args[0])\n+\n+\n+if __name__ == \"__main__\":\n+ unittest.main()\n"
},
{
"index": 11,
"sha256": "31e6f743d3f6398cec7764659acdadf813ec346fb4030866a8b449441362427b",
"content": "diff --git a/scripts/lib/review-routing-resilience.mjs b/scripts/lib/review-routing-resilience.mjs\nnew file mode 100644\nindex 000000000..8e07f4166\n--- /dev/null\n+++ b/scripts/lib/review-routing-resilience.mjs\n@@ -0,0 +1,134 @@\n+import { createHash } from \"node:crypto\";\n+\n+export const REVIEW_EQUIVALENCE_SCHEMA = \"smartsql-review-equivalence:v1\";\n+export const ROUTE_ERRORS = new Set([\"MODEL_UNAVAILABLE\", \"ADAPTER_UNAVAILABLE\", \"AUTH_UNAVAILABLE\", \"RATE_LIMITED\", \"TIMEOUT\", \"MALFORMED_RESPONSE\", \"PACKET_INCOMPLETE\", \"POLICY_REJECTED\"]);\n+export const APPROVED_REVIEWER_FAMILIES = Object.freeze({\n+ deepseek: [\"runtime-session/complete\"], qwen: [\"runtime-session/complete\"], fable: [\"advisor/complete\"], other: [\"approved-registry-only\"],\n+});\n+\n+export function sha256(value) { return createHash(\"sha256\").update(String(value)).digest(\"hex\"); }\n+export function manifestHash(files = []) { return sha256([...files].sort().join(\"\\n\")); }\n+export function reviewRequestKey({ repository, pr, headSha, reviewer, packetHash }) {\n+ return sha256([repository, pr, headSha, reviewer, packetHash].join(\"\\n\"));\n+}\n+export function reviewRequestDecision({ request, seen = new Set(), currentHead }) {\n+ if (request.headSha !== currentHead) return { accepted: false, reason: \"STALE_HEAD\", key: null };\n+ const key = reviewRequestKey(request);\n+ return { accepted: !seen.has(key), reason: seen.has(key) ? \"DUPLICATE_REQUEST\" : \"ACCEPTED\", key };\n+}\n+\n+export function decideReviewEquivalence(input) {\n+ const reasons = [];\n+ if (input.previousFileManifestHash !== input.currentFileManifestHash) reasons.push(\"FILE_MANIFEST_CHANGED\");\n+ if (input.previousPatchId !== input.currentPatchId) reasons.push(\"EXECUTABLE_PATCH_CHANGED\");\n+ if (input.newExecutableLines) reasons.push(\"NEW_EXECUTABLE_LINES\");\n+ if (input.newHighRiskPaths) reasons.push(\"NEW_HIGH_RISK_PATH\");\n+ if (input.conflictResolutionDelta) reasons.push(\"CONFLICT_RESOLUTION_DELTA\");\n+ if (String(input.previousVerdict || \"\").toUpperCase() !== \"PASS\") reasons.push(\"PREVIOUS_REVIEW_NOT_PASS\");\n+ if (!input.previousReceiptUrl) reasons.push(\"PREVIOUS_RECEIPT_MISSING\");\n+ if (!input.currentCiGreen) reasons.push(\"CURRENT_CI_NOT_GREEN\");\n+ return { status: reasons.length ? (reasons.includes(\"CONFLICT_RESOLUTION_DELTA\") ? \"REVIEW_EQUIVALENCE_FAIL_CONFLICT_RESOLUTION\" : \"REVIEW_EQUIVALENCE_FAIL_NEW_DELTA\") : \"REVIEW_EQUIVALENCE_PASS\", reasons };\n+}\n+\n+export function buildEquivalenceReceipt(input) {\n+ const decision = decideReviewEquivalence(input);\n+ return {\n+ schema: REVIEW_EQUIVALENCE_SCHEMA,\n+ repository: input.repository,\n+ pr: input.pr,\n+ previous_base_sha: input.previousBaseSha,\n+ previous_head_sha: input.previousHeadSha,\n+ current_base_sha: input.currentBaseSha,\n+ current_head_sha: input.currentHeadSha,\n+ previous_patch_id: input.previousPatchId,\n+ current_patch_id: input.currentPatchId,\n+ previous_file_manifest_hash: input.previousFileManifestHash,\n+ current_file_manifest_hash: input.currentFileManifestHash,\n+ executable_delta: input.newExecutableLines ? \"present\" : \"none\",\n+ reviewed_sha: input.reviewedSha || input.previousHeadSha,\n+ review_receipt_url: input.previousReceiptUrl || null,\n+ reviewer: input.reviewer || null,\n+ model: input.model || null,\n+ family: input.family || null,\n+ verdict: input.previousVerdict || null,\n+ generated_at: input.generatedAt || new Date().toISOString(),\n+ validator_version: input.validatorVersion || \"review-routing-resilience/v1\",\n+ status: decision.status,\n+ reasons: decision.reasons,\n+ };\n+}\n+\n+export function selectReviewerFallback({ writerFamily, routes = [], requested = \"deepseek\" }) {\n+ const ordered = [requested, ...[\"deepseek\", \"qwen\", \"fable\", \"other\"].filter((item) => item !== requested)];\n+ for (const identity of ordered) {\n+ const route = routes.find((item) => item.id === identity && item.fallbackEligible !== false && item.health === \"healthy\" && item.family !== writerFamily && (APPROVED_REVIEWER_FAMILIES[item.id] || item.approved === true));\n+ if (route) return { requestedReviewer: requested, actualReviewer: route.id, fallbackUsed: route.id !== requested, fallbackReason: route.id === requested ? null : \"REQUESTED_ROUTE_UNAVAILABLE\", familyIndependent: true };\n+ }\n+ return { requestedReviewer: requested, actualReviewer: null, fallbackUsed: false, fallbackReason: \"NO_INDEPENDENT_HEALTHY_ROUTE\", familyIndependent: false };\n+}\n+\n+export function reviewerRouteHealth(route, event = {}) {\n+ const errorClass = event.errorClass && ROUTE_ERRORS.has(event.errorClass) ? event.errorClass : null;\n+ return {\n+ requestedModel: route.requestedModel,\n+ resolvedModel: event.resolvedModel || route.resolvedModel || null,\n+ provider: route.provider,\n+ route: route.route,\n+ healthState: errorClass ? \"unhealthy\" : (event.healthState || \"healthy\"),\n+ lastSuccess: errorClass ? route.lastSuccess || null : (event.at || route.lastSuccess || null),\n+ lastFailure: errorClass ? (event.at || route.lastFailure || null) : route.lastFailure || null,\n+ errorClass,\n+ cooldownUntil: errorClass ? (event.cooldownUntil || null) : null,\n+ fallbackEligibility: route.fallbackEligibility !== false,\n+ };\n+}\n+\n+export function fallbackReceipt(selection, actual = {}) {\n+ return {\n+ requestedReviewer: selection.requestedReviewer,\n+ actualReviewer: selection.actualReviewer,\n+ fallbackUsed: selection.fallbackUsed,\n+ fallbackReason: selection.fallbackReason,\n+ familyIndependent: selection.familyIndependent,\n+ actualFamily: actual.family || null,\n+ verdict: actual.verdict || \"NOT_REVIEWED\",\n+ githubUrl: actual.githubUrl || null,\n+ };\n+}\n+\n+export function packetCompleteness(packet = {}) {\n+ const required = [\"taskGoal\", \"currentBase\", \"currentHead\", \"changedFileManifest\", \"executableDiff\", \"affectedContracts\", \"testEvidence\", \"ciState\", \"priorFindings\", \"requestedVerdict\"];\n+ const missing = required.filter((key) => !packet"
},
{
"index": 12,
"sha256": "c4a1df9426718faef6d19be263cd1752066eece26950efd4f587f45dfec8b2ce",
"content": "[key] || (Array.isArray(packet[key]) && !packet[key].length));\n+ return { complete: missing.length === 0, missing };\n+}\n+\n+export function chunkReviewPacket(files = [], maxChars = 7400) {\n+ const chunks = []; let current = []; let size = 0;\n+ for (const file of files) {\n+ const text = String(file.content || \"\");\n+ if (text.length > maxChars) return { complete: false, chunks: [], reason: `FILE_TOO_LARGE:${file.path}` };\n+ if (current.length && size + text.length > maxChars) { chunks.push(current); current = []; size = 0; }\n+ current.push(file); size += text.length;\n+ }\n+ if (current.length) chunks.push(current);\n+ return { complete: true, chunks: chunks.map((items) => ({ files: items.map((item) => item.path), manifestHash: manifestHash(items.map((item) => item.path)), content: items.map((item) => `# ${item.path}\\n${item.content}`).join(\"\\n\\n\") })) };\n+}\n+\n+export function prepareFableCall({ packet, rebuild }) {\n+ let candidate = packet;\n+ let rebuilt = false;\n+ let check = packetCompleteness(candidate);\n+ if (!check.complete) { candidate = rebuild(candidate, check.missing); rebuilt = true; check = packetCompleteness(candidate); }\n+ return { packet: candidate, rebuilt, ...check, callAllowed: check.complete, maxBlockedContextRetries: 1 };\n+}\n+\n+export function reviewGate({ risk, deterministicGreen, writerFamily, receipt, explicitQuorum = [] }) {\n+ const level = String(risk).toUpperCase();\n+ if (!deterministicGreen) return { pass: false, reason: \"DETERMINISTIC_CHECKS_FAILED\" };\n+ if (level === \"R0\") return { pass: true, reason: \"DETERMINISTIC_ONLY\" };\n+ const independent = receipt && receipt.verdict === \"PASS\" && receipt.familyIndependent && receipt.actualReviewer && receipt.actualFamily !== writerFamily;\n+ if (level === \"R1\") return { pass: true, reason: independent ? \"OPTIONAL_REVIEW_PRESENT\" : \"OPTIONAL_REVIEW\" };\n+ if (level === \"R2\") return { pass: Boolean(independent && receipt.githubUrl), reason: independent && receipt.githubUrl ? \"INDEPENDENT_REVIEWER_PASS\" : \"INDEPENDENT_REVIEW_REQUIRED\" };\n+ const quorumOk = explicitQuorum.length ? explicitQuorum.every((member) => member === receipt?.actualReviewer) : Boolean(receipt?.architectureReviewed && independent);\n+ return { pass: Boolean(independent && receipt?.rollbackPlan && quorumOk), reason: \"R3_ARCHITECTURE_AND_ROLLBACK_REQUIRED\" };\n+}\n"
},
{
"index": 13,
"sha256": "b2ddb02075880ad940638629765de2b47ff989f851c39ab5ffd645fff495a0a6",
"content": "diff --git a/scripts/review-routing-resilience.mjs b/scripts/review-routing-resilience.mjs\nnew file mode 100644\nindex 000000000..0cc885928\n--- /dev/null\n+++ b/scripts/review-routing-resilience.mjs\n@@ -0,0 +1,22 @@\n+#!/usr/bin/env node\n+import { readFileSync } from \"node:fs\";\n+import { buildEquivalenceReceipt, decideReviewEquivalence, packetCompleteness, selectReviewerFallback } from \"./lib/review-routing-resilience.mjs\";\n+\n+function input(argv) {\n+ const file = argv.indexOf(\"--input\");\n+ if (file === -1 || !argv[file + 1]) throw new Error(\"--input <json-file> is required\");\n+ return JSON.parse(readFileSync(argv[file + 1], \"utf8\"));\n+}\n+\n+try {\n+ const payload = input(process.argv.slice(2));\n+ const action = payload.action || \"equivalence\";\n+ const result = action === \"equivalence\" ? { decision: decideReviewEquivalence(payload), receipt: buildEquivalenceReceipt(payload) }\n+ : action === \"fallback\" ? selectReviewerFallback(payload)\n+ : action === \"packet\" ? packetCompleteness(payload.packet)\n+ : (() => { throw new Error(`unknown action ${action}`); })();\n+ console.log(JSON.stringify(result, null, 2));\n+} catch (error) {\n+ console.error(`review-routing-resilience: ${error.message}`);\n+ process.exitCode = 2;\n+}\n"
},
{
"index": 14,
"sha256": "aa1970f0ea441f26d974437fdf6e8b6b3c17ba426883ab476ca246a072018354",
"content": "diff --git a/scripts/review-routing-resilience.test.mjs b/scripts/review-routing-resilience.test.mjs\nnew file mode 100644\nindex 000000000..4dd6ba708\n--- /dev/null\n+++ b/scripts/review-routing-resilience.test.mjs\n@@ -0,0 +1,99 @@\n+import test from \"node:test\";\n+import assert from \"node:assert/strict\";\n+import { spawnSync } from \"node:child_process\";\n+import { mkdtempSync, writeFileSync, rmSync } from \"node:fs\";\n+import { tmpdir } from \"node:os\";\n+import path from \"node:path\";\n+import { decideReviewEquivalence, buildEquivalenceReceipt, selectReviewerFallback, packetCompleteness, prepareFableCall, reviewGate, reviewerRouteHealth, fallbackReceipt, reviewRequestDecision, chunkReviewPacket } from \"./lib/review-routing-resilience.mjs\";\n+\n+const equivalent = { previousFileManifestHash: \"a\", currentFileManifestHash: \"a\", previousPatchId: \"p\", currentPatchId: \"p\", previousVerdict: \"PASS\", previousReceiptUrl: \"https://github.test/receipt\", currentCiGreen: true };\n+test(\"base refresh carries a PASS review only for identical executable scope\", () => assert.equal(decideReviewEquivalence(equivalent).status, \"REVIEW_EQUIVALENCE_PASS\"));\n+test(\"equivalence receipt is machine-readable and carries both SHA pairs\", () => {\n+ const receipt = buildEquivalenceReceipt({ ...equivalent, repository: \"deskbuilder/smartsql\", pr: 3360, previousBaseSha: \"b0\", previousHeadSha: \"h0\", currentBaseSha: \"b1\", currentHeadSha: \"h1\", reviewer: \"fable\", model: \"claude\", family: \"claude\" });\n+ assert.equal(receipt.schema, \"smartsql-review-equivalence:v1\"); assert.equal(receipt.current_head_sha, \"h1\"); assert.equal(receipt.status, \"REVIEW_EQUIVALENCE_PASS\");\n+});\n+test(\"code, conflict, receipt and CI deltas refuse carry-forward\", () => {\n+ assert.match(decideReviewEquivalence({ ...equivalent, currentPatchId: \"next\" }).status, /FAIL_NEW_DELTA/);\n+ assert.equal(decideReviewEquivalence({ ...equivalent, conflictResolutionDelta: true }).status, \"REVIEW_EQUIVALENCE_FAIL_CONFLICT_RESOLUTION\");\n+ assert.match(decideReviewEquivalence({ ...equivalent, previousReceiptUrl: \"\" }).status, /FAIL_NEW_DELTA/);\n+ assert.match(decideReviewEquivalence({ ...equivalent, currentCiGreen: false }).status, /FAIL_NEW_DELTA/);\n+});\n+test(\"fallback skips unavailable families and records requested versus actual\", () => {\n+ const result = selectReviewerFallback({ writerFamily: \"qwen\", requested: \"deepseek\", routes: [{ id: \"deepseek\", family: \"deepseek\", health: \"unavailable\" }, { id: \"fable\", family: \"claude\", health: \"healthy\" }] });\n+ assert.deepEqual(result, { requestedReviewer: \"deepseek\", actualReviewer: \"fable\", fallbackUsed: true, fallbackReason: \"REQUESTED_ROUTE_UNAVAILABLE\", familyIndependent: true });\n+});\n+test(\"route health preserves the requested route and classifies retryable outage\", () => {\n+ const health = reviewerRouteHealth({ requestedModel: \"deepseek/deepseek-v4-pro\", provider: \"openrouter\", route: \"runtime-session/complete\" }, { errorClass: \"MODEL_UNAVAILABLE\", at: \"2026-07-12T00:00:00Z\", cooldownUntil: \"2026-07-12T00:01:00Z\" });\n+ assert.equal(health.errorClass, \"MODEL_UNAVAILABLE\"); assert.equal(health.fallbackEligibility, true);\n+ const receipt = fallbackReceipt({ requestedReviewer: \"deepseek\", actualReviewer: \"qwen\", fallbackUsed: true, fallbackReason: \"REQUESTED_ROUTE_UNAVAILABLE\", familyIndependent: true }, { family: \"qwen\", verdict: \"PASS\", githubUrl: \"https://github.test/r\" });\n+ assert.equal(receipt.actualReviewer, \"qwen\"); assert.equal(receipt.fallbackUsed, true);\n+});\n+test(\"packet preflight rebuilds before one bounded Fable call\", () => {\n+ const incomplete = { taskGoal: \"x\" };\n+ assert.equal(packetCompleteness(incomplete).complete, false);\n+ const prepared = prepareFableCall({ packet: incomplete, rebuild: () => ({ taskGoal: \"x\", currentBase: \"b\", currentHead: \"h\", changedFileManifest: [\"a\"], executableDiff: \"d\", affectedContracts: [\"c\"], testEvidence: \"t\", ciState: \"green\", priorFindings: \"none\", requestedVerdict: \"PASS\" }) });\n+ assert.equal(prepared.callAllowed, true); assert.equal(prepared.rebuilt, true); assert.equal(prepared.maxBlockedContextRetries, 1);\n+});\n+test(\"R2 needs one GitHub-published independent reviewer, not fixed models\", () => {\n+ const receipt = { verdict: \"PASS\", familyIndependent: true, actualReviewer: \"fable\", actualFamily: \"claude\", githubUrl: \"https://github.test/r\" };\n+ assert.equal(reviewGate({ risk: \"R2\", deterministicGreen: true, writerFamily: \"qwen\", receipt }).pass, true);\n+ assert.equal(reviewGate({ risk: \"R2\", deterministicGreen: true, writerFamily: \"qwen\", receipt: { ...receipt, actualFamily: \"qwen\" } }).pass, false);\n+});\n+test(\"body-only and evidence-only changes retain product equivalence\", () => {\n+ assert.equal(decideReviewEquivalence(equivalent).status, \"REVIEW_EQUIVALENCE_PASS\");\n+ assert.equal(decideReviewEquivalence({ ...equivalent, newExecutableLines: false }).status, \"REVIEW_EQUIVALENCE_PASS\");\n+});\n+test(\"DeepSeek outage falls through to Qwen\", () => {\n+ assert.equal(selectReviewerFallback({ writerFamily: \"claude\", requested: \"deepseek\", routes: [{ id: \"deepseek\", family: \"deepseek\", health: \"unavailable\" }, { id: \"qwen\", family: \"qwen\", health: \"healthy\" }] }).actualReviewer, \"qwen\");\n+});\n+test(\"Qwen outage falls through to DeepSeek\", () => {\n+ assert.equal(selectReviewerFallback({ writerFamily: \"claude\", requested: \"qwen\", routes: [{ id: \"qwen\", family: \"qwen\", health: \"unavailable\" }, { id: \"deepseek\", family: \"deepseek\", health: \"healthy\" }] }).actualReviewer, \"deepseek\");\n+});\n+test(\"same writer family never counts as independent fallback\", () => {\n+ assert.equal(selectReviewerFallback({ writerFamily: \"qwen\", requested: \"deepseek\", routes: [{ id: \"deepseek\", family: \"deepseek\", health: \"unavailable\" }, { id: \"qwen\", family: \"qwen\", health: \"healthy\" }] }).actualReviewer, null);\n+});\n+test(\"packet still blocks a model call after failed rebuild\", () => {\n+ assert.equal(prepareFableCall({ packet: {}, rebuild: () => ({}) }).callAllowed, false);\n+});\n+tes"
},
{
"index": 15,
"sha256": "e0c7770885c373c427c072f3ec6079adcb6f32a8ff5d915507227b6dc192883b",
"content": "t(\"R0 and R1 never require a named model\", () => {\n+ assert.equal(reviewGate({ risk: \"R0\", deterministicGreen: true }).pass, true);\n+ assert.equal(reviewGate({ risk: \"R1\", deterministicGreen: true }).pass, true);\n+});\n+test(\"R2 rejects missing independent GitHub receipt\", () => {\n+ assert.equal(reviewGate({ risk: \"R2\", deterministicGreen: true, writerFamily: \"qwen\", receipt: { verdict: \"PASS\", familyIndependent: true, actualReviewer: \"deepseek\", actualFamily: \"deepseek\" } }).pass, false);\n+});\n+test(\"R3 requires architecture and rollback proof\", () => {\n+ const receipt = { verdict: \"PASS\", familyIndependent: true, actualReviewer: \"fable\", actualFamily: \"claude\", githubUrl: \"https://github.test/r\" };\n+ assert.equal(reviewGate({ risk: \"R3\", deterministicGreen: true, writerFamily: \"qwen\", receipt }).pass, false);\n+ assert.equal(reviewGate({ risk: \"R3\", deterministicGreen: true, writerFamily: \"qwen\", receipt: { ...receipt, architectureReviewed: true, rollbackPlan: true } }).pass, true);\n+});\n+test(\"explicit R3 quorum remains exact\", () => {\n+ const receipt = { verdict: \"PASS\", familyIndependent: true, actualReviewer: \"fable\", actualFamily: \"claude\", githubUrl: \"https://github.test/r\", architectureReviewed: true, rollbackPlan: true };\n+ assert.equal(reviewGate({ risk: \"R3\", deterministicGreen: true, writerFamily: \"qwen\", receipt, explicitQuorum: [\"fable\"] }).pass, true);\n+ assert.equal(reviewGate({ risk: \"R3\", deterministicGreen: true, writerFamily: \"qwen\", receipt, explicitQuorum: [\"deepseek\"] }).pass, false);\n+});\n+test(\"model outage remains a route event, not an owner blocker\", () => {\n+ assert.equal(reviewerRouteHealth({ requestedModel: \"qwen\", provider: \"openrouter\", route: \"runtime-session/complete\" }, { errorClass: \"MODEL_UNAVAILABLE\" }).healthState, \"unhealthy\");\n+});\n+test(\"stale review and red CI are rejected before carry-forward\", () => {\n+ assert.match(decideReviewEquivalence({ ...equivalent, previousVerdict: \"STALE\" }).status, /FAIL/);\n+ assert.match(decideReviewEquivalence({ ...equivalent, currentCiGreen: false }).reasons.join(\",\"), /CURRENT_CI_NOT_GREEN/);\n+});\n+test(\"duplicate review is deduped and stale head is rejected\", () => {\n+ const request = { repository: \"deskbuilder/smartsql\", pr: 3360, headSha: \"current\", reviewer: \"fable\", packetHash: \"p\" };\n+ const first = reviewRequestDecision({ request, currentHead: \"current\" });\n+ assert.equal(first.accepted, true);\n+ assert.equal(reviewRequestDecision({ request, currentHead: \"current\", seen: new Set([first.key]) }).reason, \"DUPLICATE_REQUEST\");\n+ assert.equal(reviewRequestDecision({ request: { ...request, headSha: \"old\" }, currentHead: \"current\" }).reason, \"STALE_HEAD\");\n+});\n+test(\"CLI emits a receipt without calling a provider\", () => {\n+ const dir = mkdtempSync(path.join(tmpdir(), \"review-routing-\")); const file = path.join(dir, \"input.json\");\n+ writeFileSync(file, JSON.stringify({ ...equivalent, repository: \"deskbuilder/smartsql\", pr: 3360, previousBaseSha: \"b0\", previousHeadSha: \"h0\", currentBaseSha: \"b1\", currentHeadSha: \"h1\" }));\n+ const run = spawnSync(process.execPath, [\"scripts/review-routing-resilience.mjs\", \"--input\", file], { cwd: process.cwd(), encoding: \"utf8\" }); rmSync(dir, { recursive: true, force: true });\n+ assert.equal(run.status, 0); assert.equal(JSON.parse(run.stdout).receipt.schema, \"smartsql-review-equivalence:v1\");\n+});\n+test(\"oversized review input is rebuilt into deterministic bounded file chunks\", () => {\n+ const chunks = chunkReviewPacket([{ path: \"a\", content: \"a\".repeat(4000) }, { path: \"b\", content: \"b\".repeat(4000) }], 7400);\n+ assert.equal(chunks.complete, true); assert.equal(chunks.chunks.length, 2); assert.match(chunks.chunks[0].manifestHash, /^[a-f0-9]{64}$/);\n+ assert.equal(chunkReviewPacket([{ path: \"huge\", content: \"x\".repeat(7401) }]).complete, false);\n+});\n"
}
],
"affectedContracts": [
"docs/agent-governance/RISK_AND_REVIEW_MODES.md",
"scripts/agent-copilot.mjs",
"scripts/agent-copilot.test.mjs",
"scripts/agent-run",
"scripts/agents/committee.py",
"scripts/agents/committee_review_routing_test.py",
"scripts/lib/review-routing-resilience.mjs",
"scripts/review-routing-resilience.mjs",
"scripts/review-routing-resilience.test.mjs"
],
"testEvidence": "35 targeted Node tests and committee routing unittest pass locally",
"ciState": "pending",
"priorFindings": "prior bounded packet lacked chunk payloads; every declared chunk now has a deterministic payload excerpt",
"requestedVerdict": "PASS|NEEDS_FIX|BLOCKED",
"complete": true,
"missing": []
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment