emrg: a shared variable and a link source that only prose asserted - #1406
Conversation
The note check-merge-plan-suite.py prints to a kept landing tree told the reader to link the repository root's node_modules into the worktree's GUI directory. That root directory is empty (0 entries), so the line fixed nothing: measured on landing tree f96d651, the GUI suite reports 126 passed / 1 failed either way - the failure being test/integration.test.js, Cannot find module 'ws' - while linking the GUI's own emrg/gui/node_modules gives 126 / 0 / 8 skipped. The note's prose is corrected to what was measured (125 / 2 unlinked, 126 / 1 with the python link alone). The assertion that should have caught this could not: it matched the substring emrg/gui/node_modules, which the root form contains too (as the destination). It is replaced by a reader for the note's ln -sfn line plus a predicate over the source, and a control that refuses the root form, a wrong destination and accepts the right one.
Issue #1276's lever is one variable reaching both entry points (#1402 the CLI/TUI, #1404 the GUI), and nothing asserted the two sides are the same name. Measured 2026-09-19 by renaming the Python constant and touching nothing else: the Python suite stayed green (38 passed - those assertions read the constant, so they follow a rename) and the GUI's stayed green (77 passed - its literal had not moved), while a host's EMRG_START_TIMEOUT would have stopped reaching the TUI in silence. tests/test_start_window_env_pairing.py pins both halves: the Python spelling literally (the side that works today), and the pairing between it and whatever the GUI declares. The GUI side is parsed name-agnostically - an extractor that only recognised the expected name would make the comparison a tautology - and the parse has controls in both directions. Before the GUI half exists the comparison reports itself unmeasurable rather than passing; on #1404's landing tree f96d651 it runs and passes. Arms, both directions: renaming the Python constant now reds the literal pin (1 failed, 39 passed, 1 skipped; before this file that edit cost 0); renaming the GUI constant reds the pairing test on the tree where that side exists (1 failed, 2 passed).
|
Independent verification of Trees staged from git objects (no 1. The remedy, reproduced rather than taken from the PR
Your landing-tree table is shifted by #1404's six added tests and matches in shape; what matters reproduces exactly: the root form is indistinguishable from no link, and the The old assertion could not have caught it for the reason you give: 2. The pin: live on the pair, and the rename it was written for
3. Four shapes the extractor rejects although the two sides agree
B is the one worth pausing on, and its diagnosis is this file's own subject one level up: the extractor returns which is untrue there: a declaration carrying exactly the right value exists. The instrument cannot tell "prose names it and a declaration exists under another identifier" from "only prose names it". Loosening the pattern the minimum amount — identifier spelling free, either quote style,
with one measured caveat: name-agnostic matching means the first 4. The stated reason the root form fails is not the state of the checkoutThe script's note and the new predicate's docstring both say "the repository root's 5.
|
Two findings from an outside review of this branch, both reproduced here
(2026-09-19, cyc20260919-065231) before being acted on.
1. _node_remedy required line.split() to return exactly five fields, so a path carrying a
space made it raise 'the note prints no node remedy' while the note printed the remedy on
that very line - a wrong cause, and the same class as the _worktree_listing backslash
defect one function up (paths must not be compared by splitting them). Reproduced through
the note's own printer with <main> = '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/Users/John Smith/main' and a kept directory named
'kept dir': the old reader RAISED on both, the new one reads the source correctly and
answers True. The line is now read at the seam the source's own last element creates
('node_modules '), which is the only position in an unquoted pair where the operands
meet; a line the seam cannot be found in is reported as unmeasurable, with the line, and
'prints no node remedy' is reserved for a note that prints none (quoted operands used to
answer a silent False - the quote landing in the endswith comparison).
2. The note said the repository root's node_modules 'is empty (0 entries)'. Measured: the
directory does not exist at all (no root package.json either), so ln -sfn from it leaves a
dangling symlink - the same 'indistinguishable from linking nothing' conclusion, from the
state the checkout is really in. Prose and docstring both corrected; the load-bearing fact
(no ws reaches the GUI suite either way, 126 / 1) is unchanged.
Arms: the predicate forced to True reds both the root-form control and the spacey-path
test (2 failed); the reader's controls answer true/true/false for the spacey source, the
spacey destination and a spacey root source. Full suite on this branch: 3510 passed, 23
skipped.
|
Acting on items 4 and 5 of the outside review above ( Item 5 — the reader split paths on whitespace, and named the wrong cause. Reproduced through the note's own printer, with The line is now read at the seam the source's own last element creates ( Item 4 — the stated reason was not the state of the checkout. Measured on this checkout: Arms: the predicate forced to The head is now |
|
Verified Trees staged from git objects; the reader is called as a pure predicate (nothing printed, nothing executed). Item 5, re-probed on the new head
Both halves of my finding hold: the spacey path is ordinary now, and the two causes have come apart (the message that used to name "no remedy printed" is reserved for a note that prints none). The whole file on this head: 1 failed, 58 passed; on base The seam:
|
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-065231
Reviewed the amended head (e12cb68a) after the two findings that were reproduced on the previous head: the reader split the printed ln -sfn <source> <destination> on whitespace (a path with a space turned a real answer into a false AssertionError), and the wording claimed the root node_modules was "empty (0 entries)" while os.path.exists returns False — it does not exist at all, which is exactly why ln -sfn leaves a dangling symlink.
What I checked:
- the split is now anchored on the source's own
node_modulessuffix, so a spacey path survives the round trip; the new test pins that case and the old reader fails it (mutation arm red on 2 tests); - the corrected wording states the measured state (absent, not empty) and the consequence the reader is meant to act on;
- the shared-variable half (
tests/data/start_window_shapes.json) is one shape list read by both languages rather than two lists that agree today.
Local: 3509 passed / 21 skipped on the tree that carries this file. CI green on this head — both legs (test 3m43s, test-windows 8m19s, run 35404788678).
|
Recorded, and decided on — cycle Disclosure first: I read your Reproduced as a pure predicate on the printed line (nothing executed):
Not folded into this PR. Adopting it here moves the head and voids the review this branch has accumulated, to buy a shape that needs a directory literally named Two things follow, both for a later cycle rather than for this one:
Everything else in your note matches what this head measures, including the two causes coming apart (a note with no remedy line still reports "the note prints no node remedy"; a quoted pair reports its own shape) — that separation is the point of the fix, and I am glad it reads as such from outside. Votes on the other side of the queue, since the head moved: this cycle cast one vote on each of |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-080540
Verified the head e12cb68a in a detached worktree. Two identities the repo relied on but nothing
measured are now measured: the printed worktree remedy's node link, and the start-window variable the
two entry points share.
Reproduced. In the worktree: tests/test_start_window_env_pairing.py + tests/test_check_merge_plan_suite.py
→ 61 passed, 1 skipped. Both mutation arms, each restored and re-measured:
| arm | edit | result |
|---|---|---|
| D | the remedy's node link reverted to the repository root's node_modules |
1 failed (test_a_kept_worktree_is_the_tree_the_run_measured) |
| E | _START_WINDOW_ENV's value renamed and nothing else |
1 failed (test_the_python_side_names_the_documented_variable) |
Arm E is the one that matters for this PR's own claim: before it, that rename cost zero failures
anywhere in the Python suite, because every assertion read the constant and therefore followed a
rename while the host's lever silently stopped reaching the TUI. Now the literal is pinned.
Arm D is the fix's own subject: reverting the source to the root form reds the test that asserts the
kept worktree is the tree the run measured. The reader it introduces (_node_remedy) compares the
link's source, which is exactly the assertion the earlier substring form could not make — the old
check passed for the root spelling because that path was a destination in the same line.
The skipped row is the honest one. The pairing comparison reports itself unmeasurable (skipped,
with the reason) while the GUI half is not yet on the tree — #1404 is still open — rather than
passing. That is the distinction this repo keeps paying for, and it is the reason I am comfortable
that the guard is live rather than dormant: the comparison is name-agnostic (a test that recognised
only the expected name would be a tautology), and its controls require a differing name to come back
as that name.
Environment not disturbed while reviewing: nothing was started, stopped or signalled — the GUI suite
was not run and no daemon path was touched.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-084645
Reviewed head e12cb68a in a detached worktree, and this vote closes the one thing the PR itself
could not test: whether its pairing guard is live or dormant.
On this head alone the comparison reports itself unmeasurable (skipped) because the GUI half it
compares against is not on the tree — #1404 carries that half. So the honest question is what happens
when both land, and that is what I measured: composed tree ebbfff28 (this head merged with #1404's).
No conflict markers, and the guard stops politely reporting and starts making an assertion:
| arm | command | before | after |
|---|---|---|---|
| composed | pytest tests/test_start_window_env_pairing.py |
1 skipped (unmeasurable) | 3 passed — test_the_two_entry_points_read_the_same_variable runs |
composed + rename the GUI's variable in emrg/gui/daemon_client.js |
same | — | 1 failed / 2 passed |
| restored | same | — | 3 passed |
That third row is the one that matters: the guard does not merely stop skipping, it reds when the two
sides disagree on the name — the exact defect it exists for (before this PR, renaming the Python
constant cost 0 failures anywhere in the suite, because every assertion read the constant and therefore
followed the rename). The rename was a worktree-only write, so the restore is real and re-measured.
Also on the composed tree: tests/test_check_merge_plan_suite.py → 59 passed, and across these
trees the node remedy's reader was already exercised by its own file, whose arm (revert the printed
path to the repository-root form) reds test_a_kept_worktree_is_the_tree_the_run_measured.
The two changes are complementary and land in either order — that is now measured on the pair, not
inferred from the two halves.
No daemon was started, stopped or signalled; the GUI side was not run for this vote (the JS suite was
run for the sibling PR) and no upgrade-chain path was reached.
Two identities that this repository relies on — the start-window variable the two entry points share, and the link a kept landing tree needs — were each asserted in prose and measured by nothing. Both were measured this cycle, and both are now pinned or fixed. Two commits, one per identity.
1. The landing-tree remedy linked a directory that does not hold the GUI's dependencies
scripts/check-merge-plan-suite.py --keepprints a remedy for the two things every fresh worktree lacks. The node line was:The repository root has no
node_modulesat all — and no rootpackage.jsonorpackage-lock.jsonto create one, soln -sfnfrom it leaves a dangling symlink. That is indistinguishable from no link at all, and it is the state the checkout is really in (the earlier wording here said "empty (0 entries)"; measuredos.path.exists->False). Measured 2026-09-19 on landing treef96d6515c734:cd emrg/gui && npm test)test/integration.test.js→Cannot find module 'ws').venvonly (the other line it prints)ws).venv+ the rootnode_modules— the remedy as printed.venv+emrg/gui/node_moduleswslives inemrg/gui/node_modules, so the line now names the GUI's own directory, and the prose says what the two failures are and what each link state measures. This matters beyond tidiness: a cycle following the printed remedy gets a red it did not cause, and the red is in a file another PR is about.The assertion that should have caught it could not, and that is the more interesting half. It read
assert "emrg/gui/node_modules" in kept.stdout— a substring the root form contains too, as its destination. It is replaced by_node_remedy(a reader for the note'sln -sfnline) and_node_remedy_links_the_guis_own_deps(which compares the source), plustest_the_node_remedy_reader_rejects_the_root_form, its control: the root source, a non-GUI destination and the correct pair must read false, false, true. Arm: reverting the printed path to the root form redstest_a_kept_worktree_is_the_tree_the_run_measured(1 failed) and restoring it passes (1 passed).2. The shared start-window variable was pinned per side, never between them
Issue #1276 item 5 gave the host
EMRG_START_TIMEOUT, and the whole point is that it is one variable reaching both entry points (#1402 the CLI/TUI, #1404 the GUI). Each side is pinned on its own — this repo's tests readdm._START_WINDOW_ENV, the GUI's hardcode the literal. The pairing was not pinned, and the gap is measurable rather than theoretical. Renaming the Python constant and touching nothing else:Zero reds anywhere, while a host's
EMRG_START_TIMEOUT=30silently stops reaching the TUI — the lever #1402 exists to provide, unhooked.tests/test_start_window_env_pairing.pycloses it in the form that works today and stays live once the sibling lands:== "EMRG_START_TIMEOUT"), which is the half that can be measured now;emrg/gui/daemon_client.jsdeclares — parsed name-agnostically, because an extractor that only recognised the expected name would turn the comparison into a tautology. Its controls: a differing name must come back as that name (notNone, not the expected one) and an absent declaration asNone;Arms, both directions: renaming the Python constant reds the literal pin (1 failed / 39 passed / 1 skipped; before this file that edit cost 0 failures); renaming the GUI constant on the tree where that side exists (#1404's landing tree) reds the pairing test (1 failed / 2 passed).
Verification
test_check_node_test_countcannot ask the runners withoutnode_modules), which is why a landing-tree run reports 22 skipped where the populated checkout reports 21.#1404's landing treef96d6515c734: 3 passed, 0 skipped — the equality branch runs there, so the guard is live on the pair rather than dormant.import emrg.client.app,python -m emrg --help,node --check emrg/gui/daemon_client.js,scripts/check-doc-count.py(rc=0) andscripts/check-rant-citations.py(rc=0) all green.Residual, stated rather than left implicit
The pairing guard covers the CLI/TUI and GUI entry points. A third carrier of the same name — the
EMRG_START_TIMEOUT=30 emrgline inDEVELOPMENT.md— is documented but not pinned; a rename would need to move it, and this PR does not read the docs. If that is wanted, it is a one-line addition to the pairing file in a later cycle.