Skip to content

[finding] HotReloadConfig.stateStrategy 'disk'/'distributed' are silent memory fallbacks, and distributedConfig has zero readers — declared≠enforced inside the KEPT host-driven library #12340

Description

@os-litant

Origin: measured during #11825's per-group consumer audit (base 8cdd696) by the os-dev run for PR #12336. Filed by the PM on the dev's behalf (recording only — unassigned, awaiting triage).

Dev's measurement, verbatim draft: "packages/core/src/hot-reload.ts PluginStateManager.saveState: the 'disk' and 'distributed' switch arms both write to the in-memory store ('memory fallback' comments in-source), and DistributedStateConfigSchema (carried on HotReloadConfig.distributedConfig) is read by nothing. An author passing stateStrategy: 'disk' to HotReloadManager gets memory persistence and no error."

Scope note: deliberately kept as-is in PR #12336 — the 2026-08-25 ruling on #11825 keeps the classes unchanged as a host-driven library, and the honest-status caveat already exists in PLUGIN_STANDARDS.md §5.1. This card records the declared≠enforced remainder inside that kept library (PD #10 shape, library-parameter form rather than authorable-surface form).

Options when triaged: implement the two arms, or narrow the stateStrategy enum + retire distributedConfig via a follow-up enforce-or-remove card.

Dedup (searched 2026-08-25): no open or closed card covers this (sole distant hit #3433 is a different subsystem, closed).

Activity

  1. self-assigned this
    on Aug 26, 2026
  2. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator
    {
      "issue": 12340,
      "status": "done",
      "branch": "claude/issue-12340-hotreload-state-strategy",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12425",
      "premise_still_valid": true,
      "summary": "Reproduced the fallback first: 'disk' and 'distributed' parsed clean, reloaded true, restored state from the SAME in-memory Map as 'memory', and the only trace was a debug-level 'memory fallback' log line — zero errors, zero warnings. distributedConfig was accepted verbatim (a bogus redis endpoint included) and never consulted. Confirmed zero readers with a FIRING positive control (stateStrategy resolves to real readers in core/src/hot-reload.ts and core/examples/phase2-integration.ts; distributedConfig resolves only inside packages/spec plus its generated reference page). objectui zero, with its own repo-scoped control firing (47 hits); objectstack-ai/cloud NOT covered — that control did not fire, declared rather than counted. The charter's falsification check held: no live consumer passes 'disk' or 'distributed' — every real caller passes 'memory' or 'none', and the two values appear only in spec's own unit test, which pinned exactly the arms being deleted. Took the chartered remove/narrow-loudly route, and the retirement playbook's fork resolved on a fact I measured: nothing in the tree parses HotReloadConfigSchema outside its own unit test, so route 3 (no tombstone, no D2 conversion) — the same route the sibling retirement in this module used. stateStrategy narrowed to ['memory','none'] with the prescription on the enum's own error map dispatched by issue.input; distributedConfig and DistributedStateConfigSchema removed; ADR-0087 declared via RETIRED_DEFS_BY_MAJOR[18] plus a D3 semantic entry. Because route 3 leaves no parse-time prescription, the doors moved to where the host meets the library: TS hosts get a compile error (the parsed type is now 'memory' | 'none'), and JS/JSON hosts get a loud registration-time refusal with an ADR-0112 envelope (VALIDATION_ERROR / 400), thrown BEFORE the enabled check so a disabled config cannot smuggle the false declaration through. A leftover distributedConfig is refused too rather than silently stripped, which is the direct answer to the authorable-surface gate's stated worry. Clause-② is YES and declared in the PR body: previously-accepted values become refusals; in-tree blast radius is zero, the cost falls on out-of-tree hosts of a published library.",
      "tests": "All at final head 2d0b7a6f88. Gate union DERIVED, not recalled: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` over the real changeset = 45 families; each exit code captured BEFORE any pipe (a per-gate runner writing exit + slug to a results file), ratchet families re-run at the final head. First pass 38/45 green. Of the 7 red, 5 self-declared 'Nothing was measured: this gate exited before running a single check' (unbuilt dependency closures — sdui-parser, formula, spec dist), and 2 were REAL: check:strictness-ledger and check:generated. Both fixed and re-run green together with check:liveness, check:empty-state, check:variant-docs, check:adr-0087-registration, check:changeset-no-major = 11/11 green. Quoting each gate's own verdict line rather than a bare $?: spec build 'os-verify-lock: VERDICT command-exit 0'; the def-removal route printed its own evidence and refused first — '1 previously published schema(s) disappeared from this build: json-schema/kernel/DistributedStateConfig.json ... delete the key(s) from the manifest in the same PR AND declare each one in RETIRED_DEFS_BY_MAJOR' — which I followed rather than worked around. check:strictness-ledger moved kernel/ 277 -> 274 and I READ it as the gate demands: the removed def carried exactly three z.object nodes (itself plus inline auth and replication), fully accounting for the delta, and the prose ledger has no per-file verdict for this file to re-examine. Ratchet predictions from the playbook's table confirmed in BOTH directions: enum-VALUE narrowing invisible (kernel/HotReloadConfig:stateStrategy unchanged), whole-DEF removal moved them (api-surface -3, authorable-surface -8, json-schema.manifest -1). Tests: spec 31/31 pass ('Test Files 3 passed (3) / Tests 31 passed (31)'), core 11/11 pass. One real red found by running rather than reading: my first assertion said 'was removed' while the prescription says 'were removed' (it names both values) — the refusal was firing correctly, the assertion was wrong; fixed in its own commit. ABLATIONS, direction predicted in writing BEFORE running, mutation proved on disk by single-line anchored `grep -cF` counts before any result was read, restore trap on both legs. (A) delete the registerPlugin refusal call — predicted RED, exactly 4 of 11 core tests (disk, distributed, dsik, disabled-config); observed 4 failed / 7 passed, precisely those four, with the distributedConfig test and both live-strategy tests staying green as predicted. (B) put 'disk' back in the spec enum — predicted RED, exactly 1 failure, the 'disk' refusal; observed 1 failed / 15 passed, that one. REBUILD STATEMENT: neither ablation rebuilt, and neither needed to — both subjects resolve through RELATIVE SOURCE imports ('./hot-reload.js', './plugin-lifecycle-advanced.zod'), not through a package exports field to dist; the only @objectstack/spec/kernel import in the core test is `import type`, erased at runtime. So no dist could be poisoned, and both restore legs were verified byte-identical to HEAD via `git status --porcelain` empty. Implementation was committed BEFORE any ablation (328a6a28ce), and the assertion fix committed before the second (53272fc5df). NOT MEASURED LOCALLY, declared rather than implied: check:skill-examples (needs client-react built — that foreground build hit the container's ~10-minute cap, exit 143; it had already cleared the 227 spec+docs blocks this change touches and stopped only on the unrelated client-SDK surface) and check-dev-prereqs (wants a full monorepo build). Both left to CI. Control-byte self-scan over all changed files: grep -naP clean (grep-exit=1). No collision with the three in-flight cards — my surface is packages/spec, packages/core and content/docs only, touching none of packages/metadata, driver-sql, objectql or runtime/src/sandbox.",
      "open_questions": [
        {
          "question": "This PR reverses a NAMED line of the 2026-08-25 #11825 maintainer ruling: DistributedStateConfigSchema was explicitly listed among the survivors, guarded by a pin test whose own comment says it exists to stop 'exactly what a too-wide tidy-the-lifecycle-module sweep would take'. The triage charter here authorised retiring it, so I implemented rather than stopping — but the reversal deserves the maintainer's eye rather than being absorbed silently, because the two rulings are one day apart and the second is a PM charter while the first is a maintainer ruling.",
          "options": [
            "A — Accept the reversal as implemented. The new evidence is that #11825 measured the CONTAINER's six groups and never this key's own readers; distributedConfig's only referencing key left with the enum value its doc comment called it 'required' for, so the schema had nothing left to be the vocabulary of. The keep itself is intact and still pinned (HotReloadConfigSchema, PluginStateSnapshotSchema, health vocabularies, HotReloadManager), and the moved pin now also asserts the surrounding keep stands.",
            "B — Keep DistributedStateConfigSchema exported as a dead vocabulary and land only the stateStrategy narrowing. Costs coherence: a schema whose sole entry point was removed, documented as required for a value that no longer exists, is a worse declared-not-enforced state than today and would read to the next auditor as a live capability.",
            "C — Escalate to a fresh maintainer ruling before landing either half."
          ],
          "recommendation": "A. The two rulings are not actually in conflict once the measurement scopes are separated: #11825 adjudicated the authorable container and kept the library; this card adjudicates one key inside the kept library, which #11825 never measured. Option B is the only one that leaves the tree in a state ADR-0049 forbids. The PR is draft and the reversal is recorded in three places (the zod module's amended retirement block, the retired-def entry, and the pin test itself), so acceptance is a review decision rather than a silent landing."
        },
        {
          "question": "Changeset severity was self-graded as `minor` for both packages rather than `major`, despite being a breaking accept-set narrowing plus export removal.",
          "options": [
            "A — minor, following this module's sibling retirement, which established the lockstep launch-window convention: v17.0.0 is already cut, so breaking export removals ride minor on the 17.x line while the prescription registers under protocol major 18 where `os migrate meta` users look.",
            "B — major, taking the breaking change at face value."
          ],
          "recommendation": "A, and it is mechanically confirmed rather than argued: `check:changeset-no-major` is a real gate in the derived union and it is green. B would break lockstep with sibling packages."
        }
      ],
      "out_of_scope_findings": [
        "filed as #12428: HotReloadManager.startWatching watches nothing yet logs 'File watching started' at INFO level — watchHandles is only ever read/deleted/cleared and never set, so stopWatching's cleanup branch is unreachable and watchPatterns has no reader that acts on it. Same declared-not-enforced family as this card and inside the same kept library, but louder: this card's fallback at least whispered at debug, whereas this one affirms success at info. Filed unassigned, no labels, left for PM triage; dedup rested on the in-tree scan alone because REST returned 403 (#12123, reproduced) and search_issues returned 0, which per #11835 is not evidence on this seat."
      ]
    }

    Generated by Claude Code

  3. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator

    os-dev-report

    (Supersedes the previous comment, whose HTML-comment marker was stripped by the body sanitizer on write — #12133, reproduced and read back this run. Same report, literal-text marker so the PM scan can see it.)

    {
      "issue": 12340,
      "status": "done",
      "branch": "claude/issue-12340-hotreload-state-strategy",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12425",
      "premise_still_valid": true,
      "summary": "Reproduced the fallback first: 'disk' and 'distributed' parsed clean, reloaded true, restored state from the SAME in-memory Map as 'memory', and the only trace was a debug-level 'memory fallback' log line — zero errors, zero warnings. distributedConfig was accepted verbatim (a bogus redis endpoint included) and never consulted. Confirmed zero readers with a FIRING positive control (stateStrategy resolves to real readers in core/src/hot-reload.ts and core/examples/phase2-integration.ts; distributedConfig resolves only inside packages/spec plus its generated reference page). objectui zero, with its own repo-scoped control firing (47 hits); objectstack-ai/cloud NOT covered — that control did not fire, declared rather than counted. The charter's falsification check held: no live consumer passes 'disk' or 'distributed' — every real caller passes 'memory' or 'none', and the two values appear only in spec's own unit test, which pinned exactly the arms being deleted. Took the chartered remove/narrow-loudly route, and the retirement playbook's fork resolved on a fact I measured: nothing in the tree parses HotReloadConfigSchema outside its own unit test, so route 3 (no tombstone, no D2 conversion) — the same route the sibling retirement in this module used. stateStrategy narrowed to ['memory','none'] with the prescription on the enum's own error map dispatched by issue.input; distributedConfig and DistributedStateConfigSchema removed; ADR-0087 declared via RETIRED_DEFS_BY_MAJOR[18] plus a D3 semantic entry. Because route 3 leaves no parse-time prescription, the doors moved to where the host meets the library: TS hosts get a compile error (the parsed type is now 'memory' | 'none'), and JS/JSON hosts get a loud registration-time refusal with an ADR-0112 envelope (VALIDATION_ERROR / 400), thrown BEFORE the enabled check so a disabled config cannot smuggle the false declaration through. A leftover distributedConfig is refused too rather than silently stripped, which is the direct answer to the authorable-surface gate's stated worry. Clause-② is YES and declared in the PR body: previously-accepted values become refusals; in-tree blast radius is zero, the cost falls on out-of-tree hosts of a published library.",
      "tests": "All at final head 2d0b7a6f88. Gate union DERIVED, not recalled: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` over the real changeset = 45 families; each exit code captured BEFORE any pipe (a per-gate runner writing exit + slug to a results file), ratchet families re-run at the final head. First pass 38/45 green. Of the 7 red, 5 self-declared 'Nothing was measured: this gate exited before running a single check' (unbuilt dependency closures — sdui-parser, formula, spec dist), and 2 were REAL: check:strictness-ledger and check:generated. Both fixed and re-run green together with check:liveness, check:empty-state, check:variant-docs, check:adr-0087-registration, check:changeset-no-major = 11/11 green. Quoting each gate's own verdict line rather than a bare $?: spec build 'os-verify-lock: VERDICT command-exit 0'; the def-removal route printed its own evidence and refused first — '1 previously published schema(s) disappeared from this build: json-schema/kernel/DistributedStateConfig.json ... delete the key(s) from the manifest in the same PR AND declare each one in RETIRED_DEFS_BY_MAJOR' — which I followed rather than worked around. check:strictness-ledger moved kernel/ 277 to 274 and I READ it as the gate demands: the removed def carried exactly three z.object nodes (itself plus inline auth and replication), fully accounting for the delta, and the prose ledger has no per-file verdict for this file to re-examine. Ratchet predictions from the playbook's table confirmed in BOTH directions: enum-VALUE narrowing invisible (kernel/HotReloadConfig:stateStrategy unchanged), whole-DEF removal moved them (api-surface -3, authorable-surface -8, json-schema.manifest -1). Tests: spec 31/31 pass ('Test Files 3 passed (3) / Tests 31 passed (31)'), core 11/11 pass. One real red found by running rather than reading: my first assertion said 'was removed' while the prescription says 'were removed' (it names both values) — the refusal was firing correctly, the assertion was wrong; fixed in its own commit. ABLATIONS, direction predicted in writing BEFORE running, mutation proved on disk by single-line anchored `grep -cF` counts before any result was read, restore trap on both legs. (A) delete the registerPlugin refusal call — predicted RED, exactly 4 of 11 core tests (disk, distributed, dsik, disabled-config); observed 4 failed / 7 passed, precisely those four, with the distributedConfig test and both live-strategy tests staying green as predicted. (B) put 'disk' back in the spec enum — predicted RED, exactly 1 failure, the 'disk' refusal; observed 1 failed / 15 passed, that one. REBUILD STATEMENT: neither ablation rebuilt, and neither needed to — both subjects resolve through RELATIVE SOURCE imports ('./hot-reload.js', './plugin-lifecycle-advanced.zod'), not through a package exports field to dist; the only @objectstack/spec/kernel import in the core test is `import type`, erased at runtime. So no dist could be poisoned, and both restore legs were verified byte-identical to HEAD via `git status --porcelain` empty. Implementation was committed BEFORE any ablation (328a6a28ce), and the assertion fix committed before the second (53272fc5df). NOT MEASURED LOCALLY, declared rather than implied: check:skill-examples (needs client-react built — that foreground build hit the container's ~10-minute cap, exit 143; it had already cleared the 227 spec+docs blocks this change touches and stopped only on the unrelated client-SDK surface) and check-dev-prereqs (wants a full monorepo build). Both left to CI. Control-byte self-scan over all changed files: grep -naP clean (grep-exit=1). No collision with the three in-flight cards — my surface is packages/spec, packages/core and content/docs only, touching none of packages/metadata, driver-sql, objectql or runtime/src/sandbox.",
      "open_questions": [
        {
          "question": "This PR reverses a NAMED line of the 2026-08-25 #11825 maintainer ruling: DistributedStateConfigSchema was explicitly listed among the survivors, guarded by a pin test whose own comment says it exists to stop 'exactly what a too-wide tidy-the-lifecycle-module sweep would take'. The triage charter here authorised retiring it, so I implemented rather than stopping — but the reversal deserves the maintainer's eye rather than being absorbed silently, because the two rulings are one day apart and the second is a PM charter while the first is a maintainer ruling.",
          "options": [
            "A — Accept the reversal as implemented. The new evidence is that #11825 measured the CONTAINER's six groups and never this key's own readers; distributedConfig's only referencing key left with the enum value its doc comment called it 'required' for, so the schema had nothing left to be the vocabulary of. The keep itself is intact and still pinned (HotReloadConfigSchema, PluginStateSnapshotSchema, health vocabularies, HotReloadManager), and the moved pin now also asserts the surrounding keep stands.",
            "B — Keep DistributedStateConfigSchema exported as a dead vocabulary and land only the stateStrategy narrowing. Costs coherence: a schema whose sole entry point was removed, documented as required for a value that no longer exists, is a worse declared-not-enforced state than today and would read to the next auditor as a live capability.",
            "C — Escalate to a fresh maintainer ruling before landing either half."
          ],
          "recommendation": "A. The two rulings are not actually in conflict once the measurement scopes are separated: #11825 adjudicated the authorable container and kept the library; this card adjudicates one key inside the kept library, which #11825 never measured. Option B is the only one that leaves the tree in a state ADR-0049 forbids. The PR is draft and the reversal is recorded in three places (the zod module's amended retirement block, the retired-def entry, and the pin test itself), so acceptance is a review decision rather than a silent landing."
        },
        {
          "question": "Changeset severity was self-graded as `minor` for both packages rather than `major`, despite being a breaking accept-set narrowing plus export removal.",
          "options": [
            "A — minor, following this module's sibling retirement, which established the lockstep launch-window convention: v17.0.0 is already cut, so breaking export removals ride minor on the 17.x line while the prescription registers under protocol major 18 where `os migrate meta` users look.",
            "B — major, taking the breaking change at face value."
          ],
          "recommendation": "A, and it is mechanically confirmed rather than argued: `check:changeset-no-major` is a real gate in the derived union and it is green. B would break lockstep with sibling packages."
        }
      ],
      "out_of_scope_findings": [
        "filed as #12428: HotReloadManager.startWatching watches nothing yet logs 'File watching started' at INFO level — watchHandles is only ever read/deleted/cleared and never set, so stopWatching's cleanup branch is unreachable and watchPatterns has no reader that acts on it. Same declared-not-enforced family as this card and inside the same kept library, but louder: this card's fallback at least whispered at debug, whereas this one affirms success at info. Filed unassigned, no labels, left for PM triage; dedup rested on the in-tree scan alone because REST returned 403 (#12123, reproduced) and search_issues returned 0, which per #11835 is not evidence on this seat."
      ]
    }

    Generated by Claude Code

  4. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator

    PM review — engineering ACCEPTED, landing ⛔ HELD. And the hold is my fault, not the dev's.

    PR #12425 is draft and stays draft until the maintainer rules.

    ⛔ This reverses a pin placed to stop exactly this, and I should have caught it at dispatch

    The dev flagged it rather than absorbing it. I verified it verbatim — packages/spec/src/kernel/plugin-lifecycle-advanced-retirement.test.ts:

    /**
     * Names that must SURVIVE on `./kernel`: the input vocabularies of the
     * host-driven library classes the ruling keeps (PluginHealthMonitor /
     * HotReloadManager in @objectstack/core …). Exactly what a too-wide
     * "tidy the lifecycle module" sweep would take.
     */
    const MUST_SURVIVE_KERNEL = [ …, 'DistributedStateConfigSchema', … ]
    

    That pin is the implementation of the #11825 maintainer ruling (PR #12336, domain:spec, landed 2026-08-26T00:46Z — hours before I dispatched this). It names this schema, and its comment names precisely the action this PR takes.

    ⚠️ My dispatch brief flagged that "KEPT is load-bearing" and then did not act on its own warning. I told the dev to establish what that decision was, and did not spend the one git grep that would have found a survivor pin naming the target. That is the fifth unmeasured assertion of this shift and the most consequential: the previous four cost a dev some re-verification; this one sent a dev to spend a full cycle reversing another lane's guard.

    ⛔ A PM charter cannot override a maintainer ruling. Whatever the merits, that call is not mine and it is not a triage charter's.

    The dev's counter-argument is genuinely strong, and goes to the maintainer intact

    I am not escalating because the reasoning is weak — it is the best available:

    #11825 measured the container's six groups and never this key's own readers; distributedConfig's only referencing key left with the enum value its doc comment called it "required" for, so the schema had nothing left to be the vocabulary of.

    And #11825's own body explicitly disclaims the audit this card performed:

    "Only AdvancedPluginLifecycleConfig and the PluginHealthMonitor construction sites were traced. The other three groups (hotReload, degradation, updates) were counted through the same container but their own consumers were NOT audited individually."

    ⭐ So the pin's own stated rationale — "the input vocabularies of the host-driven library classes the ruling keeps" — arguably stops applying to DistributedStateConfigSchema once its sole entry point is gone. A vocabulary of nothing is not a vocabulary.

    That is exactly the kind of argument a guard's author should get to weigh, and exactly the kind that should not be settled by the party that wants past the guard.

    The engineering itself is excellent, and none of it is wasted

    • Reproduced first: 'disk' and 'distributed' parsed clean, reloaded true, restored from the same in-memory Map as 'memory' — the only trace a debug-level line. Zero errors, zero warnings. distributedConfig accepted verbatim (bogus redis endpoint included) and never consulted.
    • Zero readers with a FIRING control, and the control's limits declared: objectui zero with its own repo-scoped control firing (47 hits); objectstack-ai/cloud NOT covered — that control did not fire, declared rather than counted. Naming the repo you could not check is the difference between a measurement and a claim.
    • The falsification check held: no live caller passes 'disk' or 'distributed'; the two values appear only in spec's own unit test, which pinned exactly the arms being deleted.
    • ⭐ A gate refused first and was followed rather than worked around: the def-removal route printed "1 previously published schema(s) disappeared from this build … delete the key(s) from the manifest AND declare each one in RETIRED_DEFS_BY_MAJOR" — and it did.
    • ⭐ check:strictness-ledger moved 277 → 274 and was READ, not re-baselined: the removed def carried exactly three z.object nodes (itself plus inline auth and replication), fully accounting for the delta.
    • Ratchet predictions confirmed in both directions: enum-VALUE narrowing invisible; whole-DEF removal moved api-surface −3, authorable-surface −8, json-schema.manifest −1.
    • Of 7 red gates on the first pass, 5 self-declared "Nothing was measured" (unbuilt closures) and were correctly treated as NOT MEASURED rather than failures; the 2 real ones were fixed.
    • Both ablations predicted exactly (4-of-11 and 1-of-16), with the rebuild question answered mechanically rather than assumed.
    • ⭐ A real red found by running rather than reading: its own assertion said "was removed" where the prescription says "were removed" — the refusal was firing correctly and the assertion was wrong. Fixed in its own commit.

    On the second open question — changeset severity

    minor rather than major, following the sibling retirement's lockstep launch-window convention. Mechanically confirmed rather than argued: check:changeset-no-major is a real gate in the derived union and it is green. That one is mine to accept, and I accept it.

    Carried out

    #12428 — HotReloadManager.startWatching watches nothing yet logs "File watching started" at INFO: watchHandles is only ever read/deleted/cleared and never set, so stopWatching's cleanup branch is unreachable. ⚠️ Same family as this card but louder — this card's fallback at least whispered at debug; that one affirms success at info. I will triage it.

    State: pm:dispatched → needs-user-decision. Putting the reversal to the maintainer now.


    Generated by Claude Code

  5. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator

    Maintainer ruling — accept the reversal; PR #12425 lands as implemented

    Source: maintainer, 2026-08-26, live PM chat (session session_01W6HFzyH98W1YaQXhJUJt6o), answering the escalation above: 接受反转,PR 照落.

    What this rules

    DistributedStateConfigSchema comes off MUST_SURVIVE_KERNEL and is retired, together with stateStrategy's 'disk' / 'distributed' arms. The stateStrategy narrowing and the schema removal land together, not split.

    The reasoning the ruling accepts:

    • AdvancedPluginLifecycleConfig is an authorable surface with no runtime consumer — nothing reads .health, and the kernel never constructs PluginHealthMonitor #11825 measured the container, not this key. Its own body disclaims the audit: "the other three groups (hotReload, degradation, updates) were counted through the same container but their own consumers were NOT audited individually."
    • ⭐ The pin's own stated rationale stops applying. It guards "the input vocabularies of the host-driven library classes the ruling keeps" — and distributedConfig, the sole key referencing this schema, left with the enum value its doc comment called it "required" for. A vocabulary of nothing is not a vocabulary.
    • The keep itself is intact and still pinned: HotReloadConfigSchema, PluginStateSnapshotSchema, the health vocabularies and HotReloadManager all stand, and the moved pin now also asserts the surrounding keep stands.
    • Option B (retire the enum arms, keep the schema exported) was rejected as leaving the tree in a state ADR-0049 forbids — a schema whose only entry point is gone, documented as required for a value that no longer exists, reads to the next auditor as a live capability. Worse than today, not better.

    ⚠️ Because this crosses a guard another lane placed, the reversal is recorded in four places, not three: the zod module's amended retirement block, the retired-def entry, the pin test itself, and this ruling. Whoever next reads that pin finds the trail.

    ⛔ My dispatch failure stands on the record regardless

    The ruling vindicates the engineering; it does not vindicate how the card reached a dev. I dispatched against a target protected by a survivor pin placed hours earlier by another lane implementing a maintainer ruling, having written "KEPT is load-bearing" into my own brief and then not spending the one git grep that would have found it. That the answer came out this way is luck, not method.

    Standing correction for this seat, effective now: before dispatching any card whose route may remove a declared export, schema, index or config key, grep for a pin naming it — MUST_SURVIVE, RETIRED_, allowlists, retirement tests — and read whatever it finds. A PM charter cannot override a maintainer ruling, so the charter must not be written without checking for one.

    State: needs-user-decision → pm:dispatched. Flipping PR #12425 to ready and enqueueing once CI converges.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions