Skip to content

check:durability-log-level cannot represent a seam that propagates a durability failure by RETURNING an outcome object — its propagation lists are keyed on callee names #16233

Description

@claude

The gap

scripts/check-durability-degradation-log-level.mjs cannot represent a durability seam whose catch propagates by returning an outcome object, and this is a limitation of the instrument's shape rather than a missing entry in its lists.

The seam that exposed it: recordNotificationEventReceipt in
packages/metadata/src/migrations/migrate-sys-notification-to-event.ts (added by PR #16230).
It is a durability seam of exactly the persistSeedTenancyReceiptRow class — the data really was
rewritten, every other reading is clean, and the only durable record of it can be absent
.

Why the gate cannot hold it:

  • DURABILITY_CRITICAL_CALLEES does not name it — that part is merely an entry.
  • ⛔ But it cannot be added without a second change: this catch propagates by RETURNING an
    outcome object
    , while both FAILURE_PROPAGATION_CALLEES and FAILURE_PROPAGATION_SITES are
    keyed on callee NAMES. A returned object literal has none.

⇒ Any seam in this repo that reports a durability failure to its caller through a return value,
rather than by calling a named reporter, is invisible to this gate by construction.

What was measured, and what was not

  • Measured (by the metadata: the adr-0030-notification-event migration writes its sys_migration receipt row per the ruled ledger-claim matrix — last_run_at on every completed run, applied_at on migrated only, never verified_at (#15710 follow-on) #16100 round, on its own diff): the gate's write-side limb reports 29
    seams both with and without
    the new file — i.e. adding a real return-propagating durability
    seam moves that population by zero.
  • Measured, and it is the control that makes the zero above a reading: the gate's read-side
    limb moves 68 → 67 when the same file is swapped back to origin/main. ⇒ the file is in the
    swept population; the write-side silence is the instrument's blindness, not the file's absence.
  • ⚠️ NOT a live defect. The seam is pinned per-file by that round's own failed receipt
    case, so this is an instrument-expressiveness gap, ⛔ not an unprotected degradation. Hence
    finding, not bug.
  • ⚠️ NOT measured: how many other seams in the tree propagate this way. That census is the first
    thing a round on this card should buy, because it decides whether the fix is worth its cost — a
    gap with one member is a note, a gap with thirty is a hole.

Why the shape of the fix is not obvious

A name-keyed list cannot key on "returns an object with a failure-shaped field". The candidate
directions each cost something and none is free:

  • Key on the enclosing function's name instead of the callee's, for a declared set of seams —
    cheap, but it moves the gate from "what does this call do" to "who is asking", which is a
    different question and may weaken the existing 29.
  • Recognise a declared return shape (an outcome type carrying a failure member) — precise, but
    it needs the gate to read types, which it currently does not.
  • Leave it name-keyed and accept the class as out of scope, recorded — honest, and it makes this
    card's answer a documented boundary rather than a hole.

⛔ This card proposes none of them. It records the gap, its measurement, and its control.

⚠️ Context a maintainer may want more than this row

This is now the third open finding against this one script, alongside a decision and a blocked
triage card:

# state what it is about
#8897 pm:on-hold collectLoggedLevels only records part of what it should
#12576 pm:on-hold readInventionKey falls back where it should not
this card new return-propagating seams are unrepresentable
#9882 pm:on-hold decide the shrink-only delta-ratchet for the same gate
#12753 pm:blocked triage the parameterless-catch read seams, then decide the 2a criterion

⇒ Five open items on one instrument. Whether that is five small repairs or one rewrite is a
question this card does not answer and probably should not be decided one row at a time.

Provenance and dedup

Found by the #16100 execution round, which could not file it itself and said so rather than
guessing: filing without a duplicate check is a forbidden shape, and its dedup channel was gone in
both directions — REST /search/issues is not served on that channel (a same-session control query
that must have hit #16100 came back empty, so its zero was a channel artefact, not a reading),
and both search_issues MCP calls were refused by the user-level rate limit. It handed the row to
the PM seat instead. Its full reasoning:
#16100 (comment)

Dedup performed by this seat, over a complete enumeration rather than a search: all 652 open
issues fetched with bodies (651 of 652 carry one) and grepped locally.
FAILURE_PROPAGATION_CALLEES, FAILURE_PROPAGATION_SITES, persistSeedTenancyReceiptRow and
recordNotificationEventReceipt return 0 hits each; DURABILITY_CRITICAL_CALLEES returns 2
(#15166, #8897) and check-durability-degradation-log-level returns 7 — all read, none covering
this shape. Firing controls on the same command and scope: sys_migration → 4 (#16194, #16185,
#16100, #15207), NOTIFICATION_EVENT_MIGRATION_ID → 2, durability → 12. ⇒ the zeros are readings.

Filed unassigned and ungraded — domain:* and priority are triage's.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions