Skip to content

v1.91.261.0 fix(restamp): quoted since: rows and stale base-version mentions on branch-added lines when VERSION equals the base (gbrain #7640) - #3491

Merged
capy-ai[bot] merged 5 commits into
mainfrom
capy/quoted-since-rows-and
Oct 27, 2026
Merged

capy-ai[bot] merged 5 commits into
mainfrom
capy/quoted-since-rows-and

Conversation

@garrytan

@garrytan garrytan commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner
{
  "schema_version": 1,
  "pr": 3491,
  "head": "b5ca5a68cd950946a78cd08a1579f0a35e6f72c1",
  "version": "1.91.253.0",
  "base": "main",
  "gate": {
    "free-tier (remote, Ubicloud, requires-remote cleared)": {
      "pass": 9379,
      "fail": 0,
      "skip": 0
    },
    "macos-named-regressions (CI run 40398992761, requires-remote cleared)": {
      "pass": 2,
      "fail": 0,
      "skip": 0
    },
    "windows-free-shard (CI run 40398998688, requires-remote cleared)": {
      "pass": 290,
      "fail": 0,
      "skip": 0
    },
    "focused: restamp-mentions + stamp-templates": {
      "pass": 16,
      "fail": 0,
      "skip": 0
    }
  },
  "session_kind": "unattended",
  "pregate": "requires-remote cleared @1ac3e5f54779 (1 checks, current for b5ca5a68cd95)",
  "requires_remote": [
    "free-tests.yml/macos-named-regressions: https://github.com/garrytan/gstack/actions/runs/40398992761",
    "windows-free-tests.yml/windows-free-shard: https://github.com/garrytan/gstack/actions/runs/40398998688",
    "pregate.json/remote_gate: remote-gate tree=1ac3e5f54779 tests=9379"
  ],
  "issues": {
    "fixes": [],
    "refs": [
      7640
    ],
    "flakes": []
  }
}

What changed

gstack-restamp --next --dry-run on a gbrain-shaped scratch repo, before (left, gstack 1.91.253.0) and after (right, this PR): the PR's quoted since: '0.60.468.0' row moves to 0.60.469.0, master's own 0.60.468.0 row stays, and the stamp_paths stage says why

Before (left): the real bin/gstack-restamp --next --base master --pr 7640 --dry-run at 1.91.253.0 on a two-commit scratch repo in gbrain's state (master released .467, the PR stamped itself .468 and added { since: '0.60.468.0', id: 'sync_anchor_relative' }, master then shipped .468 on its own with its own { since: '0.60.468.0', id: 'bun_floor_1_4_4' } row, the PR merged master; VERSION .468 on both sides). stage stamp_paths: skipped (no tentative mention newer than the fork version 0.60.467.0) and the PR's row stays at .468 while the release goes out as .469. After (right): the PR's row moves to .469, master's .468 row is untouched, and the stage detail reads VERSION equals base 0.60.468.0, so 0.60.468.0 moved only on lines this branch added and stayed on the base's own lines (src/core/behavior-change-notice.ts: 1 moved, 1 kept). Captured at e27c799; the stage output is unchanged since (the later commits changed how provenance is computed, not what it prints), and the outside reviewer's pass-3 probes reproduced the same counts at 8f3f686.

Quoted versions are stamp forms. tentativeMentions (the scanner over branch-added lines) and rewriteStamps (the exact rewrite table, shared with the tree receipt and the gate-reuse normalizers) in lib/restamp.ts accept since: 'X', since: "X", (was 'X') and (was "X") beside the bare forms and keep the quote style; an opened quote must close right after X (since: '1.2.3-beta' and since: '1.2.3" are not the form), and vX already accepted a quote before the v. The scanner also reads the policy's stamp_patterns templates, so a template-only form on a branch-added line is a candidate too.

The equal-version rule. When the branch has no stamp of its own (its VERSION equals the base's and no --was is given), a stamp-form mention of the base version on a line the branch added is its stale tentative stamp and moves to the allocated version; the same version on a line the base already carries stays. The stage is its own function (rewriteStampPaths) and its detail, printed in the stage table and kept in the journal, names both counts per file. When the branch has an explicit stamp that no stamp file mentions, the skipped detail names it instead of hiding it. --was X is unchanged: it rewrites X on every line, the base's own included, and re-heads ## [X] (its help line now says both).

Provenance is by position, not text. branchAddedLines returns the new-side line positions from git diff --unified=0 --inter-hunk-context=0, and parseUnifiedDiff counts context lines on the new side (Hunk.addedAt), so a branch-added line whose text equals a base line moves alone, a diff.interHunkContext setting that joins hunks cannot shift positions, and a CRLF checkout whose diff is LF-normalized still finds its rows. A stamp interrupted after VERSION was written (EACCES on a stamp file) retries as a stamp, not a noop: the journal restores the equal-base provenance.

Why

Relayed from gbrain #7640 (gstack 1.91.246.0): the PR stamped 0.60.468.0, lost its slot, merged master, and master was 0.60.468.0 by then. At its slot gstack-restamp --next left the PR's notice rows at .468 while the release shipped as .469, and gbrain's compareReleases then hid that notice from every user already on .468. Two gaps combined: branchStamp was null because VERSION matched the base, so nothing was rewritten everywhere, and the scanner's since:\s*(\d…) could not pass the opening quote, so the branch-added-lines path found nothing either (--was 0.60.468.0 rewrites master's rows too, so it was no workaround). Both verified by reproducing the state with the real bin (screenshot above) and by running the new test file against the unfixed lib/restamp.ts: eleven of its thirteen cases are red there (the template-overlap and --was cases pass, as they should).

Approvals

No approvals recorded for branch capy/quoted-since-rows-and.

None were needed: a bug fix inside the GSTA-118 brief from GStack Minder (2026-10-26), within the lane's file ownership. Deferred, filed as noted in the body: none.

Summary

  • Quoted since: and (was …) forms are stamp forms in both the writer and the tree-receipt classifier; the scanner reads stamp_patterns templates.
  • The equal-version rule for a branch whose VERSION equals the base's, with per-file moved/kept counts in the stage detail and the journal.
  • Positional line provenance (--inter-hunk-context=0, Hunk.addedAt), CRLF-safe, and journal recovery of the equal-base provenance on retry.
  • --was help and docs/ship-policy.md stage 4 state the quoted forms, the provenance rule, its false-positive class and the escape hatch; CHANGELOG under [Unreleased] (VERSION, package.json and the digest stay at main's; the train stamps at the slot).

Test Coverage

New: test/restamp-mentions.test.ts, 13 cases. Pure: single and double quote, bare unchanged, quote style kept, mismatched pair refused, template overlapping the built-in form counted once, fork-version floor on quoted mentions. Fixture repo with the real bin/gstack-restamp: the reporter's state with and without the since: '{version}' template, a negative control, fork equals base, --was rewriting the base's row too, prerelease and mismatched-quote boundaries, duplicate text at two positions, diff.interHunkContext=1, a CRLF checkout, and an EACCES-interrupted apply whose retry completes the stamp (skipped on win32 and as root). Changed: test/stamp-templates.test.ts counts the quoted since: row once with or without a template.

Regression proof: 11 of 13 cases fail at the base lib/restamp.ts, all 13 pass at HEAD; each review pass's new cases failed on the tree before that pass's fix.

Tests ran: free tier (remote, Ubicloud) 9379, failed 0, tree 1ac3e5f54779 · macos-named-regressions 2 (run 40398992761) · windows-free-shard 290 (run 40398998688) · focused restamp-mentions + stamp-templates 16 · bun run typecheck clean · typecheck:test 819 known, none new · module-size ratchet pass (lib/restamp.ts under 800 lines).

Pre-Landing Review

Three passes (the cap), blocking floor applied: pass 1 over the whole diff at e27c799 (core review: 0 critical, 4 informational; outside voice openai/gpt-6.1-sol: 4 blocking, all fixed in 6469472: closing-quote rule, positional provenance for duplicate text, CRLF, retry after a partial apply); pass 2 over e27c799..6469472 (1 blocking: inter-hunk context shifting positions, fixed in 8f3f686); pass 3 over 6469472..8f3f686 (inter-hunk finding resolved, no new findings, all saved probes green). b5ca5a6 is three one-line text corrections from the docs re-audit, checked in-host against the code, no code change.

Gate edits: none detected (GATE_SUMMARY listed=0; test/stamp-templates.test.ts flips a pinned count 0 to 1 for a quoted since: row with no template, which is the change under test).

Outside review: completed, converged (openai/gpt-6.1-sol, 3 passes). Informational items kept as design decisions, named here: the equal-version rule reads line ownership, not meaning, so a branch-added Requires vX or later on a branch forked at X moves with it (documented in ship-policy stage 4 with the escape hatch); no --after (gate-ahead) fixture for the equal-version rule (code path shared with --next, read but not fixture-tested).

Exploratory QA

Reproduced the reporter's state on a gbrain-shaped scratch repo with the real bin, before and after (screenshot above). Not run: a live gbrain checkout.

Design Review

No frontend files changed — design review skipped.

Eval Results

No prompt-related files changed — evals skipped (no SKILL.md or template touched).

Greptile Review

Greptile triage: UNAVAILABLE (not run in this unattended Capy lane).

Issues

Refs #7640

Verification Results

Fixture before/after PASS (real bin, dry run); 11-of-13 red proof PASS; focused suites PASS; remote free tier PASS; macOS and Windows lanes PASS via hand-dispatched runs (the pull_request event does not schedule CI on a head GitHub shows as CONFLICTING on CHANGELOG.md). Not run: live gbrain repository.

Documentation

Status: updated at b5ca5a6. The read-only re-audit at 8f3f686 (audit id ship-20261026T204211Z-8f3f686, second attempt) returned blocked on two text corrections and nothing else; both are applied in b5ca5a6 together with its advisory wording note, so no claim in the audited scope contradicts the code.

Scope. The six committed files (lib/restamp.ts, bin/gstack-restamp, docs/ship-policy.md, CHANGELOG.md, test/restamp-mentions.test.ts, test/stamp-templates.test.ts) plus lib/tree-receipt.ts, docs/pregate.md and docs/troubleshooting.md. The auditor read the restamp stages, the stamp_paths and stamp_patterns table rows, the tree-receipt sentence and the restamp error anchors, grepped the rest of the doc tree for stamp forms, --was, stamp_patterns and restamp contracts, and ran both test files from a scratch extract (16 pass, 0 fail; 11 of 13 red with the base lib).

Corrections applied in b5ca5a6. CHANGELOG: the red-case count is eleven of thirteen (was "seven of the first nine", left over from before pass 1 added cases). docs/ship-policy.md stage 4: the branch's own stamp and --was <X>, when given, are each rewritten everywhere (explicit is their union in rewriteStampPaths; the old "else" said only one). bin/gstack-restamp --was help default: "plus the stale mentions on lines the branch added".

Documentation health. docs/ship-policy.md: current (stage 4, both table rows, receipt sentence). CHANGELOG.md: current under [Unreleased]. bin/gstack-restamp help: current. docs/pregate.md, docs/troubleshooting.md: current, no new result code so no new anchor. README/AGENTS/CLAUDE/CONTRIBUTING/ARCHITECTURE: no restamp stamp-form contract to update.

Debt, not blocking. The journal-recovery behaviour (a retry after an apply interrupted once VERSION was written now completes as a stamp) is stated in the CHANGELOG and tests, not in ship-policy stage 6 or the RESTAMP_CONFLICT anchor.

Test plan

  • gstack-pregate --stage tests --remote --spend-cap 2 --timeout 2700 --allow-repo-commands: PREGATE_REMOTE tree=1ac3e5f54779 tests=9379 failed=0 spent=0.04
  • gh workflow run free-tests.yml --ref capy/quoted-since-rows-and (40398992761) and windows-free-tests.yml (40398998688): success; gstack-pregate verify: ok, requires-remote cleared for tree 1ac3e5f54779
  • bun test test/restamp-mentions.test.ts test/stamp-templates.test.ts: 16 pass, 0 fail; same file against the base lib: 11 fail, 2 pass
  • bun run typecheck: clean; bun run typecheck:test: 819 known, none new; bun test test/module-size-ratchet.test.ts: pass
  • Real bin on the gbrain-shaped fixture (--next --base master --pr 7640 --dry-run): before skipped at the fork version, after 1 moved, 1 kept

🤖 Generated with Claude Code

…ranch-added lines when VERSION equals the base (gbrain #7640)

A PR that stamped X, lost its slot and merged a base that had reached X on
its own had no branch stamp, and the mention scanner could not pass the
opening quote of a quoted row, so gstack-restamp --next left the PR's
{ since: 'X' } rows at X while the release shipped as X+1.

- tentativeMentions and rewriteStamps accept since: 'X' / since: "X" /
  (was 'X') / (was "X") and keep the quote style; the scanner also reads
  the policy's stamp_patterns templates.
- With no branch stamp and no --was, a stamp-form mention of the base
  version on a line the branch added moves; the same version on a base
  line stays. The stamp_paths stage detail names both counts per file.
- test/restamp-mentions.test.ts (pure forms + the reporter's state rebuilt
  with the real bin), stamp-templates pin updated, docs/ship-policy.md,
  --was help line, CHANGELOG [Unreleased].

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
@trunk-io

trunk-io Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

😎 This pull request was merged.

garrytan and others added 3 commits October 26, 2026 20:21
…retry after a partial apply

- since: 'X' and (was 'X') require the quote to close right after X in the
  scanner and the rewrite table, so a quoted prerelease or a mismatched pair
  is never a stamp (the tree receipt shares the table).
- branchAddedLines records positions from the unified=0 hunk headers; the
  stamp_paths stage rewrites by position, so a base line with the same text
  stays and a CRLF checkout still finds the branch's row.
- Journal recovery restores the equal-base provenance (no branch stamp) so a
  stamp interrupted after VERSION was written retries as a stamp, not noop.
- Skipped detail names the explicit version it searched for; --was help line
  names the CHANGELOG re-heading; docs and CHANGELOG state the boundaries,
  the fork-point attribution and the rule's false-positive class.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…ontext

parseUnifiedDiff records each added line's new-side position (addedAt),
counting context lines a hunk carries, and branchAddedLines pins
--inter-hunk-context=0, so a diff.interHunkContext setting that joins two
hunks around a base row no longer shifts the branch's second row onto the
base row. Test with interHunkContext=1 (red on the pass-1 tree).

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
… help default

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
… base=origin/main@6c5b2d4 policy=base origin/main@6c5b2d46123c:.gstack/ship-policy.json)

STAMP_VERSION: 1.91.261.0
FILES: CHANGELOG.md VERSION agents-digest/gstack-AGENTS.md package.json
MERGE_CONDITION: sha=b5ca5a68cd950946a78cd08a1579f0a35e6f72c1

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
@github-actions github-actions Bot changed the title fix(restamp): quoted since: rows and stale base-version mentions on branch-added lines when VERSION equals the base (gbrain #7640) v1.91.261.0 fix(restamp): quoted since: rows and stale base-version mentions on branch-added lines when VERSION equals the base (gbrain #7640) Oct 27, 2026
@capy-ai
capy-ai Bot merged commit 51a2b19 into main Oct 27, 2026
63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant