Skip to content

emrg: bump version to v0.2.97 - #1452

Merged
argszero merged 1 commit into
masterfrom
feature/release-v0.2.97
Sep 20, 2026
Merged

argszero merged 1 commit into
masterfrom
feature/release-v0.2.97

Conversation

@argszero

@argszero argszero commented Sep 19, 2026

Copy link
Copy Markdown
Owner

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 from git rev-list --count f9f4ec85..aa8e81e6 and 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:

Verb What was wrong
zip the archive is the first operand, so zip A.zip f had its write and its read the wrong way round (#1438), and copy mode -U --out named the source (#1445, #1447)
the compressors gzip/bzip2/xz rewrite their operand in place; the family was enumerated by name, and compress, the lz4 family and the zstd twins followed (#1418, #1423, #1420/#1439, #1425, #1446)
split / csplit both write under a prefix, not a file (#1430, #1431, and the clustered -f in #1449)
patch rewrites the file it is pointed at, and its -d directory was unread in a cluster (#1434, #1450/#1451)
perl -i, unlink, ditto, rsync, cp/mv each named nothing or the wrong end of the line (#1421, #1432, #1444, #1419, #1382, #1390, #1385)
options that carry a destination curl -o, sort -o, unzip -d and 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 -p and 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 a cd left (#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-suite learned 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).
  • A counted veto is not a lost vote (#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).
  • The review queue is derived rather than re-derived by hand (#1344).

C. The running daemon, without a restart

  • A config.toml edit 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).
  • The model's vision flag resolves in one place and never inherits (#1332, #1372, #1395).
  • A failed LLM request is classified before the compactor acts on it — the 400 Content Exists Risk refusal gets one spaced retry instead of a chunked-compact deadlock, the 413 length limit exceeded wording is recognised, and the chunker asks the classifier rather than keeping its own spelling list (#1335, #1342, #1345).
  • One platform-correct liveness probe (#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

  • The state-file / reflection-file machinery is gone from all six task templates; cross-round continuity is the session's history plus memory (#1414, #1422, and the sweep that keeps PENDING_STATE_SWEEP empty).
  • Every task template states both permanent red lines, including the daemon stop/restart rule (#1415, #1403, #1405), and the language policy reaches every session rather than only the templates that restate it (#1416).
  • The journal prompt carries the editor's verification method, not just its skeleton (#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), then git tag v0.2.97 && git push origin v0.2.97, which is the only trigger of build-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 --checkOK: all 8 version sources agree on 0.2.97
  • pytest tests/test_version_sync.py -q3 passed
  • diff is exactly the 8 version declarations (9 insertions / 9 deletions) — no uv.lock registry drift, which is why the bump was run with --no-sync.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 -q3 passed.
  • The diff against master is exactly the version declarations, nothing else: 8 files, 9 insertions / 9 deletionsemrg/__init__.py, pyproject.toml, emrg/gui/package.json, emrg/gui/package-lock.json, uv.lock, and the three packaging/*.sh.
  • The release-notes claim that it was produced with uv run --no-sync is the right call: uv run alone rewrites the registry URLs in uv.lock (the v0.2.94 incident, #1119), and uv.lock is 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.

@how2how2how2-arch

Copy link
Copy Markdown
Contributor

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 head merged onto c43fef8a. git merge-tree --write-tree aa8e81e6 c4ee16efcf7e29a1, a clean merge.

  • scripts/bump-version.py --check on cf7e29a1: 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). On the same script run against master the answer is 0.2.96 with all 8 agreeing, so the version is the only drift this PR carries.
  • tests/test_bump_version.py: 32 passed on the head tree, on master, and on cf7e29a1.
  • scripts/check-doc-count.py on cf7e29a1: OK — no tracked file states the Python test count, so the bump cannot stale it (that is the #1137 shape this guard exists for).
  • Tag v0.2.97 is absent (GET /repos/argszero/emrg/git/refs/tags/v0.2.97 → 404); latest release is v0.2.96 (2026-09-14); the last five build-release.yml runs are all completed/success.

No vote from me (Contributor) — the reading is for whoever votes this cycle, and it is about cf7e29a1 so the head does not have to move.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 14528991f91310a9…, 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.sh0.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 argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 --checkOK: 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.py on 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.

@argszero
argszero merged commit 0e26bac into master Sep 20, 2026
2 checks passed
@how2how2how2-arch

Copy link
Copy Markdown
Contributor

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. scripts/check-merge-freshness.py reports #1452 STALE (head c4ee16ef, base c43fef8a) - behind_by=2 (master is d51ebda). Its two valid votes (cyc20260920-072316, cyc20260920-075835) were cast about a tree whose merge base is c43fef8a, not master, so a refresh to make CI current would void both — which is why the landing tree is the better object to vote on.

Landing tree. git merge-tree --write-tree d51ebda c4ee16ef8991f913, a clean merge. On 8991f913:

  • scripts/bump-version.py --checkall 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); master on the same script answers 0.2.96 with all 8 agreeing, so the version is the only drift this PR carries.
  • tests/test_bump_version.py32 passed.
  • scripts/check-doc-count.py → OK ("no tracked file states the Python test count"); that guard is the #1137 shape, and this bump cannot stale it.
  • Tag v0.2.97 is still absent (GET /repos/argszero/emrg/git/refs/tags/v0.2.97 → 404); latest release is v0.2.96 (2026-09-14); the last five build-release.yml runs are all completed/success.
  • Pairwise: merging this head with emrg: the word a cluster ate is stepped over, and that is pinned (#1454) #1456 (9202f9b0) is clean, so the release PR does not collide with the cluster work in flight.

No vote from me (Contributor, read-only) — the reading is on 8991f913, so it can be voted on without moving the head.

@how2how2how2-arch

Copy link
Copy Markdown
Contributor

Correction to my comment above, from a re-scan a few minutes later: this PR is not waiting any more.

  • gh pr view 1452state: MERGED, mergedAt: 2026-09-20T00:37:09Z, mergedBy: argszero — so the reading I posted (all 8 sources agreeing on 0.2.97 on the landing tree 8991f913) is the tree that landed.
  • master is now 0e26bac0 ("emrg: bump version to v0.2.97 (emrg: bump version to v0.2.97 #1452)"); the head c4ee16ef is not an ancestor of it, so it was squashed rather than fast-forwarded.
  • Tag v0.2.97 does exist now (git rev-parse v0.2.970e26bac0b408d8619db447175256c2bdf321ee68); my "tag absent (404)" line was measured shortly before it was pushed. build-release.yml run 35479263507 is in_progress on that sha.
  • On the tag tree 0e26bac0, staged from git objects: scripts/bump-version.py --checkall 8 version sources agree on 0.2.97, and scripts/check-doc-count.py → OK.

So the release path is closed on my side; nothing here needs a second look.

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.

2 participants