Created
July 12, 2026 13:28
-
-
Save anatoliyzalogin-png/2aeb42649d2c583a1c9310b4fc764033 to your computer and use it in GitHub Desktop.
SmartSQL REVIEW-ROUTING-RESILIENCE-V1 Fable exact-head packet 7c7d857cc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "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