gosalyn: Case 2 v1 briefs round d2 for Webby d1: C2-15 keeps both exclude forms with a rule to tell them apart and one excluded helper that C2-05a's index, R6 and the guard call; pin and exclude refused under a source playground; R8 counts a same-type same-name hybrid version, not a link; C2-09b groups only consecutive seqs and refuses addressee-ambiguous; C2-10c maps all four room codes to 409 (webby-0088 B1 N1-N5, mos-1233)

This commit is contained in:
jason.woltje committed 2026-10-21 17:33:18 -05:00
1 parent 72ceda9356
commit 99b1e08591
1 file changed
+46 -20
+46 -20
View File
@@ -1178,8 +1178,8 @@ Base: `refactor` at cca206cdc. Each row builds on its parents' candidates and re
landings (Q177). C2-09a is row 219, C2-05a row 216, C2-05b row 224, C2-06 row 217 and C2-10b row 225,
all briefed and not landed. Line cites are at cca206cdc. A builder re-reads them on its base.
New refusal codes below go to Sage with these briefs, as the rule at the top asks: `room-not-open`
(C2-09b) and the problem code R8 (C2-15).
New refusal codes below go to Sage with these briefs, as the rule at the top asks: `room-not-open` and
`addressee-ambiguous` (C2-09b), and the problem code R8 (C2-15).
---
@@ -1217,7 +1217,9 @@ Design 6, C2-09b. A room is a project. Its members are the project's thread inst
`{project, threads: [{id, instance, name}]}`, sorted by instance key.
- `room.send {project, body}`. Human only. `parseAddress(body)` in `address.mjs` reads leading
`@name` tokens, each ended by whitespace, up to the first token that doesn't start with `@`. Names
resolve through `#named` (`broker.mjs:1480-1488`) or as instance keys, and must be members. With no
resolve through `#named` (`broker.mjs:1480-1488`) or as instance keys, and must be members. A tag
that is one member's current name and another member's instance key refuses `addressee-ambiguous`
and inserts nothing. A tag that names the same member both ways resolves to it. With no
tag every member gets the message. Repeated tags count once. An unknown tag refuses
`unknown-addressee` and inserts nothing. A body of tags only refuses `invalid-request`. A room
whose member threads aren't all there refuses `room-not-open`. The body is stored as written, tags
@@ -1226,8 +1228,10 @@ Design 6, C2-09b. A room is a project. Its members are the project's thread inst
run inside the dispatch transaction, so one refused target leaves nothing inserted. Returns
`{ids: [...], to: [...]}`.
- `room.read {project, after?, limit?}`. Human only. The caller's member threads' messages, oldest
first by `messages.seq`, each with its `thread` and `instance`. The human's messages from one
`room.send` share `at`, body and sender, and come back as one entry with `to` listing the instances.
first by `messages.seq`, each with its `thread` and `instance`. One `room.send` inserts its rows in one
transaction, so their seqs are consecutive. `room.read` groups only a run of consecutive seqs from the
human with the same `at` and body into one entry, with `to` listing the instances once each. Two sends
of the same body in one millisecond, such as a double click, stay two entries.
Paging follows `thread.messages` (`THREAD_PAGE`, `:260`): `after` is a message id in one of the
room's threads, else `message-not-found`.
- The tester gets none of the three verbs, so they aren't added to `TESTER_VERBS` (`:117-119`). A tester
@@ -1238,7 +1242,8 @@ Tests, from design 6: `@continuity x` makes exactly one message, in continuity's
one per member. `@nobody x` refuses with the `messages` count unchanged. A tag after the first word is
plain text. `room.open` against a same-name thread on another instance refuses and creates nothing, and a
second `room.open` reuses the threads. Also: a `#named` display name resolves, repeated tags send once, a
tags-only body refuses, a member whose messaging list refuses `human` makes the whole send insert
tag that is one member's name and another's key refuses `addressee-ambiguous` with the `messages` count
unchanged, two identical sends at one `at` read back as two entries, a tags-only body refuses, a member whose messaging list refuses `human` makes the whole send insert
nothing, `room.read` merges one send into one entry, pages with `after`, and refuses an `after` from
another project. `parseAddress` has a table test, which C2-10c's parity test reuses.
@@ -1292,8 +1297,9 @@ and `packages/plugins/tests`.
- The composer completes `@` against the room's members. The broker stays the authority: the browser
sends the body as typed through `room.send`, and shows the broker's `unknown-addressee` refusal as a
fixed line.
- `chat-http.mjs` maps `unknown-addressee` and `room-thread-name-taken` to 409 and `room-not-open` to 409,
with fixed text for each, as it does for the thread codes (`:15-23`).
- `chat-http.mjs` maps all four room codes, `unknown-addressee`, `addressee-ambiguous`,
`room-thread-name-taken` and `room-not-open`, to 409, with fixed text for each, as it does for the
thread codes (`:15-23`).
Tests: webui tests for the merged timeline, one entry per room send, the reply renderer in the room,
completion against members, and each refusal's fixed line. A parity test runs C2-09b's `parseAddress`
@@ -1329,7 +1335,8 @@ validator reads a hybrid entry (`packages/library/src/mapping.mjs:69-120`), and
link into a source (`reader.mjs:206-209`). C2-05a's queries and C2-06's guard take hybrids as a shape. M9
in C2-04 keeps agents off the mapping, so only her hand edit makes a hybrid. Three things are missing:
an `exclude` isn't checked against the sources, a link into an excluded path isn't reported, and nothing
checks a `pin` (design 1.4). Design 8 lists C2-15 with no model.
checks a `pin` (design 1.4). C2-05a's index drops "exclusions" (its brief above, What ships) without a
rule for reading them. Design 8 lists C2-15 with no model.
### Owner and reviewer
@@ -1341,26 +1348,41 @@ checks a `pin` (design 1.4). Design 8 lists C2-15 with no model.
### Files owned
`packages/library/src/mapping.mjs` (the `exclude` check), `packages/library/src/problems.mjs` (R8),
`packages/library/src/mapping.mjs` (the `exclude` and `pin` checks), `packages/library/src/scope.mjs`
(an `excluded` helper), the exclude read in C2-05a's `packages/library/src/index-build.mjs` or
`query.mjs`, `packages/library/src/problems.mjs` (R8),
`packages/library/src/reader.mjs` (the excluded-link R6 and the pin check), the guard file from C2-06
(`packages/library/src/guard.mjs`, the excluded-path cite only), `docs/case2/SETUP-SHEET.md` from C2-12 (one
hybrid section), and tests in `packages/library/tests` and `packages/bus/tests`.
### What ships
- The validator refuses `library-mapping-invalid` for an `exclude` that doesn't lie inside a book folder
of one of the hybrid's sources, and for a `pin.entity` outside them. Its existence at a commit is the
reader's to check.
- `exclude` keeps both forms of design 1.4. It is the type form when its first segment is one of the
hybrid's sources and the rest is exactly one key of `types`, as `second/Characters` is in
`mapping.test.mjs:57`. That form excludes the type folder in each of that source's books. Otherwise it
is the path form, and it must lie inside, or equal, a book folder of one of the sources. If both
readings fit, the validator refuses `library-mapping-invalid`. A path-form `exclude` or a `pin.entity`
that lies inside no source book folder, or under one of a source's playground prefixes, refuses
`library-mapping-invalid`, since the hybrid never reads it (REQ-LIB-3). Whether the path exists at a
commit is the reader's to check.
- One helper decides exclusion: `excluded(mapping, hybrid, path)` in `scope.mjs`, true when either form
covers the path. C2-05a's index, the reader's R6 and the guard all call it. If C2-05a has landed with
its own reading of `exclude`, this row replaces that reading with the helper, and C2-05a's query
tests stay green.
- The reader adds R6 for a link from a hybrid file into an excluded path. The index already drops it
from the hybrid's results (C2-05a).
- R8, "pinned value changed", reported only, like R5 and R6. At each commit the reader reports R8 on the
pinned entity when the file is missing, or when its front matter `field` doesn't equal `value`. It also
reports R8 on a hybrid file, with the line, when its front matter sets `field` to another value and a
front matter wikilink in that file resolves to the entity. Values compare as the reader's parsed text
against `String(value)`. That second case is my reading of design 1.4's "when a hybrid file changes
it". The reviewer checks it against the design.
- The guard refuses `citation-cross-universe` for an item that cites a hybrid file together with an
excluded source path. An item that cites only one source universe is that universe's item, as now.
reports R8, with the line, on a hybrid file that is a version of the entity and sets `field` to another
value in its front matter. A hybrid file is a version of the entity when `classify` gives it the
entity's type and its front matter `name` equals the entity's. When either file has no `name`, the
file names compare instead. A link alone doesn't count: a hybrid character that links `[[Mara]]` in
`relationships` and has its own `age` is someone else, and so is a place file with `age: 300`. Values
compare as the reader's parsed text against `String(value)`. A hybrid can't edit a source's file, so
this is how this brief reads design 1.4's "when a hybrid file changes it" (Webby d1 N1 agreed with the
reading and asked for the narrowing).
- The guard refuses `citation-cross-universe` for an item that cites a hybrid file together with a
source path that `excluded` covers, in either form. An item that cites only one source universe is that universe's item, as now.
- The setup sheet gets one section on making a hybrid by hand: the entry's keys, `exclude`, `pin` and
`notes`, and that the continuity role cites the notes.
@@ -1370,7 +1392,11 @@ each with its source universe, and never a source's playground or an excluded pa
`links_out` into sources and drops excluded targets. A source universe's lookups never return hybrid
files. A cite to source canon in a hybrid item passes the guard, and one beside an excluded path
refuses with the `messages` count unchanged. `library_universe` returns sources, constraints and notes.
One case per validator refusal, per R8 case and for the excluded-link R6, and an agent's organization
Each `exclude` form gets its own case on the lookups, R6 and the guard, and `second/Characters` from
`mapping.test.mjs:57` still checks. One case per validator refusal, including both readings fitting and a
pin or exclude under a source playground. For R8: a missing entity, a changed entity, a same-name hybrid
version with no link, and as non-R8 a place file with `age: 300` that links the entity and a different
character that links it. Also the excluded-link R6, and an agent's organization
write that adds a hybrid refuses `organization-projects-protected` (M9).
### Out of scope