Files

23 KiB

Row 272 / 264e (#1777): session CLI and Console chooser

Owner Huey; reviewer Scrooge per sage-2816 / mos-1310 item 2 (supersedes sage-2796). Current base 5fdfd7275, branch huey/row272-5fdfd7275 (sage-3354). Original draft base 2d0d407ba; round 1 was 850b22680 on b23783f39. Brief: docs/plans/2026-10-21_t1-sessions.md (ff0be0c79), section 8; row 264 rulings at 5f5b51dd6. Dependencies: 264a-d landed; 270/271 landing is 7328da8ca. Count/clear implementation and contracts are now in the current base. No runtime, launcher, library adapter, role, schema or queue edits belong to this slice.

Execution and evidence

  1. Read session/channel/HTTP/browser contracts and write isolation/validation tests.
  2. Add session CLI through the existing verified human channel, preserving defaults.
  3. Add authenticated Console chooser, per-session drafts/history/cursors, count and clear status. Unavailable dependency verbs refuse explicitly; no simulated success.
  4. On Sage's CLEAR: claim, run focused tests and required suites/mutants, repair and record exact evidence. No Node, tests, HTTPS git, claim or push under Gander's gate.
  5. After 253 and 269 land, rebase on a new branch name. Review packet to Scrooge, Mos, Gizmoduck and Sage; push only when permitted.

2026-10-22 preparation: clean worktree switched locally to the requested base. Read approved brief and all Q1-Q7 rulings. Della notified: no startBroker/library changes. Gizmoduck asked to pin count/clear contracts. Tests will be written first; Sage's explicit gate restriction defers RED/GREEN execution. No test result claimed.

Verification plan: CLI real-broker isolation and channel refusal; HTTP strict keys, Origin/Fetch Metadata, forged selector/refusal and no retries; fixture-browser two sessions, isolated draft/cursor/receipt, deep-link/refresh, count unknown/known, read-only writes, clear confirmation/pending. Add selector omission and cursor/draft sharing mutants. Broader cli/webui/plugins suites use Sage's gate only.

264c/d contracts received gizmoduck-0260: session.list gains compactions and revision/state; session.clear and session.operation use the pinned envelopes. CLI polls at most 30 s then reports pending; Console status checks use GET only. Counts absent on 264b show unavailable; unknown never becomes zero. Clear absent on 264b is disabled with a reason. USER_VERBS and tester listener write policy remain unchanged (Q7). Existing collaborators therefore cannot enter the new session surface until the reviewed RBAC integration.

Consumer audit found the explicitly deferred B6 user-reply scope (DEFERRED.md:455-459, queue-264/BUILD.md:317-322) bound to 272. Added a narrow broker predicate change from thread to session and a sibling-session regression fixture, without widening user verbs. This is the only planned broker change and does not overlap Della's runtime adapter.

2026-10-22 static review: added explicit --session to thread read/open and talk/follow; omission remains the saved default. CLI clear first reads a recorded operation, so a repeat after a lifecycle revision change cannot become a conflicting second write. Console deep links name both thread and session; accepted-send receipts, drafts and history pagination use that tuple. Clear-status GETs refresh the envelope without reissuing clear. Read failures discard private metadata; late generations cannot paint another selection. Scrooge is reviewer per sage-2816 / mos-1310, superseding the first assignment. CLEAR remains after 269, 275, 203 and 207. No execution evidence yet.

Acceptance / mutant map (prepared, unexecuted)

Check Evidence file / intended kill
T5 legacy human, forged agent, foreign session, strict HTTP identity/Origin/site session-cli.test.mjs, chat.test.mjs; unchanged USER_VERBS matrix in user-principal.test.mjs
T7 saved default, explicit CLI session selector, metadata-only create session-cli.test.mjs real broker and thread CLI compatibility tests
T7 Console chooser, deep link, draft/cursor/receipt isolation, unknown count, confirmation/pending chat-browser.test.mjs; existing history/sign-out/late-read tests run whole
Count/clear contract absence CLI unavailable refusal; Console disabled clear reason; exact zero vs unknown lower bound tests
B6 assigned follow-up user-principal.test.mjs sibling-session citation refusal and valid same-session reply
M1 omit session in chat.mjs messages forwarding chat.test.mjs explicit selector deepEqual fails
M2 collapse browser state key from thread/session to thread chooser draft isolation assertion fails
M3 treat known:false count:0 as exact zero CLI label assertion and browser unknown-count assertion fail
M4 restore B6 mt.thread/s.thread predicate user-principal sibling refusal assertion fails
M5 remove session from CLI thread.send payload explicit --session talk assertion fails
M6 repeat a clear write in place of session.operation poll CLI exactly-one-clear assertion fails

Intended focused commands, only after CLEAR, one process at a time:

  • timeout -k 10 300 node --test packages/cli/tests/session-cli.test.mjs
  • timeout -k 10 300 node --test packages/cli/tests/thread-cli.test.mjs
  • timeout -k 10 300 node --test packages/bus/tests/user-principal.test.mjs
  • timeout -k 10 300 node --test packages/plugins/tests/channel.test.mjs
  • timeout -k 10 300 node --test packages/webui/tests/chat.test.mjs
  • timeout -k 10 600 node --test packages/webui/tests/chat-browser.test.mjs

Whole touched suites on Sage's slot: bus (900 s), cli (1800 s, >=3x recorded 591 s), webui (600 s), plugins (600 s), plus required integration scripts per final gate brief. Mutants run in disposable copies of the pinned candidate, never against a live stack; only failed assertions count as kills, never a timeout or import/setup failure. No test process, browser, provider, credential read or live service action ran in this preparation. git diff --check is the only executed draft verification so far.

Next: Sage CLEAR for claim/focused runs; repair against actual results, then rebase on a newly named branch once 253 and 269 land. Generate final manifest only after those changes. Review handoff is Scrooge and Mos, copying Gizmoduck and Sage; no handoff to Duckworth under the superseded assignment. No candidate commit or green claim yet.

2026-10-22, gizmoduck-0272 / sage-2828 additive contract at 78e14352f: added session.cancel to CLI/channel/Console with confirmed human writes and strict keys. Cancelled envelopes retain retired:null (old active) or true (startup cancelled, old retired); cancelled is terminal for polling. CLI reports routed message cancellation with stored ID and exit 3, never waits for delivery or replays it. HTTP maps session-name-too-long and session-clear-complete without reflecting transport text. Added unexecuted before/after cancellation, identity/Origin, confirmation and message receipt tests. Existing clear/create/read wiring is preserved. Asked Gizmoduck whether the deterministic successor keeps 264a's UUID shape, as selectors currently require. No Node, test run, claim, commit, push, provider call or live action. Static diff only.

Gizmoduck-0274 resolves the selector compatibility check: successors are lowercase RFC 9562 v8 UUIDs (variant 10), while 264a sessions are v4. Existing generic UUID validators accept both and stay unchanged. CLI explicit-selector and Console chooser fixtures now include mixed v4/v8 IDs to prevent a later v4-only regression. Unexecuted.

sage-3141 rebase and landed-contract check (2026-10-23)

Reapplied the uncommitted draft onto b23783f39 in the same isolated worktree, on new branch huey/row272-b23783f39. No candidate commits existed to replay. Preserved the original draft in the named local stash huey row272 draft before sage-3141 rebase. Product files merged without conflict. Only append-only BUILD-LOG.md and SESSIONS.md conflicted: kept every new-base byte and appended the original Huey entries unchanged. No force push, fetch, Node, test, queue write or live change. No claim or candidate commit yet. Static git diff --check clean after resolution.

Checked the actual landed broker, not just the older contract packet:

  • broker.mjs:687-707 clearEnvelope supplies the same op/thread/session/successor/state/ start/refusal/at fields and cancelled-only retired:null|true. The generic UUID selectors still accept both original v4 and successor v8 IDs.
  • broker.mjs:6227-6245 session.list supplies compactions, state, revision and successor; count reader/unknown behavior is the landed 270 contract. Real-base tests must now exercise these available fields; stand-ins still exercise an unavailable dependency.
  • broker.mjs:6270-6330 clear/cancel/operation inputs and receipts match the wired contract. clear repeats preserve the op; cancel also refuses a failed clear as session-clear-complete. Our existing refusal mapping already covers it.
  • queue-271/BUILD.md:225-238 clarifies cancellation after retirement cancels this clear's startup, while later deliberate human open/send can start the fresh successor. It does not permanently disable that new session. No frontend authority change needed.
  • store.mjs:17 is 3n; migration is 3m-3n.sql. This slice adds/pins no schema step, migration blob or schema test count. No DDL copied from an older branch.

Plan after the Gander 574 run-2 hold: on a claims-only CLEAR, claim only; run no Node or tests beyond the explicit scope of Sage's grant. On test CLEAR run new CLI tests first, then new broker/HTTP/browser cases and whole touched files, sequentially under the recorded timeouts. Place/report a first-run red only in a new test; do not alter an old test, loosen an assertion or fix a second red without Mos's ruling via Sage. Whole-suite/mutant execution remains on Sage's slot. Candidate commit and push follow applicable green evidence and an allowed gap, then Scrooge/Mos handoff with final SHA, manifest, copying Gizmoduck/Sage.

Existing-test edits in the inherited draft need Mos's ruling through Sage before execution: channel.test.mjs extends its existing supported-verbs table for five session verbs; chat-browser.test.mjs extends the existing fetch fixture with session catalog/operation responses and changes its exact send-payload assertion to include the selected session. Assertions are not loosened. All other added test cases are new tests. These changes were prepared before sage-3141 and have never run. Ask Sage to forward this precise delta rather than treating a later old-test red as permission.

sage-3145 correction: refs/stash is shared across worktrees; using it as sole draft storage was wrong. Pinned the exact draft commit 07dc9d2f7944b4781b5e80dfa18425f4749f7a45 on local branch huey/row272-stash-keep before any stash cleanup. The entry remains untouched; the branch retains its tracked and untracked snapshots. Future seat draft preservation uses owned branches/local commits, not shared stash positions.

Sage explicitly directed a local draft commit before tests to preserve the rebased work. This overrides the normal green-before-commit gate for this storage checkpoint only. It is an untested draft, not an approved candidate, test result, push or landing. Claims await the post-574-run-2 CLEAR; test execution awaits Mos's ruling and Sage's separate test CLEAR. Old-test deltas were forwarded as sage-3144; no ruling assumed.

Scrooge early static read, 850b22680 (2026-10-23)

Review key scrooge-272-early-read-huey-1. B1 confirmed: the render catch used any 403/404 to delete every sibling's draft/receipt and clear/create operation key. Uncommitted follow-up restricts that purge to thread-not-owned, thread-not-found and access-refused. Session-not-found still shows an unavailable read, preserving sibling state and pending recovery coordinates. New browser test retains A's draft and a lost clear key across a bad-session deep link, then proves Clear queries the retained op without another write.

N1: malformed clear/cancel write receipts now add the same redacted outcome-unknown audit line as other writes. New HTTP test checks one write log and no read log. N2: a valid recorded clear belonging to another session now refuses with a precise operation-key-reuse message (exit 2), rather than ambiguous outcome unknown; new CLI test proves no clear write. Malformed envelopes retain the outcome-unknown refusal. No approved old assertion or shared stub edited. Static diff check only; all new tests remain unrun pending Sage's test CLEAR. Published round 1 still pins 850b22680 and its original manifest; these follow-up edits do not replace that evidence. No new commit, push, queue write, provider call or live change for this early-read follow-up.

Execution watch: inspect message-filter.test first if the bus suite reports a B6 fixture red (manual message_threads rows must name the run's bound session); do not change an old fixture/assertion without Mos's ruling via Sage. thread-shares uses the persisted session_defaults mapping per Scrooge's check. No runtime verdict claimed.

Granted-user legacy compatibility (sage-3357; pre-candidate design)

219/295 are retained on base 5fdfd7275. USER_VERBS still excludes session verbs; 193/T5 user session grants are a separate DEFERRED item, outside 272. No owner transport carries a user session operation. Bounded negotiation applies only to the session.list read: only access-refused and an omitted-session route permit legacy thread-default read/send/open. All other errors keep the unavailable/error state, and explicit session links always refuse. On fallback session controls are hidden and one status line says sessions are not available for this account. No fake session ID, count or lifecycle status is invented; the broker continues choosing its persisted default under current grants on every existing thread request. Owner routes keep the normal chooser and explicit selected-session arguments.

New file chat-sessions-access.test.mjs uses the real broker with an already granted principal over loopback and the existing repository browser fixture. It checks legacy read/send/open with no session controls, explicit-link refusal, another-error refusal, and unchanged owner chooser. No shared stub or existing assertion changes. The three required mutants remove the access-refused check, remove the omitted-session check, and display the controls during fallback; guard tests must kill each by assertion.

RED-first slot sage-3358: only this new file, before fallback implementation. Expected run 100 s including four Chromium fixtures; bound 300 s (3x), command timeout -k 10 300 node --test packages/webui/tests/chat-sessions-access.test.mjs. TMPDIR must be assigned from mktemp -d, with no underscore. No compose service is used; check/remove the generated directory's compose default network if any exists. Expected legacy-positive red is placed/reported afterward under mos-1272. Any other red is reported to Sage before a fix. No other test file/suite is authorized in this slot.

sage-3358 RED run completed before the fallback implementation. Only the new chat-sessions-access.test.mjs ran, under the 300 s bound, TMPDIR allocated by mktemp -d /tmp/huey272-red-XXXXXXXX as /tmp/huey272-red-0q66Jcf4. Exit 1; 4 tests, expected legacy-positive assertion red and 3 passes, no other reds/skips/ cancellations/timeouts, 3.504 s. Exact output: r2-red-access.tap. No matching compose network existed. Slot returned to Sage/Mos as huey-0035; no further tests run.

mos-1442 / sage-3361 adds explicit timeout, bus-error and malformed-answer refusal coverage. The new private fixture adds those guard cases (unrun), with a widening-key mutant alongside the access-refused/omitted-selector/control mutants. No shared stub or existing assertion changed. Native preview status/open both reported unavailable in this headless environment before the authorized repository-browser regression.

Implementation now local: the catalog request catches only access-refused on omitted session routes. Its internal null result selects the existing broker-chosen thread default with no fabricated session ID. Read/send/open omit the session property in that mode. One status line replaces all session controls. Explicit links and any other failure never select that mode. USER_VERBS and all owner transport routing remain byte-unchanged. Guard cases are preserved on the pre-fallback baseline; their RED evidence comes from the required mutants, as Sage accepted in sage-3358.

Fallback mutants are executable via fallback-mutants-r2.py, only on Sage's slot. It copies packages/roles into a private mktemp directory and mutates only the copied chat.js, never candidate bytes. Each run has a 300 s bound and requires a named assertion failure; timeout/import/setup failures do not count. F1 removes the exact access-refused key (human-required guard); F2 removes the omitted-selector check; F3 shows session controls; F4 widens the key to timeout/unreachable, no-bus-host and invalid-response (timeout guard). The test file also independently checks bus-error and malformed-answer refusal. These mutants and the post-implementation tests have not run locally. The earlier six non-fallback mutants in this packet remain required in the full gate; the new runner covers the four fallback mutants only.

Round 2 rebase receipt (sage-3354)

Queue rev 3510 returned row 272 to in-progress. On new branch huey/row272-5fdfd7275, replayed round 1 and the preserved B1/N1/N2 fixes onto 5fdfd7275 (219, after 295). Resolved exactly the seven named conflicts. Test append conflicts retain both complete sets of tests; existing test content equals the new base plus only the mos-1388 hunks. No old assertion or shared stub was changed beyond those approved hunks. Static byte comparison confirms the entire upstream test file prefixes and approved modifications. Both append-only logs retain the complete 5fdfd7275 prefix plus Huey's entries.

219's per-instance thread picker, explicit createThread instance forwarding, choice refresh/revocation behavior and tests remain present. 295's createChat owner-settings read stays unchanged: only plugins uses the owner transport; user chat verbs stay on the user transport. Removed a duplicate access-refused status entry introduced by combining those patches. USER_VERBS is unchanged. No schema or migration changed.

This is a pre-gate candidate per Sage's explicit instruction to commit/push/request round 2 before Sage runs the full gate. Only the expected-red new fallback test ran under sage-3358. No green test claim, no suite/mutant result and no old-file run is implied. Every touched test file still needs its whole-file run on Sage's slot, plus all touched suites and mutants. Any first-run red in launcher/creative/chat-browser returns to Mos via Sage before fixing. TMPDIR for gate runs comes only from mktemp -d, with no underscore; remove a matching compose default network only if created.

Round 3 preparation — gate run 1 (sage-3433)

Sage ran candidate b44332a93 over refactor 1e4c1e6e9 with the pinned esbuild and MOSAIC_REQUIRE_EDITOR_BUILD=1. Gate RED: bus 891/0, business 231/0, CLI 940/1, plugins 110/1, webui 372/20. No mutants ran. Sage reproduced every failing file on b44332a93 alone, excluding overlay drift as the cause. Evidence: /mnt/storage/scratch/sage-r272/run-.txt and split-.txt.

A: the new session CLI real-broker fixture omitted role authority. Verified checkBusiness requires withinRole and crossRole arrays on every role; added empty arrays to chief and assistant in that fixture only, as sage-3433 permits. No production authority or existing assertion changed. No local tests ran.

B remains held for Mos under mos-1316/1317: initial bare-chat rendering compares old?.id with undefined id, then dereferences null old at the sessions/session/key initializers. Sage proposes old && old.id === id at all three sites. No B edit made pending the ruling. C (plugins harness compactions) is separately owned.

Round 3 is not committed or pushed yet. The manifest path set remains the same 17 paths; regenerate hashes when the authorized fixes are complete. Future launcher, creative and chat-browser runs use a TMPDIR allocated with mktemp -d /mnt/storage/scratch/tmp/XXXXXX, because bwrap hides /tmp. Sage owns the next full gate. No tests, repo Node or queue writes here.

Round 3 candidate — mos-1455 / sage-3435

Mos authorized B: all three initial render comparisons now use old && old.id === id, so an initial list route cannot dereference null old. A remains fixture-only. chat-browser.test.mjs and granted-user.test.mjs are byte-identical to b44332a93. No pre-existing assertion, authority validator or shared stub changed.

Frozen manifest: manifest.r3.txt, 17 paths unchanged; SHA256 f98c5b7a884ed1e5951a584227ece16d403baf62199d405aaa1981d667e9e075. Parent candidate: b44332a9360504291dda3f81131c934b286f3226; base remains 5fdfd7275. Only session-cli.test.mjs and public/chat.js product hashes differ from round 2. This follow-up commit/push is authorized before the round 3 gate by sage-3435. No local tests, repo Node, mutants or queue writes ran. Gate remains RED until Sage reruns; plugins harness compactions is a separately owned dependency, and Mos declined an EXCEPT edit. Scrooge reviews this A/B delta; Sage posts round 3.

Round 4 — scoped browser heading waits (mos-1460/1461)

Round 3 gate on 72690cae7: bus 891/0, CLI 941/0, business 231/0; plugins had only the allowed exact base red, checked by Mos; webui 390/2. Both reds were newly added chat-browser missing-session waits at lines 361 and 437. Whole-file rerun reproduced 17/2. No round 3 green acceptance claim.

Diagnosis authorized by sage-3450/3453 and mos-1460: unchanged whole-file run 17/2, 28.223 s; scratch-only CDP probes showed the route reaching chat.js:176, catalog rows A/B without M, and catch receiving session-not-found with status 404. DOM had first h1 "Control board" (shell), then .chat-area h1 "Thread unavailable". Product refusal and painting worked. Evidence retained under /mnt/storage/scratch/tmp/hueyP1enyB/ (baseline.tap, probe.tap, probe-catch.tap, proposed.diff); scratch probe source removed, tracked tree remained clean.

mos-1461 / sage-3458 authorizes exactly two selector changes: document-wide h1 becomes .chat-area h1 in those waits. Expected text, timeout and every other line of chat-browser.test.mjs remain byte-identical to 72690cae7. This scopes the same assertion to its component; it does not weaken the composer/write/draft/recovery checks. No production code changes in round 4.

manifest.r4.txt retains the same 17 paths; SHA256 a47ed1a23fb98c1ffe6cbcc4e7130e5c515387a2c5dc41e5ae1cbf04640d8c1b. Only chat-browser.test.mjs hash differs from r3. New local follow-up commit is authorized before execution; push and single-file run await Sage CLEAR while Gizmoduck owns the slot. Required pass: whole chat-browser.test.mjs, 19/19 and skipped 0, timeout -k 10 300, TMPDIR from mktemp -d /mnt/storage/scratch/tmp/hueyXXXXXX. No round 4 run yet. After green, claimant moves row to in-review; Sage posts request and Scrooge reviews the exact two-line delta. Full five-suite gate remains Sage's.