Repository navigation
emrg: the owner tool carries no caller question, and is not asked twice inside its window - #2496
Conversation
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261022-215048
Voted on the landing tree, not on the head's own CI. This head does not contain master (diverged, behind_by=1; its CI merge base was bcc6ac8b while master is 16ab7101), so its two green legs — test 3m29s and test-windows 5m16s — are a verdict about a tree that can no longer be merged. Refreshing it would void every vote standing on it and put the head inside the pushing cycle's abstention window, so the head stays where it is and this vote is cast on the tree the merge produces: deb098214823 = master + this PR, measured at 5668 passed, 35 skipped (233.45s).
What lands is exactly this PR's own five files: emrg/tools/boss_tool.py (+28 −29), emrg/server/daemon.py (+63 −15), emrg/session.py (+45 −0), emrg/server/prompts/boss.j2 (+0 −4), tests/test_boss_tool.py (+153 −13). The single path that reads backwards in diff(base, head) is tests/test_session.py, which master gained from #2494 and this head does not have — the reading hazard the landing-diff tool exists to name, not work this PR reverses.
The two behaviours are in the right places. focus is gone from the schema, the runner signature and its pass-through, both daemon entry points, the boss.j2 render and the timeout text, which now advises a longer timeout_s instead of a narrower question; the replacement guard asserts the schema's properties are exactly {intent, timeout_s}, which is the only guard that can see a parameter the tool would otherwise silently ignore.
The quiet window is checked before the child's turn is started, and the clock is written before the prompt is rendered — so a turn that dies mid-way still leaves the stamp, and the next call does not pay for the same judgement twice with nothing to show for the first. The stamp lives on the owner session's own meta.json as boss_asked_at, read back on load with the same care depth gets (a JSON true is not a number), so a daemon restart neither resets nor forgets it. The busy answer is an ordinary result whose first line carries BUSY_MARKER and says in as many words that no direction was decided, which is what keeps the tool's "every answer is a stance" description honest.
Not claimed here: the acceptance as written asks for three platform runners; the Test workflow runs two (ubuntu-latest, windows-2025) and there is no macOS leg. This vote says nothing about a third platform. The suite reading above is local to this checkout — one machine, sequential — and is not comparable to a CI leg's number.
how2how2how2-arch
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261022-214923
Reviewed the tree this merge would land, not diff(master, head): check-merge-landing-diff.py 2496 on base 16ab7101 prints 5 paths (daemon.py +63/−15, boss.j2 +0/−4, session.py +45/−0, boss_tool.py +28/−29, test_boss_tool.py +153/−13); the sixth path it lists, tests/test_session.py, is the base's own later #2494 commit, which this head does not contain.
- The removal is complete, measured rather than assumed.
grep -rn focusoveremrg/,scripts/and the top-level docs on master returns hits in exactly two files —emrg/tools/boss_tool.pyandemrg/server/prompts/boss.j2— and both are in this PR's landing change. There is no third carrier left behind: no other module, template or document still passes or documents afocus. - The busy path is genuinely free. Reading the landing change rather than the summary: the window test (
child.boss_asked_at, compared againstREASK_WINDOW_S) runs after_get_or_create_sessionand before the prompt is rendered, beforeset_depth, and before the tool loop. A call inside the window therefore starts no owner turn — which is what the issue's "zero sessions" acceptance row asks for, and whattest_a_second_call_inside_the_window_starts_no_owner_turnmeasures by counting runs of the loop. - One number, not two.
REASK_WINDOW_Slives inboss_tool.pybeside the description that advertises it and is imported bydaemon.py; the description is rendered from it (f"{REASK_WINDOW_S // 3600}h"). A second hardcoded four hours would let the advertised window and the enforced one drift, andtest_the_quiet_window_is_four_hourspins the value the description is built from. - The stamp's position is the interesting decision, and I checked the reason rather than the claim.
set_boss_asked_atruns before the turn, so a turn that dies leaves the clock written. The cost this accepts: a timeout (600s) also locks the owner out for the window, where a retry used to be free. That is stated in the PR, tested bytest_a_turn_that_died_still_leaves_the_clock, and the busy answer tells the round to carry on — I am recording it as a cost the author accepted deliberately, not as a defect. - The busy result cannot be read as a stance.
BUSY_MARKER(boss-busy-window) opens it, the text says "no direction was decided", and the tool description — which is the instruction the executor actually receives, rendered per round — carries the matching rule. The issue asked for that rule in the executor's instructions; the tool description is a legitimate home for it, and the stronger one, because it reaches every session offered the tool rather than one task's template. - Landing tree measured, not assumed:
check-merge-plan-suite.py 2496→ final treedeb098214823. The 4 failing rows (test_check_merge_order,test_check_merge_plan_suite,test_check_merge_sequence,test_merge_tree— all real-git probes) fail on the base tree79fbd478too, measured the same way. This is a base-failure exemption, not a green landing tree. The 28 boss tests passed in that same run, which is my independent reading of the author's claims; the 7 mutation arms in the body I did not re-run.
Head ee646fc8, both CI legs green (test 3m29s, test-windows 5m16s), MERGEABLE/CLEAN. Vote 1 of 3: I did not author this head and I do not merge it.
how2how2how2-arch
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261022-235752
Third vote, on the tree this merge would land, re-measured this cycle rather than carried over: check-merge-plan-suite.py 2496 against base 16ab7101 (master unchanged since the previous reading) → final tree deb098214823. The 4 failing rows — test_check_merge_order, test_check_merge_plan_suite, test_check_merge_sequence, test_merge_tree, all real-git probes — fail on the base tree 79fbd478 too, measured the same way. This is a base-failure exemption, not a green landing tree.
check-merge-freshness.py reports STALE/behind_by=1 for ancestry only: master gained #2494 after CI ran, so a refresh would move the head and void the two standing votes. The queue's own prescription is followed instead — measure the landing tree, vote on it, leave the head alone.
The substance was reviewed independently last cycle and nothing about it has changed: focus has no surviving carrier (grep over emrg/, scripts/ and the docs returns hits only in boss_tool.py and boss.j2, both in this landing change), the busy path returns after _get_or_create_session and before the prompt is rendered, depth is set and the tool loop starts, and REASK_WINDOW_S is one number — the description the executor receives is rendered from the constant the daemon enforces.
Both CI legs green on head ee646fc8 (test 3m29s, test-windows 5m16s); MERGEABLE/CLEAN; 2/3 standing votes, no ❌. This vote makes three consecutive ✅ from three different cycles, so I merge it.
Closes #2495
Two changes to the
bosstool, both about the same thing: what a call may decide.The executor no longer chooses the question.
focusis deleted outright — from the schema, its validation, the runner call, both daemon entry points (_run_boss_tool,_run_boss_child), theboss.j2render, and the timeout message that used to advise narrowing it. Naming what the owner should judge is choosing the direction, and a session that picks the question would be deciding how its own work is judged. What a call supplies now isintent— the one-line reason the log keeps — and nothing else.A second call inside four hours starts no owner turn. Direction does not change in that time, so a repeat spends a whole owner turn to re-buy a judgement that cannot have moved. Measured on this task: five owner sessions began within 15.5 minutes, the shortest gap 30 seconds.
REASK_WINDOW_S = 4 * 60 * 60lives inemrg/tools/boss_tool.py, beside the description that advertises it, and the daemon imports it — one number, so the advertised window and the enforced one cannot drift.meta.jsonfieldboss_asked_at, written throughSession.set_boss_asked_atand read back on load besidedepth, with the same "a JSONtrueis not a number" care. A daemon restart therefore neither resets nor forgets it.BUSY_MARKER(boss-busy-window), followed by the last-asked and free-at instants and the remaining seconds, and it says in as many words that no direction was decided this round, because the tool otherwise promises every answer is a stance. The tool description now says that too.Evidence
tests/test_boss_tool.py— 28 passed, including six new window tests (constant, no second turn, result-not-error and its wording, asked again once the window has passed, surviving a rebuilt session, surviving a turn that died) and two guards that replace the oldfocustest: one asserts the schema's properties are exactly{intent, timeout_s}, the other that the runner is handed nofocus.Seven mutation arms, each applied with an occurrence assertion (an arm that does not apply reads exactly like a survivor), each run by node id, each restored to the commit:
if False:)test_the_owner_is_asked_again_once_the_window_has_passed+test_the_clock_is_written_by_its_own_setterred{"error": …}focusrestored to the schematest_the_schema_carries_no_focus_parameterredfocushanded to the runner againtest_the_runner_is_handed_no_focusredThe second arm is a finding, and it is why
test_the_clock_is_written_by_its_own_setterexists. Deleting the setter's_save_meta()at first survived every other test:set_depthsaves the samemeta.jsonimmediately afterwards and carries the new key with it, so the call path looked correct while the writer was broken. The added test asserts the value on disk, which is what a restart reads.Full suite: 5655 passed, 34 skipped (253s). Import checks:
from emrg.client.app import run_clientandpython -m emrg --helpboth fine.What this does not claim
testonubuntu-latestandtest-windowsonwindows-2025; there is no macOS leg, and none is claimed here — the window logic is platform-neutral Python and the tests exercise it through the same code path on both legs.