morgana: row 205 r2 (sage-2430): put() events carry their decision so the decision trail reaches them; pin checks the thread claim via threadsState, owner read is a follow-up row

This commit is contained in:
jason.woltje committed 2026-10-21 23:49:34 -05:00
1 parent 59f708ca21
commit ce6a96ad61
2 files changed
+28 -5

No files matched your search

+20 -1
View File
@@ -55,9 +55,28 @@ The kit is /mnt/storage/scratch/morgana-r205 (`slot205.sh`, `mutate.py`, `busy.s
Then it runs the bus suite, then M1 and M11-M14, each on its named test and restored after each. It uses umask
022 and TMPDIR from `mktemp -d /mnt/storage/scratch/tmp/g205XXXX`. It refuses unless HEAD is the candidate
and the tree is clean, and it stops after two unexpected failures (mos-0823).
and the tree is clean, and it stops at the first unexpected failure (sage-2371).
Sage's gate adds every `scripts/test-*.sh`. The webui tests that read trail or replies run there.
Static check: `node --check` on broker.mjs and the new test, run from `/`. Both parse. All five mutant anchors
match exactly once.
## r2 (after the 742cba805 slot, failure 1 of the window, sage-2428)
slot205 on 742cba805 stopped red in message-filter, 4 pass and 2 fail. Both fixes are in the test only.
broker.mjs is unchanged (sage-2430).
- "trail on a decision ..." (:124, its event shows). `#trail` reaches events on a decision trail by
`subject` = the decision or `body.$.decision` = the decision. `put()` wrote every event body as `{}`, so the
shown message's event was out of reach. That also made the "no hidden event" check (:127) vacuous: the hidden
event was never collected, so dropping it proved nothing. Fix: `put()` writes `{decision}` as the event body
when the message has a decision. Both events are now collected; the filter keeps one and drops the other.
- Governance pin, claims item (:175). A thread run's `role.claim` writes `thread_claims` only and returns
(broker.mjs:2682). `agents` (:3309) reads `role_claims` only. No owner verb shows a thread claim. The gap
predates 205. Ruling sage-2430, option (a): the pin checks through `threadsState` (the launcher view) that
the claim is recorded with holder `assistant-run` and isn't filtered. `agents` is unchanged in 205.
Follow-up: Sage adds a row for an owner-visible thread-claim read. Like the filter, it lands before any
user thread goes live, since PRD :1084 and the FED R6 sentence promise that the owner sees claims.
M1 still fails the pin: with `#readable` true, `trail` on the user message returns rows instead of `[]`.
+8 -4
View File
@@ -25,7 +25,8 @@ function threadOf(store, project, owner, name) {
);
return id;
}
// A message with its thread row, its routed delivery and one event about it.
// A message with its thread row, its routed delivery and one event about it. The event names the
// message's decision in its body, which is how #trail reaches it from the decision.
function put(store, { thread = null, from_role, from_run, to_role, in_reply_to = null, decision = null, body }) {
const id = randomUUID();
store.run(
@@ -36,7 +37,7 @@ function put(store, { thread = null, from_role, from_run, to_role, in_reply_to =
store.run('INSERT INTO deliveries(message,at,op,holder_run,transport,address) VALUES(?,?,?,?,?,?)', id, AT, 'routed', null, null, null);
store.run(
'INSERT INTO events(id,at,business,kind,actor_role,actor_run,subject,body) VALUES(?,?,?,?,?,?,?,?)',
randomUUID(), AT, 'demo', 'action.allowed', from_role, from_run, id, '{}',
randomUUID(), AT, 'demo', 'action.allowed', from_role, from_run, id, JSON.stringify(decision ? { decision } : {}),
);
return id;
}
@@ -171,8 +172,11 @@ test('governance pin: the owner sees decisions, inbox entries, claims, tasks and
const inbox = await f.call(f.human, 'inbox');
assert.ok(inbox.some((x) => x.id === d), 'inbox entries: the run raise is in the owner inbox');
const agents = await f.call(f.human, 'agents');
assert.ok(agents.some((c) => c.role === 'assistant' && c.holder_run === 'assistant-run'), 'claims: the run claim shows');
// A thread run's claim is in thread_claims, and `agents` reads role_claims only, so no owner verb
// shows it yet (sage-2430). This is the launcher view: the claim is recorded and the filter leaves it
// alone. The follow-up row Sage adds for an owner-visible thread-claim read moves this to an owner verb.
const claim = f.broker.threadsState('demo').find((x) => x.thread === U);
assert.equal(claim?.holder, 'assistant-run', 'claims: the run claim is recorded');
const tasks = await f.call(f.human, 'tasks');
assert.ok(tasks.some((x) => x.task_ref === 'vikunja:3/47' && x.run === 'assistant-run'), 'tasks: the run task write shows');
const file = await f.call(f.human, 'project.file', { project: 'stack', path: 'docs/alice-plan.md' });