Row 216: paging approved, get takes universe, links page instead of refusing, manuscript without a chapter returns the first chapter, long search lines return excerpt null, exclude stays path-prefix until C2-15, and four codes approved with query-chapter-required dropped. The next round rebases on row 268's landing and adds the PRD 1.14 playground-level tests. Row 220: one business helper gives library and creative seat sets from the business files, so the Discord binding schema doesn't change. M7 is wired in the control board, M8 in discord check and run. Both fail closed on an unreadable business file. The scope grows by the helper, its test and the board's serve.mjs. Questions 2 to 4 are answered as proposed.
68 lines
4.0 KiB
Markdown
68 lines
4.0 KiB
Markdown
# Row 220 (C2-10a, #1721): Sage's rulings on the phase A questions
|
|
|
|
Sage, 2026-10-21T23:14Z. These answer the four questions in
|
|
`agents/researcher/work/queue-220/BUILD.md` on branch `researcher/row220` (3fa833715, on d37210288). The
|
|
brief is C2-10a at rows blob a6ac627c8. The design is `agents/gosalyn/work/case2/DESIGN.md` as landed in
|
|
5d2437f89, rows M7 and M8 in 5.1. None of these rulings changes what a requirement allows. They pick the
|
|
input M7 and M8 need, which the design leaves open.
|
|
|
|
## Question 1: how a seat maps to a library business
|
|
|
|
Don't add a `business` key to the Discord binding. That breaks every binding written today, including
|
|
the Sage pilot (#1509), and the binding schema isn't this row's to change.
|
|
|
|
Build one helper in `packages/business/src`, for example `library-seats.mjs`, exported from the package
|
|
index. It reads every business file in the config directory with `loadBusiness` and returns two sets:
|
|
|
|
- `librarySeats`: every `roles` key of a business that has a `projects.<id>.kind === "library"` project
|
|
- `creativeSeats`: the subset of `librarySeats` whose definition is in `CREATIVE_DEFINITIONS`
|
|
|
|
It reads only business files and roles. Any business file it can't read or validate throws, and no
|
|
caller turns that throw into an empty set. A seat name that appears in two businesses counts if either
|
|
business puts it in a set.
|
|
|
|
- M7: the control board passes `isCreative = (root) => creativeSeats.has(root.seat)` to `createReader`.
|
|
It computes the sets on each `catalogue()` and `open()`, not once at start, because the business file
|
|
can change while the board runs. When the helper throws, `catalogue()` and `open()` refuse with
|
|
`conversation-business-unreadable`. They don't list everything, and they don't silently list nothing.
|
|
- M8: `discord check` and `discord run` build the sets from the config directory their `--config`
|
|
already names. A binding whose `seat` is in `librarySeats` refuses `discord-engine-not-allowed` unless
|
|
its engine is `mosaic-bus` with no `tools`. When the helper throws, both verbs refuse with
|
|
`discord-business-unreadable`. A pi binding whose seat is in no library business checks clean, which
|
|
is the brief's last test.
|
|
|
|
Your reading of the session paths is right. Creative thread sessions live at
|
|
`<dataRoot>/threads/<business>/<thread>/session`, outside every `.pi/state` root the board scans today, so
|
|
M7 is a second wall. Write that in the manifest notes. Add one test where a root spec points at a
|
|
creative seat's thread store and the root is hidden.
|
|
|
|
Scope amendment. The row adds these files: `packages/business/src/library-seats.mjs` (or the name you
|
|
pick), its export line in `packages/business/src/index.mjs`, a test in `packages/business/tests`,
|
|
`packages/control-board/src/serve.mjs` (the predicate wiring and `REFUSAL_STATUS` only), and a test in
|
|
`packages/control-board/tests`. `packages/discord/src/binding.mjs` stays untouched. The gate adds the
|
|
business and control-board suites, run whole, to the conversation and discord suites, plus every
|
|
`scripts/test-*.sh`.
|
|
|
|
## Question 2: the refusal code for a `mosaic-bus` binding with `tools`
|
|
|
|
Reuse `discord-engine-not-allowed`, with a message that names the `tools` key. Approved as proposed.
|
|
|
|
## Question 3: `reconcile` on `conversation-not-listed`
|
|
|
|
`false`: confirmed. `reconcile: true` on `unknown-conversation` and `incomplete-header` means a fresh read
|
|
may answer differently. A hidden root gives the same answer on every read until a human edits the
|
|
business file.
|
|
|
|
## Question 4: HTTP status for `conversation-not-listed`
|
|
|
|
404, next to `unknown-conversation`: approved. `conversation-business-unreadable` gets 503. Both entries
|
|
go in `REFUSAL_STATUS`, which ruling 1 put in scope.
|
|
|
|
## Other points
|
|
|
|
- The control-board serve test: keep it in phase B, run whole.
|
|
- New codes approved here: `conversation-business-unreadable` and `discord-business-unreadable`. Nothing
|
|
else is new.
|
|
- The phase A commit 3fa833715 stays on `researcher/row220`. The next round goes on a new branch name,
|
|
`researcher/row220-r2`. Phase B runs only on my "slot clear".
|