Commit bd0c5cc
fix(engine): a failed
* fix(metadata-protocol): a failed sys_organization probe is not 'no sole organization'
`SeedLoaderService.resolveSoleOrganizationId()` sat behind a bare `catch {}`
whose comment named ONE benign cause while the catch swallowed every cause. A
dropped connection, a timeout or a permission refusal all arrived at the caller
as `undefined` — the 'genuinely ambiguous' verdict — so `load()` stamped no
`organization_id` and every business seed row of that run landed org-less,
invisible afterwards under strict org-scoping, with the seed report's `errors`
field never touched.
Discriminate by error TYPE through the shared `isMissingTableError` predicate,
which is the repair PR #9817 already landed on the sibling probe
(`ObjectQL.probeInstallOrganizations`) with this exact shape. Only an
unprovisioned table is truthful emptiness; everything else propagates.
Part of #12852
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
* fix(objectql): a failed sys_organization tenant scan does not decide a retention window
`LifecycleService.loadGovernance()` filled `snapshot.tenantOverrides` — the
ADR-0057 3.2 per-tenant retention/expiry window set — behind a bare `catch {}`
whose comment named one benign cause while the catch swallowed every cause. On
any read failure the map came back EMPTY, which `reap()` and `archiveObject()`
read as 'this deployment has tuned no tenant': every tenant fell back to the
global window, so a tenant configured to retain LONGER had its rows expired
early. Nothing reported it — the snapshot has no field for an incomplete tenant
pass and the catch logged nothing.
Discriminate by error TYPE through the shared `isMissingTableError` predicate.
An unprovisioned `sys_organization` really is 'no tenant overrides', so a
single-tenant kernel is unchanged; every other cause aborts the sweep before any
policy is applied. For a deletion action, not acting on incomplete evidence is
the correct failure direction.
The abort is reported, not thrown: one `report.errors` entry per declared object
plus a warn. `sweep()`'s declared contract is that it never throws, and the
scheduler enters it as `void this.sweep()` where a rejection would be unhandled
— the objection #8906 recorded when it declined to rethrow from
`checkGovernance` one method below.
Part of #12853
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
* test(ablation-A): prediction before mutating ONLY the #12852 seam
Ablation A reverts the seed-loader narrowing (packages/metadata-protocol/src/
seed-loader.ts, resolveSoleOrganizationId) back to a bare swallow, leaving the
#12853 seam untouched.
PREDICTION, committed before the mutation runs:
- Direction: RED. Not 'fewer diagnostics' and not a reversal — the narrowing is
the only thing that turns a non-benign read failure into a propagated error,
and three pins assert exactly that.
- seed-loader-sole-organization-read-failure.test.ts: 3 of 9 FAIL —
'a dropped connection surfaces that error and writes NO org-less rows',
'a permission refusal surfaces the same way',
'a missing COLUMN on an existing sys_organization stays loud (the superstring
case)'. Each fails inside the rejection() helper: the load RESOLVES instead of
rejecting, and the store carries an org-less widget row.
- The other 6 in that file stay GREEN (two probe controls, the several-orgs
control, both benign phrasings, and the pinned-organizationId non-effect):
none of them reaches a non-benign throw.
- objectql lifecycle-service.test.ts: 109 of 109 stay GREEN. Ablation A must red
ONLY its own pins; a run that reds #12853's pins too proves neither card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
* test(ablation-B): prediction before mutating ONLY the #12853 seam
Ablation B reverts the lifecycle narrowing (packages/objectql/src/lifecycle/
lifecycle-service.ts, loadGovernance's tenant scan) back to a bare swallow,
leaving the #12852 seam untouched. The sweep()-side containment stays in place
deliberately: without the throw it is unreachable, which is exactly what the
pins must detect.
PREDICTION, committed before the mutation runs:
- Direction: RED. The narrowing is the only thing that stops a non-benign read
failure being answered as an empty tenant-override map.
- lifecycle-service.test.ts: 4 of 109 FAIL, all in the #12853 describe —
'a dropped connection aborts the sweep: nothing is read as a candidate and
nothing is deleted' (the reaper reads a candidate page and deletes on the
GLOBAL 30d window instead of the tenant's 90d, and report.errors is empty),
'a permission refusal aborts it the same way',
'a missing COLUMN on an existing sys_organization stays loud (the superstring
case)', and
'the abort is REPORTED, never thrown — sweep() still resolves, and the next
sweep runs' (first.errors is empty and box.deletes is not).
- The other 105 stay GREEN, including this describe's two controls and the
benign unprovisioned-table case: none of them reaches a non-benign throw.
- metadata-protocol seed-loader-sole-organization-read-failure.test.ts: 9 of 9
stay GREEN. Ablation B must red ONLY its own pins.
Ablation A already ran and matched its own prediction: 3 failed / 6 passed in
the #12852 file, 109/109 green in this one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
* test(metadata-protocol): make the new fixture's WHERE matcher liftable, and pin its engine double
Two gate families move on a NEW test file, and both were red before this:
- `pnpm check:where-matcher`: the fixture's matcher was an inline closure the
gate could not lift out of the file ('could not lift: ReferenceError: Cannot
access store before initialization'), so it landed as 1 UNJUDGED — and
'could not run' is a failure, not a pass. Lifted to a module-level
`matchesWhere(row, where)` with no free bindings; behaviour is identical
(flat equality, and a `$`-prefixed combinator is REFUSED rather than read as
a column name). Gate now: 310 discovered, 310 conforming, 194 by refusing,
0 unjudged.
- `pnpm check:engine-double-contract`: the file's engine double is pinned to
the producer's own dispatch predicates, but the shrink-only PINNED ledger did
not know the file, so the pins protected nothing. Recorded via
`node scripts/check-engine-double-contract.mjs --write` — 3 rows added
(delete/findOne/update), 0 lost, and no DEBT-baseline row was touched.
Part of #12852
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
---------
Co-authored-by: Claude <noreply@anthropic.com>sys_organization read is no longer answered as an emptiness — the two genuinely-wrong read seams (#12864)1 parent 6228c1f commit bd0c5cc
7 files changed
Lines changed: 816 additions & 8 deletions
File tree
- .changeset
- packages
- metadata-protocol/src
- objectql/src/lifecycle
- scripts
Lines changed: 42 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
0 commit comments