Skip to content

Commit 81f8b7e

Browse files
yinlianghuiclaude
andauthored
docs(gate): record the read-seam wrapper recursion's callback refusal, measured per seam (#12138) (#12356)
Comment-only. The read-seam census is unchanged at 64 and the self-test's 45 cases are unchanged. `isReadCall`'s wrapper recursion walks a wrapper body with `walkSameTickInclusive`. The top of this file documents that refusal for the `try` side and nowhere for the wrapper side, where the same answer is not obviously right. This records the missing measurement. Re-derived on one named tree (`origin/main` @ 3ddad51, reproduced after merging c312a56): 64 today vs 72 for a `walkAll` probe. The delta is still 8, but reading the 8 one by one shows the callback refusal explains only 3 of them: raising `MAX_READ_WRAPPER_DEPTH` from 2 to 6 with the same-tick walk intact admits the other 5, and saturates. One of the 3 is a FAKE seam — `Set.prototype.delete` resolving by name to this file's own `delete()` method, which is #11921's defect on the wrapper name instead of the vocabulary name. The sanctioned `Array.prototype.map` / `Promise.all` allowlist matches none of the 8, so no code change is made here. Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6 Co-authored-by: claude <noreply@anthropic.com>
1 parent 2491064 commit 81f8b7e

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

scripts/check-durability-degradation-log-level.mjs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,92 @@ const FAILURE_PROPAGATION_SITES = new Map([
967967
// and after. Only the seam count moves, and ⛔ no baseline entry was added: an
968968
// entry there says a human read the seam, not that a rule guessed wrong.
969969

970+
// ── THE WRAPPER RECURSION'S CALLBACK REFUSAL — MEASURED, NOT CHANGED (#12138) ─
971+
//
972+
// `isReadCall`'s wrapper recursion walks a same-file wrapper's body with
973+
// `walkSameTickInclusive`, so a driver read sitting inside a nested function
974+
// body is not seen. The top of this file states the reason the `try` side
975+
// refuses the same descent ("a callback registered inside a try runs later and
976+
// is not guarded by that catch"). That reason is about the TRY. #12138 asked
977+
// whether it is also the right answer for a WRAPPER body, where it is not
978+
// obviously right: a wrapper whose read sits in a SYNCHRONOUSLY invoked
979+
// callback does perform that read on its caller's behalf, inside the caller's
980+
// try. The narrowness was recorded here for the try side only and nowhere for
981+
// the wrapper side; this block is that missing record.
982+
//
983+
// RE-MEASURED on one named tree — `origin/main` @ 3ddad51b5c, and reproduced
984+
// byte-identically (both counts AND all 8 delta seams) after merging
985+
// `origin/main` @ c312a562e3 — because the filing's own table (64 → 72) was
986+
// attributed to a tree that predates #12137 and reported 66 there:
987+
//
988+
// | recognizer | read seams |
989+
// |--------------------------------------------------|-----------:|
990+
// | today — `walkSameTickInclusive`, depth 2 | 64 |
991+
// | probe — `walkAll`, depth 2 | 72 |
992+
//
993+
// The delta is still 8. What the per-seam reading found is that the wrapper
994+
// recursion's callback refusal explains only THREE of those 8.
995+
//
996+
// THE DISCRIMINATING RUN. Raising `MAX_READ_WRAPPER_DEPTH` from 2 to 6 while
997+
// leaving `walkSameTickInclusive` in place admits 5 of the same 8 (70 seams: +8
998+
// / -2, where the 2 are the SAME try lines re-attributed to a different
999+
// first-matching callee, engine.ts:9407 and :10572). Saturation checked at
1000+
// depth 50: 70 and 75, i.e. unchanged. So for those 5 the miss is the DEPTH
1001+
// BOUND, not the callback boundary — `walkAll` merely masks the bound by
1002+
// descending lexically through nested DECLARATIONS instead of counting call
1003+
// hops, which reaches the read at depth 1 no matter how many awaits are between.
1004+
//
1005+
// THE 8 DELTA SEAMS, each read at its call site:
1006+
//
1007+
// | # | seam (try line → wrapper) | why today misses it | invoked now? |
1008+
// |--:|----------------------------------------------------------------|---------------------|--------------|
1009+
// | 1 | metadata-protocol protocol.ts:10243 getMetaItemCached→getMetaItem | depth bound | yes — real |
1010+
// | 2 | metadata-protocol protocol.ts:13559 saveMetaItem→getMetaItem | depth bound | yes — real |
1011+
// | 3 | metadata-protocol protocol.ts:14535 migrateStoredMetadata→saveMetaItem | depth bound | yes — real |
1012+
// | 4 | metadata-protocol protocol.ts:17213 duplicatePackage→saveMetaItem | depth bound | yes — real |
1013+
// | 5 | metadata-protocol sys-metadata-repository.ts:883 promoteDraft→dropPromotedDraftRow | CALLBACK | yes — real |
1014+
// | 6 | metadata-protocol sys-metadata-repository.ts:1353 close→terminate | CALLBACK | NO — FAKE |
1015+
// | 7 | objectql engine.ts:9237 insert→applyAutonumbers | CALLBACK | yes — real |
1016+
// | 8 | objectql lifecycle-service.ts:625 sweep→reapObject | depth bound | yes — real |
1017+
//
1018+
// All 8 were decidable from the call site; none needed provenance. Seams 1-5,
1019+
// 7 and 8 are genuine members the census does not count: every hop is an
1020+
// `await` on the caller's own tick (`getMetaItem` → `findOverlay`/`findDraft` →
1021+
// `lookup` → `engine.findOne`; `delete` → `withTxn(cb)`, which is
1022+
// `engine.transaction(cb)` or `cb(undefined)` and is awaited either way;
1023+
// `seedAutonumber` → `keysetWalk(cb)` driven by the `for await` on the next
1024+
// line; `archiveObject` → `archivePass`, an awaited local const).
1025+
//
1026+
// ⚠️ SEAM 6 IS A FAKE SEAM, AND IT IS THE REASON `walkAll` IS NOT THE FIX.
1027+
// `close()`'s try calls `w.terminate()`. `terminate` resolves BY NAME to the
1028+
// local const arrow at sys-metadata-repository.ts:1246 — a synchronous, void,
1029+
// in-memory routine whose only call is `self.watchers.delete(subscription)` on
1030+
// `private readonly watchers = new Set<...>()`. `calleeName` reads that as
1031+
// `delete`, and the wrapper recursion resolves `delete` to THIS FILE'S
1032+
// `async delete(ref, opts)` method, whose `findOne` lives inside a `withTxn`
1033+
// callback. Ablation: refusing the `delete` wrapper hop drops the probe from 72
1034+
// to exactly 70, removing seams 5 and 6 and nothing else.
1035+
//
1036+
// That is #11921's defect — a callee NAME matched with no shape check — one
1037+
// level up, on the WRAPPER name instead of the vocabulary name.
1038+
// `contradictsDriverReadShape` guards only the `DRIVER_READ_CALLEES` hit above;
1039+
// the wrapper hop has no equivalent. Today it is harmless because the callback
1040+
// refusal stops the walk before the fake read is reached. Widening the walk is
1041+
// what arms it, and a fake seam is the UNSAFE direction: an invented member of
1042+
// the denominator #5186 / #6451 / #9165 / #8845 / #8901 are all quoted against.
1043+
//
1044+
// ⛔ NOTHING WAS CHANGED, and the reason is a measurement rather than caution.
1045+
// The sanctioned cheap fix for this card was an `Array.prototype.map` /
1046+
// `Promise.all` allowlist. NOT ONE of the 8 goes through `map` or
1047+
// `Promise.all`: the real shapes are `withTxn(cb)`, `keysetWalk(cb)` (imported
1048+
// from `@objectstack/types`, so its body is not even in this file's index) and
1049+
// locally-bound const arrows. That allowlist would admit zero of them while
1050+
// still not excluding seam 6. Telling the four real callback seams from the
1051+
// fake one needs to know what the receiving method does with its argument —
1052+
// which is #11921's provenance problem, deliberately out of this card's scope.
1053+
// The census is therefore UNMOVED at 64, and #8901's restart conjunct (b) is
1054+
// not triggered by this reading.
1055+
9701056
/**
9711057
* Where the read-seam rule looks. Narrowed on purpose — see above.
9721058
*

0 commit comments

Comments
 (0)