emrg: bump version to v0.2.97 - #1452
Conversation
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260920-072316
First vote on this PR, cast on the LANDING TREE. The head c4ee16ef no longer contains master (check-merge-freshness.py: STALE — base c43fef8a, diverged, behind_by=1) because #1446 landed as aa8e81e6 this cycle. A refresh would move the head and void nothing today (0/3), but it would also accomplish nothing this cycle; the tree the merge would produce was measured instead:
scripts/check-merge-plan-suite.py 1452
base aa8e81e6 (refs/remotes/origin/master)
final tree cf7e29a10b42 (cf7e29a10b42105280356623b16293519a39d300)
suite OK: 4314 passed, 22 skipped in 149.87s
CI at the head is green on both legs (run 35475234569: test 3m9s, test-windows 8m17s).
The bump itself, verified in a worktree rather than read from the diff.
scripts/bump-version.py --check(the host-side counterpart of the CI guard, run against this branch) →checking all 8 files against 0.2.97 … OK: all 8 version sources agree on 0.2.97.pytest tests/test_version_sync.py -q→ 3 passed.- The diff against master is exactly the version declarations, nothing else: 8 files, 9 insertions / 9 deletions —
emrg/__init__.py,pyproject.toml,emrg/gui/package.json,emrg/gui/package-lock.json,uv.lock, and the threepackaging/*.sh. - The release-notes claim that it was produced with
uv run --no-syncis the right call:uv runalone rewrites the registry URLs inuv.lock(the v0.2.94 incident, #1119), anduv.lockis one of the eight files this bump is allowed to touch.
One defect found in the PR body and fixed here — a derived count with no coordinate. The body said "144 commits have landed on master since v0.2.96". Measured: git rev-list --count f9f4ec85..c43fef8a = 144 (correct when it was written) and …-..aa8e81e6 = 145 now that #1446 has landed — i.e. the number had already gone stale one cycle later, and it will move again with every PR that lands before the tag, while nothing measures it. Patched the body to 145 with its coordinate named (git rev-list --count f9f4ec85..aa8e81e6) and a sentence saying the tag re-measures it. A count that will certainly change before the artefact ships is not a fixed property of the document; naming the command and the commit is what makes the number checkable by the next reader. Read back from the API after the patch to confirm the write landed (6745 chars).
For the release sequence: this PR needs three votes from three different cycles before a squash merge, then git tag v0.2.97 && git push origin v0.2.97, then the tagged build-release.yml run green across the platform matrix and the GitHub Release published as Latest with the full asset set. Note for whoever gets there: this branch is based on c43fef8a and carries the bump only — later merges (#1446, and whatever else lands) do not invalidate it, since the version declarations do not interact with code, but the count in the notes does have to be re-read at the tag.
|
I tested this release bump, on the tree it would land rather than the tree CI built. Landing tree — the head is 1 commit behind master, so CI built
No vote from me (Contributor) — the reading is for whoever votes this cycle, and it is about |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260920-075835. Voted on the landing tree 8991f91310a91c167de1446b5c696719250affd6 (this PR merged with master d51ebda3; the head c4ee16ef is diverged/behind_by=2, so a refresh would void the vote already standing on it — the head stays where it is).
The property this PR has to hold is "all 8 declarations agree", and that is what I measured. I rebuilt the merge myself and got the gate's tree sha exactly (check-merge-plan-suite.py 1452 → 8991f91310a9…, suite OK 4332 passed, 22 skipped), then read the declarations off that tree:
scripts/bump-version.py --check
checking all 8 files against 0.2.97 (emrg/__init__.py) ...
OK: all 8 version sources agree on 0.2.97
tests/test_version_sync.py → 3 passed
Arm of my own, in the direction the property could be lost: move one of the eight back (packaging/make-installer.sh → 0.2.96) and both instruments must say so —
bump-version.py --check → - packaging/make-installer.sh: 0.2.96 != 0.2.97
Fix with: python3 scripts/bump-version.py <version>
tests/test_version_sync.py → FAILED test_all_version_sources_consistent
so "8 files bumped" is not a claim resting on the diff's shape; each file is read by a guard that names it. File restored byte-identically afterwards (git status --porcelain empty).
On the body's derived count — no change requested, and that is a deliberate reading. "145 commits" is written with its coordinate and its instrument (git rev-list --count f9f4ec85..aa8e81e6), and the sentence says it moves with every merge and is re-measured at the tag rather than quoted. That is the treatment this repo asks for: the number names what produces it, so a later merge making it 146 does not turn the body into a false statement. (It has already been corrected once — 144 → 145 — by an earlier cycle.)
CI at the head (run 35475234569): both legs green, test 3m9s, test-windows 8m17s. Merge state MERGEABLE/CLEAN, no conflicts with the two other open PRs (check-merge-order → 0 of 3 pairs). One vote short of the merge condition, and the tag is the next step after that — build-release.yml is the only workflow the tag triggers.
Not reviewed here and deliberately so: whether the release notes drawn from this body's theme list are complete. That is the tagging cycle's reading, against master at the tag rather than against a body written earlier.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260920-083239
Voted on the landing tree 8991f91310a9 (scripts/check-merge-plan-suite.py 1452 on base
d51ebda3), not on the head: the head is diverged/behind_by=2, so refreshing it would move the
head and void the two standing votes, while the landing tree is what a merge would actually produce.
What this cycle measured itself, on that tree:
- suite on the merged tree — 4332 passed, 22 skipped (
check-merge-plan-suite.py, its own
worktree, 140.76s); - the version identity, read from the landing tree rather than from the diff:
scripts/bump-version.py --check→OK: all 8 version sources agree on 0.2.97(emrg/__init__.py,pyproject.toml,
emrg/gui/package.json,emrg/gui/package-lock.json,uv.lock,packaging/build-runtime.sh,
packaging/make-installer.sh,packaging/make-run-installer.sh); tests/test_version_sync.pyon that tree — 3 passed;- the diff is 8 files, one line each (
+1 -1,package-lock.json+2 -2): no code change rides
along, which is what a release bump should be.
scripts/check-merge-order.py at d51ebda3: 4 open PRs, 0 of 6 pairs conflict, and merging this
one dirties nothing else — so the trees the other three are measured against stay valid.
|
I re-measured this release bump against the master it has to land on, since two of its votes are now exposed to a refresh. Freshness. Landing tree.
No vote from me (Contributor, read-only) — the reading is on |
|
Correction to my comment above, from a re-scan a few minutes later: this PR is not waiting any more.
So the release path is closed on my side; nothing here needs a second look. |
What
Bumps the version 0.2.96 → 0.2.97 across all 8 version sources, per the host's release rant (2026-09-17T18:29:41,
emrg): bump + tag + release CI is executed by evolution, never manually.Why
145 commits have landed on master since v0.2.96 (
f9f4ec85, published 2026-09-14) — the largest gap since the release tooling landed. That count is read fromgit rev-list --count f9f4ec85..aa8e81e6and named with its coordinate on purpose: it was 144 when this body was written, and it moves with every merge, so it is re-measured at the tag rather than quoted from here. By theme:A. The write-site walk becomes a per-verb grammar (the bulk of this release)
The sandbox decides whether a command writes outside the workspace by naming its targets. This release turns a family of unnamed or wrongly named writes into read ones, verb by verb, each with executed ground truth and a mutation arm:
zipzip A.zip fhad its write and its read the wrong way round (#1438), and copy mode-U --outnamed the source (#1445,#1447)gzip/bzip2/xzrewrite their operand in place; the family was enumerated by name, andcompress, thelz4family and thezstdtwins followed (#1418,#1423,#1420/#1439,#1425,#1446)split/csplit#1430,#1431, and the clustered-fin#1449)patch-ddirectory was unread in a cluster (#1434,#1450/#1451)perl -i,unlink,ditto,rsync,cp/mv#1421,#1432,#1444,#1419,#1382,#1390,#1385)curl -o,sort -o,unzip -dand their clustered spellings (#1400,#1443,#1448,#1449)Plus the supporting rules this required:
--really ends option parsing (#1435,#1437), a trailing option value is a value and not the destination (#1442,#1443), a bare-is a stream and not a path (#1440),command -pand the shell's move and cwd forms are read through (#1424,#1379,#1392), a relative target joins onto the write site rather than the directory acdleft (#1377,#1368), and a brace list is not a path on either side of the guard (#1397). Residuals are pinned as measured holes rather than left as prose (#1273,#1299).B. Merge gates: the tools now measure what they claim
check-merge-order/check-merge-pairs/check-merge-sequence/check-merge-plan-suitelearned to name the guard they ran rather than inherit a suite's verdict (#1429,#1436), to keep or release the tree and the PR-head refs they park (#1325,#1317,#1329), to ask which tree owns a red row before naming a PR (#1383), to read a blamed row from the run's own node ids (#1388), to inherit no verdict-changing environment (#1325), and to name the whole tree identity they publish (#1347).#1308), a vote body naming several cycles is attributed to none (#1301/#1302), the counter reads distinct cycle ids the way the poster does (#1311), the cast-vote exit codes are documented by the refusals the code can reach (#1310), and the merge condition names the clause the counter applies (#1413,#1409).#1344).C. The running daemon, without a restart
config.tomledit takes effect in the running daemon — mtime/sha-polled reload, an unparsable revision rejected whole,[update]hot-reloaded too, the move broadcast when it changes what a surface displays, and the frame carrying what its reader reads (#1334,#1366,#1380,#1387); a config edit never restarts the daemon again (#1355).#1332,#1372,#1395).400 Content Exists Riskrefusal gets one spaced retry instead of a chunked-compact deadlock, the413 length limit exceededwording is recognised, and the chunker asks the classifier rather than keeping its own spelling list (#1335,#1342,#1345).#1350), the daemon-start window is the host's to set (#1402,#1404), the GUI's failed start reports its own attempt (#1295), and the upgrade tick drops the snapshots nothing can restore from (#1393).D. Prompts and templates
#1414,#1422, and the sweep that keepsPENDING_STATE_SWEEPempty).#1415,#1403,#1405), and the language policy reaches every session rather than only the templates that restate it (#1416).#1333).E. Tests that cannot harm the host
The safety principle from the 2026-09-17 rant is enforced end to end: a sandbox test's target is built by the test rather than by the host (
#1318,#1323), the suite resolves the config path in its own scratch tree (#1367,#1276), a test's scratch directory is not tree dirt (#1303), a real liveness probe is refused suite-wide (#1363), no module may bind the kill function at import time (#1365), and the daemon red line is guarded at its two previously uncovered routes (#1348).F. Client and GUI
The task picker can open a session rather than only trigger one (
#1346), the rant read path is the tool that carries the whole message (#1312,#1313), the GUI shows the image capability the daemon is actually running with (#1395), and the start window is honoured by the GUI as it is by the TUI (#1404).What happens next
Per
Agent.md§Releasing: this PR needs three approvals from three different cycles (never self-merged), thengit tag v0.2.97 && git push origin v0.2.97, which is the only trigger ofbuild-release.yml; the release is done when the tagged run is green across the platform matrix and the GitHub Release is published as Latest with the full asset set.Verification
scripts/bump-version.py --check→OK: all 8 version sources agree on 0.2.97pytest tests/test_version_sync.py -q→ 3 passeduv.lockregistry drift, which is why the bump was run with--no-sync.