gosalyn: Case 2 design ruling after r11, reply limits are settings: every M5 limit is a replyLimits key in the business file with Q219's numbers as defaults, changed only by the human's library.limits.set and fenced in organizationWriter.write as M11, read at check time, with a missing or invalid value using its default. C2-06 carries it and waits on Q29 (Q222, mos-1302)

This commit is contained in:
jason.woltje committed 2026-10-22 11:11:29 -05:00
1 parent 2852348d2e
commit df29550c9a
1 file changed
+64 -6
+64 -6
View File
@@ -123,6 +123,60 @@ correction edit distance. The residual in 5.1 is now 360 characters of free sugg
and 10 step 8 is closed by Jason. C2-06 hasn't landed, so it builds with the new numbers, and its tests
refuse one past each new boundary and accept at it.
## Ruling after r11: reply limits are settings (Q222)
Jason ruled Q222 (DECISIONS Q222, mos-1302): "Things should NOT be hardcoded." Q219 stands for now, and its
numbers become the defaults. Every M5 limit is a setting in a new top-level `replyLimits` object of the
business file:
| Key | Default | Valid value |
|---|---|---|
| `itemsPerReply` | 10 | an integer from 1 to 1,000 |
| `itemTextPerReply` | 1,200 | an integer from 1 to 100,000 |
| `ideasPerReply` | 3 | an integer from 0 to 1,000 |
| `ideaLength` | 120 | an integer from 1 to 100,000 |
| `itemLength` | 300, for `fact`, `finding`, `correction` and `question` | an integer from 1 to 100,000 |
| `correctionMinDistance` | 12 | an integer from 0 to 100,000 |
| `correctionPercent` | 25 | an integer from 0 to 100 |
A `correction`'s `to` is within edit distance max(`correctionMinDistance`, `correctionPercent`% of
`from`) of its `from`.
Where. The object sits beside `projects`, not inside the library entry, because M9 keeps `projects` to
hand edits and Mos asked for a CLI verb. `TOP_KEYS` (`business.mjs:21`) gains `replyLimits`, and
`validateBusinessDocument` accepts any JSON value under it. A bad hand edit then never stops her business
from loading.
Who changes them (M11). Only the business file's `human`, through a new human verb `library.limits.set`
with `revision` and `replyLimits`. She runs it as `mosaic limits replies --file <json>`, a new verb in
`packages/cli/src/limits.mjs` beside `mosaic limits set`, and later from the Console. The verb replaces the whole object, and a key left out means its default. It sits outside
`ORGANIZATION_KEYS` and `TESTER_ORGANIZATION_VERBS`, as `instances.set` does (`broker.mjs:103`, `:145`,
sage-0918). An agent session gets `human-required` and the tester gets `tester-refused`. A value outside the
table refuses `invalid-request`, and nothing is written. The fence that covers every other path sits in
`organizationWriter.write` (`organization.mjs:177`), next to M9: a write whose `replyLimits` differs from
the file's, compared in canonical JSON, refuses `organization-reply-limits-human-only` unless `actor` equals
the file's `human`. That stops the chief of staff's `organization.set`, any actor's `limits.set`,
`instances.set`, `routes.set` and `plugins.set`, the tester, and a direct call to `write`. Every setter
builds its document from the file, so none of them changes `replyLimits` today.
Read at check time. The guard reads `replyLimits` from the business file on each check, through the
organization reader, not from the broker's copy loaded at start (`broker.mjs:276-278`). A change applies to
the next reply with no restart.
Fallback. Each key resolves on its own. A key that is missing, `null`, a string, a fraction, negative,
below its minimum or above its ceiling resolves to its default, and so does every key when `replyLimits`
is missing or isn't an object. Unknown keys are ignored. The guard has no path without a number, so no
value reads as "no limit". `library.status` shows the limits in force and names each key that fell back.
The ceilings are validity bounds, not policy: they keep a value from reading as unlimited. 100,000
characters is far past any reply, and Mos can change them.
What agents see. The contracts don't state numbers. A refusal reason names the key and its value in
force, so the runner's retry (C2-07) tells the agent the limit it broke.
Hold. Q29 asks Jason whether "Agents NEVER write prose for the user" means story text for her books, with
long conversational answers allowed. A yes reworks M5's reply format, and that waits for his answer. The
settings carry over either way. C2-06 isn't built until Mos relays Q29.
## Changes from r10
Webby returned r10 with B1-B3 and N1-N5 (webby-0036, `agents/webby/work/case2-r10/REVIEW.md` at
@@ -417,7 +471,7 @@ the line says so.
refuses to open one by id (5.1 M7, C2-10a in the slice).
- B5. At most 10 items and 1,200 characters of item text per reply. An `idea` item is at most 200
characters, at most 5 per reply. Other kinds are at most 300 (5.1). Q219 later set ideas to at most 3
of 120 characters (ruling after r11).
of 120 characters (ruling after r11), and Q222 made every limit a setting with those defaults.
- B6. `correction.from` must equal one cite's `quote`, inside that cite's lines (5.2).
- B7. The broker keeps fetched page text per session, bounded and in memory, not in a table. The bundle
refuses the `harness-builtin` web source for creative definitions (5.2, 5.3).
@@ -898,15 +952,18 @@ New constants in `vocabulary.mjs`, each changed only by a reviewed commit:
| M2 tool gate (`gate.mjs:137-138`, unchanged) | `Write`, `Edit` or `Bash` from a creative session | C2-08: `decide()` with each creative policy refuses all three, on pi and claude-code |
| M3 typed tool set (`tools.mjs`) | The tool list is exact: lookups and `send_message`. No write tool, no `raise_decision` | C2-05b: a snapshot of tool names per creative role |
| M4 library verbs are human only | `library.file`, `library.sync` and `library.status` from an agent session, with `human-required` as at `broker.mjs:1434-1435`. Later write verbs join the list | C2-04 and C2-03a: each verb from an agent session refuses |
| M5 cited-reply guard (5.2) | A reply that isn't a list of short cited items. At most 10 items and 1,200 characters of item text per reply. An `idea` item is at most 120 characters, at most 3 per reply (Q219). A `fact`, `finding`, `correction` or `question` item is at most 300. A `correction`'s `to` is within edit distance max(12, 25% of `from`) of its `from` | C2-06: each limit refused one past the boundary and accepted at it |
| M5 cited-reply guard (5.2) | A reply that isn't a list of short cited items, within the `replyLimits` settings read at check time (Q222). The defaults: at most 10 items and 1,200 characters of item text per reply, an `idea` item at most 120 characters and at most 3 per reply (Q219), a `fact`, `finding`, `correction` or `question` item at most 300, and a `correction`'s `to` within edit distance max(12, 25% of `from`) of its `from`. A missing or invalid setting uses its default | C2-06: each default limit refused one past the boundary and accepted at it. A changed value takes effect on the next reply. Each invalid value in the fallback list uses the default |
| M6 no free-text decision channel | `decision.raise` from a creative definition refuses `decision-not-allowed` in the broker. The typed tool is also absent (M3) | C2-08: a creative session's raise refuses and no decision row appears. An assistant's raise still works |
| M7 no raw session view | On a business with a library project, the conversation reader leaves creative sessions out of `catalogue()`, and `open()` refuses them with `conversation-not-listed` | C2-10a: a creative session file under the root isn't listed and won't open by id. An assistant session still lists |
| M8 no raw Discord engine | On a business with a library project, `discord check` and `discord run` refuse a binding whose engine isn't `mosaic-bus` (`discord-engine-not-allowed`), and a `mosaic-bus` binding with `tools` | C2-10a: both refuse on a library business. A pi binding on a business without a library still checks clean |
| M9 no agent write to the mapping | An organization write whose `projects` differ from the file's refuses `organization-projects-protected`, in `organizationWriter.write` | C2-04: `write` called directly with a changed playground prefix, and again with a changed hybrid entry, refuses, and the file and `changes.jsonl` are unchanged |
| M10 personas and creative instances are the human's | Any write by an actor other than the business file's `human` that touches a creative instance: the chief of staff's `assistants`, `instances` and `models` keys through `organization.set`, and the tester's `instances.set` and `routes.set`. A persona file that fails its digest, mode or fence check. A harness argv that holds the prompt text | C2-08: each path refuses with its code (4) and the file is unchanged. The human's `instances.set` and `routes.set` on `bean` work, and so does her `limits.set` change to Bean's `launch.models` key. Tester cases through a bound tester session. A mutant without the `models` check fails. No `instances` read, `changes.jsonl` line or adapter argv holds a marker string from a fixture persona |
| M11 reply limits are the human's | `library.limits.set` from an agent session (`human-required`) or the tester (`tester-refused`). Any organization write whose `replyLimits` differs from the file's, unless the actor is the file's `human`, refuses `organization-reply-limits-human-only` in `organizationWriter.write` | C2-06: the human's `library.limits.set` works. The chief of staff's `organization.set`, the tester's `instances.set` and a direct `write` with a changed `replyLimits` refuse, and the file and `changes.jsonl` are unchanged. A mutant without the writer check fails |
Residual, in numbers. One reply can carry up to 3 `idea` items of 120 characters, which is 360
characters of free suggestion text inside a total of 1,200 characters of item text. Each item must quote
Residual, in numbers. At the defaults one reply can carry up to 3 `idea` items of 120 characters, which
is 360 characters of free suggestion text inside a total of 1,200 characters of item text. The numbers
are settings now (Q222), so the residual is `ideasPerReply` times `ideaLength`, within
`itemTextPerReply`, and only the human sets them (M11). Each item must quote
a real line of her repo, but the quote can be any line. Over a chat that adds up. Code can't tell a
sentence of story from a sentence of advice, and this design doesn't propose a classifier. Jason wrote
"NEVER", and Q176 says the system enforces it. He chose these caps over 5 ideas of 200 characters
@@ -1139,7 +1196,7 @@ Chains follow Q177 merge-chaining. S marks a slice row.
| C2-03a | S | Fetch-only sync (7.2): `syncCommand`, askpass helper, served ref, `library-diverged`, `--accept`, sync state file, timer, `library.sync` | C2-04 | Opus | the C2-03a tests in 7.2 |
| C2-05a | S | Lookup index and queries: find, get, search, manuscript with scope, universe, isolation | C2-01 | Haiku high | isolation, scope labels on every tool, unmapped files never returned, paging under `REPLY_CAP`, rebuild time on a 3,000-file fixture |
| C2-05b | S | Broker lookup verbs and typed tools. Sync before a read older than 60 s | C2-05a, C2-03a | Sol (Rocko) | broker verb tests. Typed tool name snapshot per creative role (M3) |
| C2-06 | S | Cited-reply guard (5.2) in `packages/library`, the broker's pre-check on agent-to-human `message.send`, the page store, the three notices | C2-05a | Opus | one pass and one refusal per check in M5 and 5.2, through the RESULT path and `send_message`, and with a non-creative sender. `messages` count unchanged on each refusal |
| C2-06 | S | Cited-reply guard (5.2) in `packages/library`, the broker's pre-check on agent-to-human `message.send`, the page store, the three notices, the `replyLimits` settings and M11 (Q222) | C2-05a, C2-04 | Opus | one pass and one refusal per check in M5, M11 and 5.2, through the RESULT path and `send_message`, and with a non-creative sender. `messages` count unchanged on each refusal |
| C2-07 | S | Runner retry on a guard refusal (at most 2), then `no-cited-answer`. `no-answer` and `turn-failed` in a library business | C2-06 | Haiku high | fake harness: free text then a valid reply stores one message. An empty and a failed turn store the fixed notices |
| C2-08 | S | Six role files and contracts, the new constants, M1, M6, the `webPlan` refusal, gate tests (M2), messaging test. `persona.name` and `persona.sha256` in `REGISTRY`, the persona file read and checks, and the fenced persona block between the contract and "## This session" in `prompt()`. M10: the `organization.set` refusals, the `models` check, the `routes.set` and `instances.set` creative branches (4). Both adapters pass the prompt as a file, not its text (4) | none | Opus | role tests, gate tests, a creative raise refused, `harness-builtin` refused for `research` and `companion`, `scripts/test-config.sh`. Persona vars refused at other layers, malformed and on a non-creative definition. The persona file refused when missing, a symlink, another uid's, group or other readable, over 8,000 characters, off its digest, holding `</persona-file>` or a `# `/`## ` line. A prompt snapshot pinning contract, persona, session. The M10 tests, tester cases through a bound tester session, the `models` mutant. Adapter tests: argv holds the prompt path and no marker from the prompt, and pi's adapter refuses a missing and an unreadable prompt file. M10's actor case: the human's `limits.set` change to Bean's `launch.models` key works next to the chief's refusal. Business file, `.history` and `changes.jsonl` modes after a write |
| C2-09a | S | `#threaded` and launcher read `THREAD_DEFINITIONS`. `thread.create` takes `instance`. The Console new-thread control picks it | C2-08 | Sol (Rocko) | the C2-09a tests in section 6 |
@@ -1416,7 +1473,8 @@ What it adds is one bot account with read access to that one repo.
start with, and when. A specialist she wants later is added by the human through `instances.set`
(4), with no hand edit and no new row.
8. The residual in 5.1. Closed: Jason ordered the lower caps, 3 idea items of 120 characters per reply,
360 characters of free text within 1,200 total (Q219, mos-1299).
360 characters of free text within 1,200 total (Q219, mos-1299). These are the defaults of settings
only the human changes (Q222, M11).
9. The instance start. Stopping Hermes comes later, and only when she chooses it.
- The bus and the Console are already running from step 2c, and both come back after a boot on
their own (Q189, which reverses mos-1097 item 1). Nobody starts the Console by hand here. With a