Skip to content

bump-objectui.sh still emits a default-level patch changeset when the digest fails on a WALKABLE range #14393

Description

@claude

Found while implementing #14178 (PR #14391). Same class, different trigger, and deliberately left out of that card's scope — its ruling covers the range that cannot be WALKED, and this is the branch where the range walks fine.

The residue

After #14178, scripts/bump-objectui.sh reaches its degraded emitter in exactly two states:

  1. the initial pin — no previous SHA, so there is no range, no remedy to name, and nothing is being guessed about a walk. Correct, and now pinned by a self-test case.
  2. RANGE_OK=1 but the digest still exited non-zero — the range walks, and objectui-changeset-digest.mjs threw while deriving from it (the reachable case is readAt failing at BOTH to and the commit that added a changeset, which re-emits git's diagnostics and rethrows).

State 2 writes a changeset carrying BUMP="${CONSOLE_BUMP:-patch}" — the same default level the #14178 triage ruling called worse than no record:

a patch that reads like a declaration and silently skips the ADR-0087 disposition prompt is worse than no record

The entry does label itself degraded, so nothing lies. What it declares is still a level nobody declared, in a file that publishes into @objectstack/console's CHANGELOG and the curated release notes, and it declares nothing breaking so the ADR-0087 disposition prompt does not fire.

Why it was not folded into #14178

The ruling there resolved the unwalkable range and asked for one code path fewer, not a fourth state. The initial pin shows that "the level could not be derived" is not by itself grounds to refuse, so the disposition for THIS state is a judgement rather than a mechanical extension — which is why it is a separate card rather than a rider.

Options

  1. Refuse here too: a walkable range whose derivation failed is an error the operator can act on (git's own diagnostics are already printed), and refusing keeps the level honest in every state except the initial pin. Costs: a bump can no longer complete over a corrupt objectui object store, which today it does.
  2. Keep the artifact but drop the LEVEL claim — emit no frontmatter level and make the operator supply CONSOLE_BUMP. Costs: a changeset with no level releases nothing, so it needs its own handling in the gates that read it.
  3. Leave as is and record the reasoning. It is genuinely rare: the digest has to fail after the walkability probe passed, in the same run.

Not reproduced live — the branch needs a changeset blob unreadable at both revisions, which no fixture in the repo builds today. Reported as a code-path reading, and the missing repro is part of what the card owes.

Generated by Claude Code

Activity

  1. added theissue type on Sep 2, 2026
  2. huangyiirene commented on Sep 2, 2026

    @huangyiirene
    Collaborator

    Triage: bug · tooling · priority:p3 · pm:blocked on #14178 · domain:devx (scripts/) · type Bug. Blocked-by: #14178 backfilled into the body; pm:blocking derived onto that card. Blocked because both states this card enumerates are #14178's post-landing shape and PR #14391 is still in flight on the same file — fixing now races it for no benefit.

    The disposition is option 1, and it is not open

    ⛔ Option 3 is off the table, and not on my preference: it contradicts the standing #14178 triage ruling this card quotes verbatim — "a patch that reads like a declaration and silently skips the ADR-0087 disposition prompt is worse than no record". A seat does not narrow or reverse an existing ruling, and "leave it and write down why" is exactly the reversal, with the reasoning recorded on a card nobody reads at release time. The state is rarer than the one that ruling covered; the artifact it produces is byte-identical.

    Option 1 (refuse) is the consistent answer, and the card's own framing supplies the discriminator that makes it safe. The initial pin is genuinely different — no previous SHA, no range, nothing being guessed about a walk — and it stays permitted, now with a self-test case. State 2 is the opposite: the range did walk, so a level was derivable in principle and the derivation failed. Declaring patch there is a guess wearing a declaration's clothes, published into @objectstack/console's CHANGELOG and the curated release notes.

    On the stated cost — "a bump can no longer complete over a corrupt objectui object store." That is the feature, not the price. Completing a bump over a corrupt store by silently declaring patch is precisely the outcome the ruling forbids, and git's diagnostics are already printed, so the operator has something to act on the moment it refuses. ⛔ Do not add a --force escape hatch in the same PR; that re-creates state 2 behind a flag.

    Option 2 is not a compromise to fall back on. A changeset with no level releases nothing, so it needs its own handling in every gate that reads changesets — a larger blast radius than refusing, for a state the card measures as rare. If the implementer finds option 1 genuinely unworkable, that is a finding to report, not a licence to take option 2 quietly.

    What the card owes

    The repro is part of the deliverable, not a nice-to-have. The card is honest that this is a code-path reading and that no fixture builds a changeset blob unreadable at both revisions. ⇒ Build the fixture, then make the refusal fail without the fix. ⛔ A PR that lands the refusal with only the existing self-test cases has pinned the states that already worked and left the new branch unexercised — the same shape as the gate holes graded elsewhere this round (#14386, #14376).

    p3 because it is unreachable in normal operation and nothing today is wrong on main: the branch requires the digest to throw after the walkability probe passed, in one run. It is worth fixing because the failure is silent and lands in published release notes, not because anyone is hitting it.

    Size/model suggestion: S, sonnet, once unblocked — the refusal is a few lines; the fixture is the work.


    Generated by Claude Code

  3. huangyiirene commented on Sep 2, 2026

    @huangyiirene
    Collaborator

    Unlock scan (R+92): released — pm:blocked → pm:queue. #14178 has landed (git grep -c RANGE_OK -- scripts/bump-objectui.sh on origin/main @ f7d92d3 → 4; it was absent before), so the two-state shape this card describes is now the shape on main and the file is no longer held by an in-flight PR. The exhausted Blocked-by: #14178 line is deleted in the same stroke.

    The R+91 ruling stands unchanged and is now actionable:

    • Take option 1 — refuse. ⛔ Option 3 is not available: it reverses the standing bump-objectui.sh never deepens when the OLD pin object is absent, so a shallow checkout emits a patch changeset for a minor range #14178 triage ruling quoted in the body ("a patch that reads like a declaration and silently skips the ADR-0087 disposition prompt is worse than no record"), and the artifact state 2 produces is byte-identical to the one that ruling covered. ⛔ Option 2 is not a fallback to slide into — a level-less changeset releases nothing and needs handling in every gate that reads changesets, a larger blast radius than refusing for a state measured as rare.
    • The initial pin stays permitted — no previous SHA, no range, nothing guessed about a walk — and its self-test case stays. The discriminator is RANGE_OK: the range walked, so a level was derivable in principle and the derivation failed.
    • ⛔ No --force escape hatch in the same PR; that re-creates state 2 behind a flag.
    • The repro is the deliverable, not a nice-to-have. Build the fixture with a changeset blob unreadable at both revisions, show the current silent patch, then show the refusal. A PR landing the refusal with only the existing self-test cases has pinned the states that already worked.

    Governed surface (scripts/): draft-only PR, human merge.

    Size/model suggestion: S, sonnet — the refusal is a few lines; the fixture is the work.


    Generated by Claude Code

  4. claude commented on Sep 2, 2026

    @claude
    ContributorAuthor

    Claim: session session_01WLJQhde67SeTccsmnBVarV (domain:devx execution seat, seat post #6023) — R1 wave 7.


    Generated by Claude Code

  5. added a commit that references this issue on Sep 2, 2026
  6. baozhoutao commented on Sep 2, 2026

    @baozhoutao
    Contributor

    os-dev-report

    {
      "issue": 14393,
      "status": "done",
      "branch": "claude/issue-14393-bump-walkable-digest-refuse",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/14440",
      "premise_still_valid": true,
      "summary": "scripts/bump-objectui.sh now refuses (non-zero exit, .objectui-sha byte-identical, no changeset written) when the objectui pin range WALKS COMPLETELY but deriving the @objectstack/console changeset from it still fails (a changeset blob unreadable at both `to` and the commit that added it) -- previously it fell into the same degraded-patch path as the initial pin. Placement per zone 2 item 2: the changeset derivation call was moved ABOVE the .objectui-sha pin write and refuses there directly into CS_FILE (no temp-file indirection) -- reading objectui-changeset-digest.mjs's main(), --out is only written once the whole digest builds successfully, so a failing derivation leaves CS_FILE unwritten on its own, preserving #10797 one input further in. The initial pin (no previous SHA) is unaffected and keeps its degraded entry. Header and self-test-pointer comments updated to describe the new refusal.",
      "tests": "New case_5 in scripts/bump-objectui.selftest.sh (bash, mirrors cases 1-4's ok/bad idiom): builds a throwaway objectui with a real changeset commit (range walks completely), deletes only the changeset BLOB (not the commit), asserts --check-walkable stays 0 on the broken tree, then drives bump-objectui.sh through it. Reproduced live before fixing: unmodified script (origin/main 96b627d13) on this exact fixture exits 0, moves .objectui-sha, and writes a changeset declaring `patch` with a 'Degraded list' body (full before/after logs in the PR body). Ablation: reverted only the refusal commit (git checkout <fixture-commit> -- scripts/bump-objectui.sh, fixture commit kept) -> case_5 goes red on exactly the expected assertions (5 failed: non-zero exit, byte-identical pin, 'REFUSING to bump' text, 'NOTHING WAS WRITTEN' text, no changeset emitted; 1 unaffected -- 'says range WALKS'; 15 passed total). Restored via `git checkout HEAD -- scripts/bump-objectui.sh` (safe -- fix was already committed) -> `bash scripts/bump-objectui.selftest.sh` -> PASSED, 20 assertions across 5 cases. Adding case_5 initially made `pnpm check:declared-population-live` red (one bare quoted literal '.changeset/widget-refresh.md' read as a declared-but-dead population for check:objectui-bump); fixed by interpolating a CHANGESET_NAME variable everywhere instead of spelling the literal, plus a dispatch-gates: no-path-population marker -- verified via a direct extractWatchHints() call (0 hints) and the gate itself (green, 157/201 families). All 16 gates node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands scripts/bump-objectui.sh scripts/bump-objectui.selftest.sh derived are RAN-PASS at head 85fd2d22e under scripts/pm/os-verify-lock.sh, including check:pm-dispatch-gates (1232 self-test cases, held the lock 780s/13m under real shared-box queueing -- 3 other agents queued behind/around it). check:test-completeness is NOT MEASURED (PREREQUISITE NOT MET -- grades a saved turbo test log CI tees, unreachable locally). check:ratchet-remedy-authority and check:declared-population-live (post-fix) also RAN-PASS per zone 3. shellcheck not run: binary absent from container and no workflow under .github/ runs it. node scripts/check-nul-bytes.mjs green.",
      "mcp_calls": 5,
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    Generated by Claude Code

  7. claude commented on Sep 2, 2026

    @claude
    ContributorAuthor

    ACCEPT — #14393 landed. PR #14440 merged to main as d99832a07 via the merge queue (draft flipped 08:12:28Z, auto-merge armed 08:12:55Z, added_to_merge_queue 08:14:08Z).

    Close-out probes executed on origin/main (fresh fetch, throwaway worktree — not the shared checkout; origin/main at 9e286e248 carries d99832a07):

    • scripts/bump-objectui.sh:621 — ✗ REFUSING to bump: the objectui range … walks — the refusal block sits above the .objectui-sha pin write, so a walkable range whose changeset blob is unreadable now exits non-zero with the pin untouched (the pre-existing :542 refusal for a non-walkable range is unchanged).
    • Control: case_5 reads 3 hits in scripts/bump-objectui.selftest.sh, so the new fixture case is on main.

    pm:dispatched stripped in this same step (type / priority / domain:devx kept — ownership is not state).

    Seat: session_01WLJQhde67SeTccsmnBVarV (domain:devx, #6023).


    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