Files

8.0 KiB

Row 220: C2-10a, no raw session view (M7) and no raw Discord engine (M8)

Phase A. No test, suite or mutant has been run. Sage opens the slot.

  • Owner: researcher seat (Haiku 5.5 subagent), dispatched by Sage under lead decision 129.
  • Reviewer: Filbert.
  • Branch: researcher/row220, from origin/refactor at d37210288.
  • Brief: docs/plans/2026-10-20_case2-rows.md, C2-10a (line 495) and "Rules every row keeps".
  • Design: agents/gosalyn/work/case2/DESIGN.md on origin/gosalyn/case2-design, sections 5.1 (M7, M8), 7.6, 8 (row list).

Status

Item State
M7 reader side (conversation-not-listed) Built. Tests written, not run.
M7 production wiring (the caller that passes isCreative) Not built. Outside the file list, and it needs a ruling (question 1).
M8 discord check and discord run refusals Not built. packages/discord/src/cli.mjs is untouched. Blocked on question 1 and question 2.

Phase A is therefore partial. The row cannot pass its acceptance until Sage rules on the questions below.

Files

  • packages/conversation/src/reader.mjs: exports NOT_LISTED; createReader takes isCreative (default () => false); catalogue(), open() and next() hide a root for which it returns truthy.
  • packages/conversation/tests/reader.test.mjs: three new tests, inserted before the "unknown conversations" test.
  • agents/researcher/work/queue-220/BUILD.md: this file.

Nothing else is changed. node_modules in the worktree is a symlink to the canonical checkout and is ignored by git.

Brief and design items mapped to tests

Item Test
M7: a creative session file under the root isn't listed "M7: a creative seat's session is not listed and won't open by id; an assistant seat still lists"
M7: and won't open by id (conversation-not-listed) same test; second check of the open refusal in "M7: a creative session already open refuses its next page once the root is hidden"
M7: an assistant session still lists same test (the sage seat is listed and opens with its text)
M7: an already-issued cursor stops once the root is hidden (my addition; the design's "won't open" implies it) "M7: a creative session already open refuses its next page once the root is hidden"
M7: refused rows and the unsupported placeholder of a creative root are hidden too (my addition; the design hides "creative sessions", and a refused row or placeholder names one) "M7: a creative root's refused files and its unsupported placeholder are hidden as well"
Default behavior without isCreative is unchanged the existing reader suite; the default predicate returns false
M8: discord check / discord run refuse discord-engine-not-allowed on a library business, and a mosaic-bus binding with tools none (not built, question 1)
M8: a pi binding on a business without a library still checks clean none (not built, question 1)

Every test's fixture text is CREATIVE-MARKER or a made-up assistant line. The M7 tests assert the marker is absent from the catalogue and refusal output.

Choices made in phase A

  • isCreative(root) is the reader's only input. The reader reads no business file. A truthy answer hides, so a doubtful answer fails closed. The predicate must depend on root.seat and root.project only, because a cursor is checked against the root object from an earlier listing.
  • Hidden check placed after the cursor's actor/purpose/conversation/branch checks in next(), and before the source is read.
  • open() checks hidden after the id resolves, so a creative id gets conversation-not-listed, and an unknown id still gets unknown-conversation.
  • conversation-not-listed is reconcile: false. The design names no reconcile flag. unknown-conversation uses true. Question 3.
  • The default predicate is () => false. Until a caller passes one, M7 does nothing. This is the open risk, and question 1 covers it.

Grep over packages/*/tests and scripts/test-*.sh (static, phase A)

Changed path packages/conversation/src/reader.mjs:

  • packages/conversation/tests/reader.test.mjs:14 imports rootsFromSpecs, createReader, conversationId, cwdInProject, UNSUPPORTED_HARNESS. It is the file under change.
  • packages/control-board/tests/serve.test.mjs:26 imports UNSUPPORTED_HARNESS. That export is unchanged.
  • packages/library/tests/sync.test.mjs:15 imports packages/library/src/reader.mjs, a different file. Not a hit.
  • scripts/test-*.sh: no reference to the conversation reader.
  • 64-hex pins naming this path: none. packages/bus/tests/grant-migration.test.mjs:24 has a 64-hex value, but it is a DDL digest, not a pin on this path. That file matched only the word "reader" in a test title.

Changed path packages/conversation/tests/reader.test.mjs:

  • No other test or script names this file. It runs through packages/conversation's node --test tests/.
  • No 64-hex value in the file.

Changed path agents/researcher/work/queue-220/BUILD.md: no pin.

node --check passed on both changed .mjs files.

Phase B plan (not run)

The reader test uses os.tmpdir(). Set TMPDIR to a scratch dir first, per the rule against /tmp.

export TMPDIR="$(mktemp -d /mnt/storage/scratch/tmp/r220XXXXXX)"
cd packages/conversation && node --test tests/reader.test.mjs
cd packages/conversation && node --test tests/
cd packages/discord && node --test tests/
cd packages/control-board && node --test tests/serve.test.mjs

The first command is the touched file whole. The second and third are the conversation and discord suites whole, as the brief asks. The control-board serve test is not in the brief. It consumes reader.mjs, so I recommend it. Sage can drop it.

Questions for Sage

  1. Business link (blocks M8 and the M7 wiring). Neither rule can be built from this row's file list, because the inputs don't exist in the code:

    • A reader root carries seat (the spec's agent) and project (the repo directory name). It has no business id. Roots come from control-board scan specs (packages/control-board/src/scan.mjs, discoverRepoAgents) and seat registrations. Neither carries a definition.
    • A Discord binding has seat and no business key (packages/discord/src/binding.mjs, TOP_KEYS).
    • Creative status lives only in the business file: roles.<instance>.definition in CREATIVE_DEFINITIONS (packages/business/src/vocabulary.mjs:176). A library project is projects.<id>.kind === "library" (packages/business/src/business.mjs:243).
    • Creative thread sessions are stored at <dataRoot>/threads/<business>/<thread>/session (packages/seat/src/session.mjs:59-64), outside the .pi/state roots the board scans. M7 may therefore reach only creative seats that have a .pi/state session root. Please confirm.
    • The design does not define the mapping from a seat to a business.

    Recommendation: add a required business key to the binding, validated against businesses/<id>.json. The board builds isCreative from loadBusiness(id): a root is creative when its seat is a roles key whose definition is in CREATIVE_DEFINITIONS, and the business has a library project. This needs file scope for packages/discord/src/binding.mjs and cli.mjs (M8), and packages/control-board/src/serve.mjs (M7 wiring). None of those is on this row's list. I need that scope amendment and the mapping ruling before I build M8 or the wiring.

  2. M8 refusal code for a mosaic-bus binding with tools. The design names discord-engine-not-allowed for the engine case and no code for the tools case. A new code goes to you first. My proposal is to reuse discord-engine-not-allowed with a message that names the tools key.

  3. reconcile on conversation-not-listed. I chose false. Confirm or change.

  4. HTTP status for conversation-not-listed. REFUSAL_STATUS in packages/control-board/src/serve.mjs has no entry, so the route returns 422. My proposal is 404, next to unknown-conversation. This needs that file on the scope list too.

Phase A stops here. On your "slot clear" I will run the phase B commands above. I will not build M8 or the wiring until question 1 is answered.