Skip to content

Parse nested JSONB job-status RPC result before completion gate checks - #156

Merged
BigSimmo merged 4 commits into
mainfrom
copilot/parse-json-status-rpc-result
Jul 2, 2026
Merged

Parse nested JSONB job-status RPC result before completion gate checks#156
BigSimmo merged 4 commits into
mainfrom
copilot/parse-json-status-rpc-result

Conversation

CopilotAI commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

completeJob assumed select * from ... returned top-level ok/gate_passed, but for JSONB-returning RPC shapes the payload can be nested under the function-name column, making rows[0]?.ok undefined and falsely routing successful jobs into failure handling.

  • Status RPC parsing hardening

    • Added parseJobStatusRpcResult(row, rpcName) in supabase/functions/indexing-v3-agent/behavior.ts.
    • Supports both response shapes:
      • direct row columns ({ ok, gate_passed, ... })
      • nested function column ({ complete_strict_enrichment_job: { ok, gate_passed, ... } })
    • Normalizes missing: null to [].
  • Completion path update

    • supabase/functions/indexing-v3-agent/index.ts now parses the first RPC row before evaluating ok and gate_passed.
  • Focused regression coverage

    • Extended tests/indexing-v3-agent.test.ts with parser tests for direct row, nested JSONB row, and missing: null.
constresult=parseJobStatusRpcResult(rows[0],"complete_strict_enrichment_job");if(!result?.ok||!result.gate_passed){thrownewError(/* blocked payload */);}

Verification

  • npm run verify:cheap
  • npm run verify:ui when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed
  • npm run verify:release before release or handoff confidence claims
  • npm run format:check
  • npm run check:production-readiness when clinical workflow, privacy, environment, Supabase, source governance, or deployment behavior changed
  • npm run check:deployment-readiness when deployment startup, hosting, or rollout behavior changed

Clinical Governance Preflight

Complete this section when the change touches ingestion, answer generation, search/ranking, source rendering, document access, privacy, production env, or clinical output.

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes

  • Scope is intentionally narrow: worker-side RPC result decoding + targeted tests; no schema, privilege, or workflow contract changes.

CopilotAI changed the title [WIP] Parse the JSON status RPC result before checking okParse nested JSONB job-status RPC result before completion gate checksJul 2, 2026
CopilotAI requested a review from BigSimmoJuly 2, 2026 10:11
@BigSimmo
BigSimmo marked this pull request as ready for review July 2, 2026 10:29
@BigSimmo
BigSimmo enabled auto-merge July 2, 2026 10:29
@BigSimmo
BigSimmo merged commit 627c54e into mainJul 2, 2026
4 checks passed
@BigSimmo
BigSimmo deleted the copilot/parse-json-status-rpc-result branch July 2, 2026 16:31
BigSimmo added a commit that referenced this pull request Jul 31, 2026
#141 closed on the production defect, but --primary-hover and --success-hover
are still undefined on main (referenced at favourites-library-redesign-page.tsx
:203 and :217, zero definitions in the stylesheet). Impact is low — mockups are
gate-exempt design scratch — but with #141 archived nothing in the ledger owned
them, and .design-sync/NOTES.md is prose in a tooling file, not a tracked item.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BigSimmo pushed a commit that referenced this pull request Jul 31, 2026
… rows
CodeRabbit found row #165 claiming its follow-ups "were renumbered again to
#156/#157/#158" — ids that now belong to unrelated rows on main. A reference
that resolves to the wrong row is worse than none in a ledger whose whole
job is to be followable later.
Two fixes:
- Dropped the renumbering trail entirely rather than updating it. It listed
superseded ids that main has since reissued, and it went stale again on
the very next sync; the row now states that renumbering happened and why,
without naming numbers that no longer mean anything.
- Corrected a second stale reference the earlier renumber missed: row #165
pointed at #157 for the privacy sign-off that variant F would need. That
is the answer-mode caveat row, now #166. The earlier pass only rewrote
three known cross-reference phrasings and did not catch this one.
Verified by extracting every backtick id reference from the three rows: #165
and #166 now point at each other and #167 at #120, all live.
check:outstanding-issues: 165 rows (50 open, 115 archived), unique ids,
next-id=168 above the highest, no ids deleted from base 3b4872f.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPyFcMfn1jMmphr6AqiWBg
BigSimmo pushed a commit that referenced this pull request Aug 5, 2026
…sign
Four conflicts, resolved as the trial merge recorded in the PR body predicted.
`search-screen.tsx` reduced to the handler alone, exactly as forecast: both
sides had independently arrived at `--text-muted`, so the only disagreement
left was `clearSearchFilters` (here) versus `clearSearch` (main). Kept this
branch's — #1616 branched before defect #1611's sibling was fixed, and
`clearSearch` on a control labelled `Clear` inside a filter row deletes the
query the reader is looking at.
`search-results-header-band.tsx` (2 hunks) took this branch: main's side is
the pre-restructure shelf carrying only the token migration this branch had
already applied.
`document-search-results.tsx` (3 hunks): the two empty-state hunks took this
branch's shared `SearchResultsEmptyState`. The import hunk is a genuine merge —
v2 renamed `metadataPill` to `metadataPillDensity`, and the auto-merged body now
calls `metadataPillDensity.roomyCompact`, so the import must follow. `EmptyState`
is dropped from it because both of v2's call sites are the ones this branch
replaced.
`docs/outstanding-issues.md`: #1616 minted its own #237 and #238 from the same
`next-id`, so both sides landed rows under both numbers. Renumbered *this*
branch's to #246 and #247 and bumped the marker to 248, rather than taking one
side wholesale — main's rows are already landed and referenced. This is the
collision `#156` and `#168` predict; the guard caught it.
Two v2 gates then failed on the merged tree and are now satisfied:
- `check:design-system-contract` ratchets `textSoftConsumers` per file, and v2
had driven both of these to zero. The four remaining usages here are icons and
glyphs, which this branch had deliberately left on `--text-soft` because v1
defined no decoration token. v2 defines `--decoration-soft`, so that
constraint is gone and they move there — the tier v2 itself uses for the same
nodes.
- The generated adoption manifest and COMPONENTS.md maturity section went stale
as a result; regenerated with `design-system:adoption:update`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qm3i3eLCDcwytzge1sKM4a
BigSimmo added a commit that referenced this pull request Aug 9, 2026
…1786)
* feat(design-system): build ErrorState, the gate with nothing behind it
GATES.md §3 lists the prohibition "Render '0 matches' after a failed
request" with the gate "ErrorState adoption + check", status planned.
Measured at origin/main 199b303, ErrorState existed nowhere in src or
tests — only in COMPONENTS.md, GATES.md and SPEC.md. This builds it.
The invariant is clinical, not cosmetic. A search that failed has no count
to report, so reporting zero is a false statement about the corpus: on the
services page "0 matches" asserts there are no crisis services when the
search never ran, and on favourites it reads as "you have saved nothing"
rather than "we could not load them". COMPONENTS.md:322 draws the same line
from the other side — "no result count is available" is not a MissingValue.
The component therefore takes no count and no children. There is no prop
through which a number can arrive, and the generated dtsPropsFor entry now
records that as the published API. The one remaining route, a caller writing
a count into title or body, is covered by a development-time tripwire that
matches a figure against a counted noun ("0 matches", "no results") so an
error code or a duration does not trip it. It warns and never throws: on the
one screen already reporting a failure, a thrown error is a blank page.
Requirements came from the surfaces that hand-roll this guard today. Three
do, and their comments state the rule outright:
search-results-header-band.tsx:210 ("no number may reach the DOM"),
services-navigator-page.tsx:634 ("a blocked registry must not reach the band
as '0 matches'") and favourites-command-library-page.tsx:1182. They are
correct, just not shared; converting them is a live-look change and
deliberately not here.
Three further sites carried into this task as hand-rolled guards are not
that, measured at this HEAD, and are recorded so the next reader does not
convert them: differentials-home.tsx:716,729 renders "0 matches"/"No
matches" when sourcesChecked is true, i.e. a legitimate zero after a search
that SUCCEEDED; specifiers-home-page.tsx:211 is a comment about not showing
a stale zero above real catalogue results, and lives in
src/components/specifiers/, not clinical-dashboard/; document-search-results
gates on recordStatus for loading, not for a failed count.
Registered per gates 11 and 12: source, design-sync export, preview, prop
contract, publication test entry, behavioural DOM tests, adoption-contract
family, and both generators regenerated (54 components, 59 roots).
Uses the shared floatingControl recipe rather than a hand-rolled control, so
the tap floor, focus ring and forced-colors border come from one owner. Adds
zero arbitrary padding/gap/radius/line-height, so the ratchet landed in the
previous commit is unaffected and the two stay separately revertible.
Scope: ErrorState only. OfflineState, PermissionDeniedState, NotFoundState
and UnavailableState share the pattern but have no gate pointing at them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(design-system): ratchet raw gap literals, the family #1780 left uncovered
#1780 landed rawPaddingLiterals, rawRadiusLiterals and rawLineHeightLiterals
for #262 part 3. Gap was the one remaining spacing surface a hand-picked
value could hide in: gap-[9px] and `gap: 18px` were counted by no ratchet at
all. This adds rawGapLiterals on that commit's own predicate and wiring.
Measured against origin/main ef9bb51: 34 sites across 9 files — 21 Tailwind
utilities, every one under src/components/therapy-compass/, plus 13 CSS
declarations in globals.css that a utility-only scan misses. Covering both
spellings is the same reason #1780 counts both: otherwise a literal escapes by
moving from a class into globals.css.
Kept as its own metric rather than folded into rawPaddingLiterals so the
therapy-compass cleanup can be paid down and re-pinned independently of the
padding debt, which is spread across fifteen unrelated files.
Reuses RAW_LITERAL_VALUE unchanged, so a value containing a CSS function
(env(, clamp(, max(, calc() stays a sanctioned computed form and is exempt.
Also corrects the §3 prohibition row, which #1780 left reading
"implemented-partial (colour/shadow/tap literals only)" and which named none
of the metrics it had just shipped. A row that understates shipped work is
what sends the next session to rebuild it — this change was itself started as
a duplicate of #262 part 3 for exactly that reason.
Mutation-verified in both halves, each naming the metric and the path:
a gap-[3px] utility gives "rawGapLiterals increased from 34 to 35" plus
"at src/components/ui/missing-value.tsx increased from 0 to 1"; a
`gap: 19px` declaration gives the same total plus "at src/app/globals.css
increased from 13 to 14".
Baseline diff is additive only. legacyShadowAliases measures 218 against its
pinned 220 on main; that slack is left exactly as found.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(issues): capture the ErrorState and duplicate-work follow-ups
Four rows (#298-#301), added with npm run issues:add. Ids 295-297 were
claimed by main while this branch was open, which is ledger #156's
read-modify-write race behaving exactly as recorded.
- #298 (P2 task) the ErrorState enforcement check. GATES.md still reads
"planned" for the 0-matches prohibition and nothing in scripts/ or
eslint-rules/ references ErrorState, so the component exists but is not
required. Deliberately not flipped to implemented.
- #299 (P3 task) adopting ErrorState at the three surfaces that genuinely
hand-roll the guard. Live-look change, downstream of the redesign.
- #300 (P2 issue) three sites miscarried into M4 as guards that are not,
so the next reader does not convert them. differentials-home renders its
zero after a search that SUCCEEDED.
- #301 (P3 issue) two sessions built #262 part 3 in parallel because the
§3 row understated what had shipped. Proposes asserting that every
baseline metric key appears in GATES.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(design-system): restrict the ErrorState copy tripwire to development
Codex review on #1786 (P2). The doc comment described the tripwire as
development-only, but the emitter only silenced NODE_ENV === "test", so a
production caller supplying count-bearing title/body copy had the full
caller-provided string written to console.warn. On a clinical surface that
copy can quote the query — "0 results for <query>" — which turns a copy
defect into a disclosure risk. Nobody reads a production browser console
for design-system warnings, so the emit is now development-only and an
unset NODE_ENV is treated as production: fail quiet.
The gate is an exported predicate rather than an inline comparison because
an inline comparison is untestable here. Vite statically replaces
process.env.NODE_ENV inside src/ modules, so under Vitest the check
compiles to `"test" === "development"` and no stubEnv can move it. The
review asked for a production console-spy check; written that way it would
have passed while proving nothing, staying silent for the wrong reason and
continuing to pass even if the guard were deleted. shouldEmitErrorStateDiagnostic
is asserted directly instead — development true; production, test and unset
false — with the console spy kept alongside as the weaker check that catches
an emitter which warns unconditionally.
Verified: typecheck 0 errors, lint 0, format:check 0,
check:design-system-contract 0, Tests 70 passed (70) across
error-state.dom and design-sync-visual-exports.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
CopilotAI added a commit that referenced this pull request Aug 12, 2026
fix#186 archive
- Restore #156 (read-modify-write race + Update-branch corruption) from
archive back to the open issues section with its original full text; the
underlying allocation race is not resolved by the conservative architecture
decision described in the previous commit.
- Re-add #156 to the recommendations priority table as row 51 (between #101
and #172, consistent with its original ordering).
- Correct #101 to credit only metadata and memory parallelisation
(hydrateCandidatesWithMetadataAndMemory) from PR #1474; visual hydration
(attachPageVisualEvidence) is still called serially after hydration on all
six call sites and is explicitly listed as a remaining candidate.
- Update #186 archive text to reflect that only metadata+memory were updated
in #101, not visual hydration.
Co-authored-by: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
BigSimmo pushed a commit that referenced this pull request Aug 12, 2026
docs/outstanding-issues.md has no merge driver by design, so this conflicted
in seven hunks. Resolved by taking main's file as the base — it carries main's
own archives (#207, #226, #230, #261, #284, #296) — then re-applying all 23 of
this branch's changes through scripts/outstanding-issues.mjs: 15 archives and
8 detail rewrites. Verified afterwards that every one landed and that the
guard reports no ids deleted from base.
Two rows are deliberately NOT resurrected. This branch's #302 (style-contract
line-break artifacts) and #303 (issues:next-id sync) no longer exist anywhere
on main under any id — main has since reused both numbers for entirely
different live rows. Both of this branch's rows were verified-delivered trivia,
so nothing actionable is lost, but the disappearance is itself evidence for
#156/#168: a main-side merge dropped rows, which is the hazard those rows
describe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
BigSimmo added a commit that referenced this pull request Aug 13, 2026
… queue text (#1890)
* docs(issues): retire 22 non-actionable ledger rows and correct the #231 queue text
A yield review of all 114 open rows against current main. The queue had become
roughly 60 tasks and 50 notes; this removes the notes and fixes two places where
the ledger was actively misdirecting.
The correction that matters most: the recommended-queue entry for #231, the top
clinical P1, told every session to "measure and fix the fast-route budget /
generation timeout" — an approach #231's own detail records as tested and
rejected, because the decisive 40-second probe completed generation in 25.272s
with route_deadline_exceeded=false and still failed quality. The session-start
hook prints the queue, not the row, so the refuted text was the text agents read.
Closed 22 rows:
- #304 was already done on main (commit d182844 refreshed the ranking
snapshot; generatedAt is 0 days old, not 2026-07-20), yet sat in the queue
advertising a freshness fuse that is not armed.
- #241#244#272#294#300#257 were standing cautions whose own text says
"no action". Each one's knowledge now lives in the code it protects, so
closing the row loses nothing.
- #196-#200 are five steps of the disaster-recovery checklist that is
canonical in docs/operator-backlog.md, with no trigger until a restore.
- #86#188 were index rows over children that are individually findable.
- #250#253#254 were superseded; #250 and #253 say so themselves.
- #156#301#152#236#260 merged into #168, #292 and #169 respectively —
each pair or group was one problem recorded two to four times.
Demoted 20 rows with a stated reason (premature ops for a single-user
prototype, upstream-blocked, measurement-gated, or design-system adoption
competing with an open clinical P1). The Pri cell is unchanged because the
writer has no --pri flag — which is now #313.
Added three rows for mechanism gaps this sweep exposed: rows outliving their
own completion (#312), the missing --pri flag (#313), and the queue being able
to contradict the row it cites with no guard (#314).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuYJz8hauCsCdx8r4fXiZU
* docs(ledger): record the ledger yield review handoff
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuYJz8hauCsCdx8r4fXiZU
* Keep recovery work visible and pin forced colors
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

**<sub><sub>![P1 Badge](https://img.shields.io/badge/P1-orange?style=flat)</sub></sub> Parse the JSON status RPC result before checking ok**

2 participants

@BigSimmo