Repository navigation
Commit 2496415
measure the return-propagating durability seams the log-level gate cannot name (#18523)
Part of #16233 — this PR delivers the CENSUS the triage ordered first.
It does not
pick among the three candidate repairs; #16233 stays open for that
decision.
Clause-②: no
## The gap, verified structurally on this tree
`check-durability-degradation-log-level.mjs` accepts three answers from
a `catch`
guarding a durability-critical operation: rethrow, log at `error`, or
hand the failure
to the caller through its declared propagation vocabulary. Both maps
that spell the
third answer are keyed on the NAME of a callee the catch reaches —
`FAILURE_PROPAGATION_CALLEES` (`:444`) repo-wide,
`FAILURE_PROPAGATION_SITES` (`:513`)
scoped to one `<file>::<function>`, whose values are themselves `[name,
via]` pairs.
`catchDeliversFailure()` proves delivery by `findPropagationCall()` →
`matchesPropagationName(child, d.name)`.
A `catch` that hands the failure back by RETURNING an out-param object
calls nothing.
`return { ok: false, error: err }` reaches no name, so the seam is not
missing a row —
it is inexpressible as the vocabulary is shaped. At such a site the only
ways to green
the gate are the two the file's own header rejects: a baseline entry for
correct code,
or a bolted-on `logger.error` ("the mirror-image failure AGENTS.md warns
about").
This is NOT a live defect. Every seam the gate sees today answers
through a log or a
declared callee — see the tier [1] reading below.
## The measurement
`scripts/measure-return-propagating-durability-seams.mjs`, a MEASUREMENT
and not a
gate (exit 0 on any membership count; deliberately not named
`check:*`/`gen:*`, the
shape `measure-durability-swallow-family.mjs` established).
```
try/catch statements 2944
...guarding a declared durable call 250
...whose catch returns a constructed failure object on EVERY path 21
MEMBERS — the gap's live population 12
[1] the gate SEES this seam today 0
[2] write-shaped (#12981 vocabulary) 4
[3] durable, read on the merits here 8
ADJACENT, not members 5
EXCLUDED by reading 4
```
Tier [1] at zero is the load-bearing reading: the shape costs the
present verdict
nothing, and the 12 sit one vocabulary entry away from becoming visible.
Membership is
four parts — a declared durable call in the `try` (same-tick), EVERY
path out of the
`catch` returning an object CONSTRUCTED IN THE CATCH, that object NAMING
the failure,
and the catch not already being expressible (no rethrow, no
`error`/`fatal` log, no
declared propagation callee, not a declared site). The path analysis
mirrors the
gate's own `catchDeliversFailure()` and reports "cannot prove" as "does
not deliver",
so an unmodelled shape is DROPPED: the instrument's declared direction
of error is to
UNDER-count.
The durability axis is three declared vocabularies, never a spelling.
Two are READ OUT
OF THEIR OWN SOURCE FILES at run time — the gate's
`DURABILITY_CRITICAL_CALLEES` and
#12981's `WRITE_SHAPED_CALLEES` — so this census cannot drift from them,
and a
vocabulary it cannot PARSE is a refusal (exit 2) rather than a shorter
list. The third
is `READ_ON_THE_MERITS`, each entry carrying the reading for why that
operation claims
persistence. `NOT_A_DURABILITY_CLAIM` runs the other way: sites matching
the shape over
a declared write name that claim no persistence, excluded WITH the
reading instead of
filtered silently. Both registers fail `--self-test` when a row stops
matching — which
already paid for itself: an `appendEvent` entry was dropped because the
call sits inside
a nested arrow the same-tick walk correctly refuses to descend into.
## The 12
| site | guards | returns |
|---|---|---|
| `core/src/utils/migration-journal.ts:660` `unwind` | `transaction` |
`status: 'failed', error` |
| `drivers/driver-sql/src/sql-driver.ts:6934` `attemptWithoutPoisoning`
| `transaction` | `ok: false, error` |
| `drivers/driver-turso/src/remote-canonical-backfill.ts:439` |
`execute`, `runBatchedUpdate` | `error` |
| `metadata-protocol/src/migrations/partial-index-probe.ts:390` | `exec`
(CREATE INDEX) | `status`, `detail` |
| `metadata-protocol/src/migrations/partial-index-probe.ts:410` | `exec`
(CREATE INDEX) | `status: 'failed', detail` |
| `metadata-protocol/src/protocol.ts:18272` `publishPackageDrafts` |
`inTxn` | `success: false, failed[]` |
| `plugins/plugin-auth/src/reconcile-membership.ts:230` |
`insertMembership` | `outcome: 'failed'` |
| `services/service-automation/src/builtin/crud-nodes.ts:320`
`create_record` | `insert` | `success: false, error` |
| `services/service-automation/src/builtin/crud-nodes.ts:460`
`update_record` | `update` | `success: false, error` |
| `services/service-automation/src/builtin/crud-nodes.ts:548`
`delete_record` | `delete` | `success: false, error` |
| `services/service-automation/src/engine.ts:2897` `claimAdvance` |
`claimSuspension` | `kind: 'unavailable', message` |
| `services/service-messaging/src/inbox-channel.ts:212` `send` |
`insert` | `ok: false, error` |
Two of them already state the contract in prose. `sql-driver.ts`'s
`attemptWithoutPoisoning` documents "Returns a discriminated result
rather than
rethrowing, so the caller keeps the ORIGINAL error"; `engine.ts`'s
`claimAdvance`
comments "Handed to the CALLER, so not a degradation and deliberately
NOT a log site
(AGENTS.md 'Degradation log levels': a failure the requester was told
about does not
look normal from the outside)". Those are the gate's own third answer,
written by hand
because there is no way to declare it.
## The card's sample seam is gone — read separately from the structural
claim
`packages/metadata/src/migrations/migrate-sys-notification-to-event.ts`
is not on
`main`, and `recordNotificationEventReceipt` is 0 occurrences repo-wide
(control word
`persistSeedTenancyReceiptRow`, same command shape: 1 file). #16194
retired the
ADR-0030 cut-over. So the card's own before/after numbers are not
reproducible and were
not re-derived; the STRUCTURAL claim was re-verified independently and
holds.
## Also here: limitation 4 in the gate's header
The gate enumerates three honest limitations. This adds the fourth — the
boundary above
— pointing at the instrument for the count rather than quoting one, and
recording that
a green over any listed site means NOT MEASURED for that site. It
explicitly does NOT
decide what to do about it: re-keying on the enclosing function, reading
the declared
return TYPE, and recording the shape as out of scope are all open, and
the first moves
the question from "what did this call do" to "who is asking".
## Verification
- `pnpm check:durability-log-level` → exit 0, `36 durability-critical
catch seam(s), all
loud, rethrowing or propagating to the caller (4 propagating, declared)`
and
`68 read seam(s)` — both unchanged by the doc-block edit.
- `node scripts/check-durability-degradation-log-level.mjs --self-test`
→ exit 0,
`57 case(s) passed`.
- `node scripts/measure-return-propagating-durability-seams.mjs
--self-test` → exit 0,
`7 membership fixture(s), 7 READ_ON_THE_MERITS entr(ies) and 4
NOT_A_DURABILITY_CLAIM
row(s) all reached`.
- Gate families derived from the diff with
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`
(32 commands) and all of them run — see the report on #16233 for the
per-command exit
codes.
## Acceptance notes
- `skip-changeset`: verified rather than assumed. Both touched paths are
under
`scripts/`, and no package `files[]` in this workspace ships
`scripts/**` from the
repo root.
- Noted, not filed — `FAILURE_PROPAGATION_SITES`-style keys are
`<file>::<function>`,
and where the enclosing function is large
(`storage-service-plugin.ts::start`) the key
is coarser than the catch. The gate accepts that trade for its own map;
this census
inherits it. Carrier: whoever takes #16233's repair route.
Generated with Claude Code in session
https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
---
_Generated by [Claude Code](https://claude.ai/code)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2882528 commit 2496415
2 files changed
Lines changed: 1008 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
| 61 | + | |
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
79 | 106 | | |
80 | 107 | | |
81 | 108 | | |
| |||
0 commit comments