Skip to content

BF-09 — propose → approve → commit governance (agent cannot approve; denial audited) - #35

Merged
AJ112103 merged 12 commits into
mainfrom
bonfire-BF-09-mrex38kk-6yof
Jul 11, 2026
Merged

BF-09 — propose → approve → commit governance (agent cannot approve; denial audited)#35
AJ112103 merged 12 commits into
mainfrom
bonfire-BF-09-mrex38kk-6yof

Conversation

@AJ112103

Copy link
Copy Markdown
Contributor

BF-09 — propose → approve → commit governance (agent cannot approve; denial audited)

Closes the core AI-safety boundary: an agent actor can only propose; any approve/commit by a non-clinician is default-deny, the denial is written to the BF-05 tamper-evident audit chain, a clinician approves, and an approved proposal commits to an immutable signed-note record. This makes BF-08's propose_resource honest — that MCP write tool was an ungoverned live commit; it now stages a proposal that reaches the canonical FHIR store only through clinician approve + commit.

What landed

  • Event-sourced governance substrate (drizzle/0012_governance.sql): governance_proposal / governance_event / governance_signed_note, all FORCE-RLS + BP-019 tenant-scoped keys + BP-018 append-only (S/I grant, belt REVOKE U/D). Immutability is a privilege-layer property — there is no UPDATE path at all — not app logic. UNIQUE(practice, proposal, action) pins at most one approve + one commit event; the signed-note UNIQUE(practice, proposal) pins one note.
  • Pure core (packages/core/src/governance/): a default-free exhaustive transition() state machine (proposed→approved→committed + rejected; illegal edges are typed errors); a total, fail-closed decideGovernance() (whole body incl. safeParse in try/catch; a throwing-getter actor yields a deny receipt, never a throw); a store layer whose audit contract is exactly zero-or-one chain row per attempt, appended only once the outcome is known and immediately before its mutation (same tx), with the bound practice sourced from the GUC (never caller input).
  • Agent/human surfaces: SDK proposeResource re-pointed to stage a proposal (never a live write); the MCP propose_resource tool re-skinned to honest staging with the frozen 3-tool ALLOWLIST unchanged (no approve/commit/reject tool); three API routes (POST /governance/proposals, /:id/approve, /:id/commit) whose actor is derived only from the authenticated identity + membership.
  • 6 Stage-2 evals (loop/evals/bf09.jsonl) driving real product builds across the harness↔product firewall via scripts/governance-demo/.

How the three named danger classes are structurally closed

  • propose-only-broken: no approve/commit tool on the MCP allowlist (pin test + eval), no approve/commit SDK method, and decideGovernance default-denies approve/commit for every non-clinician. Honest staging is pinned (a propose leaves zero fhir_resources rows).
  • fail-open-authz: authority is total and default-deny — malformed input, unknown role, a throwing-getter actor, and any non-clinician all deny; a deny is an err value (it commits its audit row), never a swallowed throw.
  • audit-bypass: every governance decision reaches the chain only through the audit public API (no-direct-audit-insert ast-grep guard, inversion-proven); a denied approval writes a hash-chain-linked deny row (actor + Governance.approve + reason); an allow event's audit_row_hash JOINs its allow row.

Adversarial verification

  • 2 independent Opus design reviews + a feasibility critic BEFORE code: found 2 blocking seam flaws (phantom allow rows under withTenant's err-value-commit semantics; practice-mismatch denials tripping the audit mis-attribution throw) + one fail-open (throwing-getter parse) — all designed out pre-implementation.
  • Opus security-unit hardening pass: three units clean; one real defense-in-depth gap closed — the store parsed the untrusted actor twice, so a value-shifting getter could split audit attribution from the persisted proposer across the audit_row_hash JOIN; fixed by a single-read frozen snapshot (audit-actor === state-actor), with a load-bearing pinning test.
  • 9 danger-class inversions (mutate→RED→restore, tree restored byte-identical): every guard/test/eval is load-bearing — widening the clinician rule, adding a 4th MCP tool, writing FHIR at propose, parse-failure-allows, inverting role equality, removing the try/catch, dropping the deny audit append, a direct insert into audit_log, and a live GRANT UPDATE/DELETE on the signed note each went RED and GREEN on restore. The pass also surfaced a real CI-blocking gate gap (an eval's sql.unsafe(variable) tripped the repo-wide raw-SQL ratchet) — fixed.
  • 5-agent review panel (verifier + Opus security-auditor + 3 Opus danger-class refuters + synthesis): zero blocking/HIGH/MED defects; all three danger classes independently refuted, two by live adversarial drives on the DB (agent/biller approve+commit all GOVERNANCE_FORBIDDEN with fhir_resources unchanged; 16 malformed/__proto__/value-shifting-getter actors all fail closed; a concurrent double-commit serialized to exactly one commit with the loser leaving zero audit rows). One LOW acted on (the explicit tenant predicate below); the rest are the disclosed residuals + close-out ratchet candidates listed here.

Verification

  • Core governance 59 tests; MCP 15; API 24; loop eval --slice BF-09 6/6; full eval corpus 35/35.
  • turbo gate + loop gate (biome/eslint/ast-grep/depcruise/semgrep/gitleaks/knip/jscpd) green.
  • Local stack is native PG17 (the Docker daemon needs a human click on its startup dialog after the earlier disk-exhaustion VM reset); the single local red is the repo's own "Postgres major version is 18+" catalog guard — the strict PG18 proof is CI's, which boots its own PG18 + pgvector stack.

Disclosed off-floor operator waves (BF-04/05/13 precedent)

The maker floor is packages/core/src/governance/** + packages/mcp/src/** + apps/api/src/routes/governance/** + tests + drizzle/** + evals. These edits are outside it, by design, and disclosed:

  • packages/sdk/** (ops/ir/generated/api.md) — the proposeResource re-point (one identity boundary, not two).
  • packages/core/src/db/fhir-rls.test.ts — posture matrix += the 3 governance tables.
  • sgrules/no-direct-audit-insert.yml (+ test) — acceptance-H2 — single-source agent generators (.claude + .codex) with drift test #5 structural guard (ratchet candidate).
  • loop/** + scripts/governance-demo/** + knip.json — the eval corpus + demo substrate (harness side of the firewall).
  • loop/src/contracts/tasks.ts — dead-path reconcile (loop/evals/governance/** → flat loop/evals/bf09.jsonl, the corpus loader is non-recursive).

Disclosed residuals (v0)

  • A failed commit attempt (a write-layer err value, or an audit-append fault) rolls back its own audit trace — fail-closed both ways (no fhir_resources row, no state advance); the asymmetry vs "every decision audited" is benign because state-without-audit is what a bypass needs and this leaves neither.
  • A denied approve/commit on a missing/foreign proposal id is unaudited (load-before-authorize); no leak — RLS makes a foreign proposal indistinguishable from a missing one, and proposal ids are server-generated.
  • A duplicate resource-id commit surfaces as TENANT_TX_FAILED (23505 rollback), not a typed governance code.
  • membership has no agent role yet, so an agent actor is constructible only at the core boundary; the MCP + API surfaces structurally cannot approve/commit regardless.
  • Greptile required by contract but waived (repo not connected).

Close-out ratchet candidates (follow-up PR, not this one)

  • An ast-grep/sgrule banning writeScribeResource imports outside the write path + governance store (nothing reaches it from an agent surface today; the guard stops a future SDK op/route from re-opening the live-write path).
  • The MCP SDK's own pre-handler validation error reflects caller-argument zod detail (pre-existing BF-08 behavior) — wrap transport validation errors in static text.

🤖 Generated with Claude Code

AJ112103 and others added 12 commits July 10, 2026 18:19
…it-insert sgrule + contract reconcile

Operator prep for the propose->approve->commit governance slice, all OFF the
maker floor by design:

- drizzle/0012_governance.sql: event-sourced, append-only governance substrate
  (governance_proposal / governance_event / governance_signed_note). Verbatim
  BF-01 RLS template + FORCE on all three; BP-019 tenant-scoped keys (every
  key leads with practice_id; row ids server-generated); BP-018 append-only
  posture (S/I grant + belt REVOKE U/D) — committed-note immutability is a
  privilege-layer property, not app logic. UNIQUE (practice, proposal, action)
  pins at most one approve + one commit event per proposal; signed-note UNIQUE
  pins one note per proposal; 'reject' deliberately absent from the action
  CHECK (rejected exists only in the pure state machine in v0). All probed
  live on :55432: app S/I ok, UPDATE/DELETE 42501, cross-tenant + no-GUC reads
  0 rows, double-approve/double-note 23505, reject CHECK violation, FK holds,
  cross-practice insert blocked by WITH CHECK.
- packages/core/src/db/fhir-rls.test.ts: BP-018 posture matrix += the three
  governance tables (append-only in both directions).
- sgrules/no-direct-audit-insert.yml (+ sgrule-tests): acceptance-#5
  structural guard — `insert into audit_log` outside packages/core/src/audit
  fails the gate (product surface only; the loop bf05 chain oracle stays
  independent by design). Inversion-proven: injected violation exits 1, clean
  tree exits 0. Ratchet candidate.
- loop/src/contracts/tasks.ts: dead-path reconcile (BF-08 precedent) — the
  eval corpus loader reads loop/evals/*.jsonl flat, so the contracted
  loop/evals/governance/** subdir could never load; bf09 eval rows will live
  at loop/evals/bf09.jsonl like every other slice.
- loop/memory/state.jsonl: BF-09 active with the full de-risk record (2 Opus
  adversarial design reviews found 2 blocking seam flaws pre-code: B1
  phantom-allow audit rows under withTenant's err-value-commits semantics; B2
  practice-mismatch denials tripping the mis-attribution throw instead of
  auditing — both designed out before the maker runs).

Local verification on the PG17 fallback stack (:55432): turbo gate + core
250 tests green EXCEPT the single repo-own "Postgres major version is 18+"
catalog guard (documented PG17-host artifact; the strict PG18 proof is CI's);
loop tests green; ast-grep 14/14 rule tests; eval corpus 29/0 on main-parity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…57 tests

bonfire-maker output, operator-committed. packages/core/src/governance/**:

- types.ts: GOVERNANCE_ROLES = [...abac ROLES, "agent"], state/action unions
  derived from exported const arrays, GovernanceError codes, signedNoteSchema
  (64-hex commit audit hash), ProposalRecord.
- policy.ts: transition() — default-free exhaustive switch, exactly three
  legal edges (proposed+approve, approved+commit, proposed+reject);
  decideGovernance() — ONE total function, whole body incl. safeParse inside
  try/catch (a throwing-getter actor yields a deny receipt, never a throw),
  single exhaustive switch(action), receipt.resourceType = "Governance."+action,
  bound practice is a parameter fed from the GUC (never caller input).
- store.ts: proposeRecord/approveProposal/commitProposal on one advance()
  seam. The audit contract (designed under review, spot-checked post-build):
  exactly ZERO or ONE appendAuditRowTx call per attempt — deny appends then
  returns an err VALUE (commits, zero mutations); allow appends immediately
  before its mutation inserts (same tx, rollback removes both); ZERO rows on
  invalid input, NOT_FOUND, illegal transitions by authorized actors, and
  write-layer err values. Per-practice governance advisory lock BEFORE the
  proposal load; lock ordering pinned (governance -> audit, never reverse).
  Signed note copies the approver from the approve EVENT row (server truth);
  commit_audit_hash = the commit allow row's chain hash.
- policy.test.ts (45) + store.test.ts (12, live DB, fresh randomUUID
  practices): 12-combo transition grid; deny grid for every non-clinician
  role x approve/commit/reject; hostile throwing-getter actor; honest-staging
  oracle (fhir_resources UNCHANGED at propose); denial audited + chain-linked
  (verifyAuditChainTx + row-hash JOIN); deny-commits vs DB-fault-rolls-back
  double-Result split; immutability (chain row_hashes literally unchanged,
  commit-event count pinned at 1).

Operator finalization: dropped the one dead export the post-build spot-check
flagged (governanceActorSchema from index.ts — the store boundary takes
actor: unknown and parses internally, so no external consumer can need it).

Verified in-worktree: governance 57/57; core 307/308 (the single red is the
documented "Postgres major version is 18+" catalog guard on this PG17 host);
mcp 15, api 18, sdk 18, sql-on-fhir 29, loop 199 all green; loop gate stages
0+1 green; bunx eslint . 0 errors; biome/ast-grep/jscpd/knip/depcruise clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ce now stages, never writes

packages/sdk/** is off the BF-09 maker floor; this is the disclosed operator
edit the slice depends on (BF-04/05/13 precedent for off-floor waves).

- ops.ts opProposeResource: writeScribeResource (live write) -> core
  proposeRecord with actor = the session subject verbatim (id = audited
  actor, role/practiceId = membership row) — the same U2 session-last control
  as every other op, so a caller can never name its own role to the
  governance authority check. Result type: ProposalRecord with
  GovernanceError | WriteError | SdkErrorCode.
- ir.ts doc + regenerated client.gen.ts: the generated TSDoc no longer claims
  the write is live (it stages a proposal requiring clinician approve+commit).
- etc/sdk.api.md regenerated (api-extractor local) for the changed surface.

LOCKSTEP NOTE (disclosed): this commit intentionally leaves packages/mcp red
at src/tools.ts:204 (the propose handler still destructures the old live-write
result). The immediately following maker phase-B commit adapts the MCP surface
(honest staging render + description + inverted tests); the pair lands
adjacently by design. sdk tests: 18/18 green at this commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lockstep pair with the preceding SDK re-point commit (tree green again here).

packages/mcp (extend-never-widen; ALLOWLIST untouched at exactly 3 tools):
- propose_resource handler adapted to the staged ProposalRecord result;
  renderProposeText JSON-encodes every derived field and says what is now
  true: proposal id, state "proposed", clinician approval + commit required
  before anything reaches the canonical FHIR store. Tool description
  rewritten off the old "write is live" claim. ERROR_TEXT gains static
  entries for GOVERNANCE_FORBIDDEN / GOVERNANCE_INVALID_TRANSITION /
  GOVERNANCE_NOT_FOUND (no error.message ever forwarded).
- server.test.ts: the old "propose writes NOW" oracle INVERTED to honest
  staging (fhir_resources unchanged + target id absent; governance_proposal
  +1; audit +1; structuredContent = {proposalId, state} with NO
  versionId/record fields); pin test name-regex widened to
  /approve|commit|reject|sign|sql|shell|fhirpath|exec|file/i on tool NAMES
  only (the honest description legitimately contains "approval").
- render.test.ts: direct-drive injection canary on renderProposeText
  (hostile newline / forged hit line / forged receipt line / forged second
  confirmation line) — e2e cannot carry a hostile char (uuid + literal), the
  BF-08 decorative-guard lesson. Exported from tools.ts only; public api.md
  surface unchanged.
- support.test.ts tenantFootprint += governance_proposal, so every existing
  zero-side-effect oracle now also pins governance rows.

apps/api/src/routes/governance (new surface):
- governanceRoutes(deps) plugin: exactly three POST routes (proposals,
  :id/approve, :id/commit), ONE handler factory; the actor is built ONLY
  from ctx.identity (iss#sub) + ctx.membership (role, practiceId) — request
  body/params are data, never authority; replies carry the typed governance
  Result as the body (runAuthenticated owns HTTP codes).
- routes.test.ts: local JWKS + owner-seeded memberships; clinician full flow
  (body parses signedNoteSchema); biller approve denied + exactly one
  hash-chained deny row (Governance.approve) + zero events; commit-before-
  approve typed error; smuggled role/practiceId body inert; no-Bearer 401
  with zero governance rows.

Verified: typecheck green; mcp 15/15; api 24/24; turbo gate 32/33 (sole red
= the documented PG18 catalog guard on this PG17 host); loop gate stages
individually green (biome/eslint 0 errors/ast-grep/depcruise/semgrep/
gitleaks/knip/jscpd); read-only surface spot-check PASS on all 8 invariants.

Disclosed: (1) INVALID_SCRIBE_INPUT is unreachable through the live MCP
transport (the SDK validates args pre-handler) — its static-text oracle
drives the allowlisted tool's run() directly with a typed stub client.
(2) Pre-existing BF-08 behavior, ratchet candidate: the MCP SDK's own
pre-handler validation error reflects caller-argument zod detail (reflected
input, not stored data). (3) apps/api imports jose via hoisting without
declaring it (pre-existing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… === state)

Adversarial ownership pass over the four security units (default-deny
authority, illegal-transition enforcement, committed-note immutability,
denial-audit wiring). Three units audited CLEAN against unicode/case role
tricks, throwing-getter parse, actor.id receipt-forgery, TOCTOU under the
per-practice advisory lock, cross-referenced audit hashes, and clock
injection. One defense-in-depth gap found and closed:

The store took `actor: unknown` and parsed it TWICE — once inside
decideGovernance (whose receipt.actorId lands in the AUDIT chain) and once in
a re-parse (whose GovernanceActor lands in the governance_event / proposal /
signed-note STATE). A value-shifting `get id()` returning a fresh value per
read could attribute the allowed decision to one identity in the audit row
and record a different proposer in state — a forged split across the exact
`governance_event.audit_row_hash = audit_log.row_hash` JOIN the whole
tamper-evidence story rests on. The old "fail loud" re-parse only caught a
parse FAILURE, not a value DIVERGENCE.

Fix (store.ts): parse the actor ONCE into a frozen snapshot; feed that
snapshot to decideGovernance AND record the same snapshot in state. Invariant:
audit-actor === state-actor, read once. Byte-identical behavior for every
plain-object input and for malformed/throwing-getter inputs (still a deny row
via decideGovernance's own catch). Only the allow-path value-divergence class
is closed, by construction.

Two pinning tests (store.test.ts): (1) a get-id() actor returning
`agent-${reads}` asserts audit_log.actor_id === governance_proposal
.proposer_actor_id — proven load-bearing (RED when the double-parse is
reintroduced, GREEN on the single-read); (2) a duplicate-resource-id second
commit rolls back wholesale (23505) leaving the first signed note + the audit
chain intact.

No shipped caller (SDK/API/MCP) can deliver a getter-bearing actor — the SDK
derives it from the session subject, the API from ctx.identity/membership,
MCP handlers hold only the session-bound client — so this is defense-in-depth
at the core boundary, not a reachable production hole.

Verified: governance 59/59 (was 57); core 309/310 (sole red = PG18 catalog
guard on the PG17 host); mcp+api 39/39; ast-grep 0 findings; jscpd 0 clones;
biome clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rator wave)

Off the maker floor (loop/** + scripts/** are the harness side of the
firewall). The Stage-2 governance evals compose the product across the
loop<->product boundary by SPAWNING these demos, never importing product code.

- scripts/governance-demo/govern.ts: argv-driven propose/approve/commit driver
  — one withTenant transaction per step (mirroring the real request
  lifecycle), prints the flattened per-step Result as one JSON line. The
  governance actor is passed in and parsed by the store, so the
  propose->approve->commit boundary is exercised without a token/JWKS
  (governance authority is decideGovernance, not a membership lookup).
- scripts/governance-demo/mcp-allowlist.ts: prints the frozen MCP tool
  ALLOWLIST names across the firewall. Imports ALLOWLIST from the tool module
  directly (core + zod only — the MCP SDK lives only under packages/mcp and is
  unresolvable from a script); the complementary in-package pin test proves the
  live tools/list a connected client sees equals these names, so this eval
  guards the Stage-2 firewall angle (an ALLOWLIST widened with an approve tool
  fails here even before a transport exists).
- scripts/governance-demo/tsconfig.json: references core + mcp, noEmit.
- loop/src/evals/bf09-governance-util.ts: govern()/mcpToolNames() spawners +
  owner/app postgres clients + expectOk/expectErr + a valid scribe Patient
  factory, shared by the six bf09-* evals.
- knip.json: scripts/governance-demo/** added to the "." entry + project globs.

Both demos smoke-tested live on :55432 (agent propose ok, biller approve
FORBIDDEN, clinician approve->commit->signed note; ALLOWLIST = the 3 frozen
tools). knip exit 0, jscpd 0 clones, depcruise clean, loop typecheck green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… inversion-designed

Off the maker floor (loop/**). Six bf09-* execution evals + the flat
loop/evals/bf09.jsonl corpus rows (the contract's loop/evals/governance/**
path was reconciled to flat in prep — the corpus loader is non-recursive).
Each drives a real product build across the harness<->product firewall via the
governance-demo scripts and asserts with its own owner/app postgres reads;
each is written so a plausible product regression reddens it (inversion stated
in each docstring). Fable authored the 3 behavioral format evals; Opus authored
the 3 security evals.

- bf09-no-approve-commit-mcp-tool (accept #6, propose-only-broken): the MCP
  ALLOWLIST across the firewall is exactly the 3 frozen tools and no name
  matches /approve|commit|reject|sign/i. Reddens if a 4th tool is added.
- bf09-propose-allowed-approve-denied (accept #4/#2, propose-only-broken; the
  BF-08 carry-over): biller AND agent each PROPOSE ok for their own practice
  and are each FORBIDDEN approving it (audited deny, zero events); a clinician
  approves both — only approve/commit are gated. Also proves governance RLS
  scopes proposals to the bound practice.
- bf09-clinician-only-approval (accept #3, fail-open-authz): approve denied for
  biller/operations/researcher/agent AND a malformed actor; clinician approve
  allowed from proposed with exactly one allow row whose row_hash the approve
  event carries.
- bf09-agent-cannot-approve (accept #2, propose-only-broken): agent approve AND
  commit both FORBIDDEN, state unchanged (zero events, no fhir_resources row
  for the proposed id), a later clinician approve still succeeds.
- bf09-governance-denial-audited (accept #5, audit-bypass): a blocked approval
  writes exactly one deny row (agent id, Governance.approve, nonempty reason);
  the whole practice chain re-verifies from the runtime-recomputed genesis; an
  allow event's audit_row_hash JOINs an allow audit row (the hash column is
  load-bearing).
- bf09-committed-note-immutable (accept #7/#8, immutability): after commit,
  re-approve/re-commit both GOVERNANCE_INVALID_TRANSITION and UNAUDITED (chain
  length + signed note unchanged, exactly one commit event); the app RLS role
  cannot UPDATE/DELETE governance_signed_note (42501).
- bf09-stage.ts: a non-eval shared stage-a-proposal helper (jscpd-motivated,
  no jsonl row). bf09-governance-util MRN slice length named (eslint).

Verified: loop eval --slice BF-09 6/6; full corpus 35/35 (was 29); eslint 0
errors on the evals; jscpd 0 clones; loop typecheck green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…st-grep gate)

The adversarial inversion pass caught that bf09-committed-note-immutable drove
its owner/app UPDATE/DELETE probe through `tx.unsafe(statement)` with a
loop-variable statement — which trips the repo-wide no-raw-sql-unsafe (BP-020)
ast-grep rule (it has no path scope, so it lints loop/** too), making
`bunx ast-grep scan` exit 1 (a blocking Stage-1 gate CI would fail on).

Rewrite the forbidden-write probe to run bare tagged-template mutations
(update/delete governance_signed_note) via a closure, so no statement text is
ever passed to sql.unsafe. Same assertion — the app role's missing
UPDATE/DELETE grant still raises 42501 — with the query shape now inside the
raw-SQL ratchet. ast-grep scan exit 0; eval still PASS; loop typecheck + eslint
+ jscpd clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…se gate)

The CI-trap sweep caught that mcp-allowlist.ts imported the deep internal path
packages/mcp/src/tools.ts, tripping depcruise's consumers-use-package-entry-
points rule. The deep import was a workaround for the MCP SDK being
unresolvable from a script — but bun resolves a bare specifier relative to the
importing SOURCE file (server.ts), not the script, so importing the mcp INDEX
entry point resolves the SDK against packages/mcp's own node_modules and runs
standalone. Switch to the entry point; depcruise clean, eval still PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… format check)

The strict slice gate's biome ci flagged govern.ts + the eval util + the
immutability eval (long Result/type signatures wrapped differently than
biome's formatter). Applied biome check --write; no logic change. Slice eval
6/6, loop typecheck green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… clone)

Biome's line-wrapping of the .trim().split().filter().pop() chain turned the
govern()/mcpToolNames() output-parsing blocks into a jscpd clone under the
strict gate. eval-util already exports lastJsonLine (leading-notice tolerant,
fails loud on a non-JSON tail) — reuse it in both, dropping the duplicated
block and the redundant try/catch. jscpd 0 clones; slice eval 6/6; biome ci
clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fense-in-depth)

The 5-agent review panel (verifier + Opus security-auditor + 3 danger-class
refuters) converged on ZERO blocking/HIGH/MED defects — all three danger
classes independently refuted, two by live adversarial drives on :55432
(agent/biller approve+commit all FORBIDDEN with fhir_resources unchanged; 16
malformed/proto/getter actors all fail closed; concurrent double-commit
serialized to exactly one commit with the loser leaving zero audit rows).

Acting on the one LOW finding worth a code change: loadProposal scoped its
proposal + event reads to the tenant via FORCE RLS alone. Added the explicit
`practice_id = (select safe_uuid(current_setting('app.current_practice_id',
true)))` predicate to both reads — redundant with RLS today, but belt over the
suspenders so a future RLS misconfiguration cannot widen the read to another
tenant. Behavior unchanged (RLS already filtered): governance 59/59, slice
eval 6/6, ast-grep + biome clean.

The remaining panel findings are disclosed residuals (an authorized commit
that fails at the write layer, and a denied approve/commit on a missing/foreign
proposal id, are both unaudited but fail-closed with no state change or leak)
and future-proofing ratchet candidates (an sgrule banning writeScribeResource
imports outside the write path) — carried to the close-out, not this PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@AJ112103
AJ112103 merged commit 2970dab into main Jul 11, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant