bus: a closed decision sends the raising role a message (#1609)
Row 115. When a decision closes by resolve or withdraw, and the closer is a human or another run, the broker routes a DECISION message to the raising role's holder: "Decision <id> on <action> was resolved by <by>: <choice> (<text>)" or "withdrawn by <by>", plus the note if there is one. A seen op sends nothing. #request falls back to the human.input that closed the decision, filtered to resolved and withdrawn. Candidate manifest 1910eb5a672e670cb7c8a197ee3fdd888412dd803fe52a0a15680103c26c078c from Darkwing's round 2 packet (40e5917f9), on BASE94ece491f. Filbert approved in 28428 and Sage in 28427. Sage reran the full gate at the landing target with the candidate applied. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -889,6 +889,30 @@ export class Broker {
|
||||
{ kind: 'answer', decision: d.id, op, choice: a.choice ?? null },
|
||||
d.id,
|
||||
);
|
||||
// Row 115 (#1609): a close by anyone but the raising run tells the raising role, routed like message.send's.
|
||||
if (op !== 'seen' && (s.human || s.run !== d.raised_by_run)) {
|
||||
const outcome =
|
||||
op === 'resolved'
|
||||
? `resolved by ${by}: ${a.choice} (${JSON.parse(d.options).find((o) => o.key === a.choice).text})`
|
||||
: `withdrawn by ${by}`;
|
||||
const mid = randomUUID();
|
||||
this.#store.run(
|
||||
'INSERT INTO messages(id,at,business,from_role,from_run,to_role,class,in_reply_to,decision,corrects,body) VALUES(?,?,?,?,?,?,?,?,?,?,?)',
|
||||
mid,
|
||||
this.#now(),
|
||||
s.business,
|
||||
s.human ? 'human' : s.role,
|
||||
s.human ? 'human-cli' : s.run,
|
||||
d.raised_by_role,
|
||||
'DECISION',
|
||||
null,
|
||||
d.id,
|
||||
null,
|
||||
`Decision ${d.id} on ${d.action} was ${outcome}.${a.note === undefined ? '' : `\nNote: ${a.note}`}`,
|
||||
);
|
||||
const h = this.#holder(s.business, d.raised_by_role);
|
||||
this.#delivery(mid, 'routed', h?.op === 'claim' ? h : null);
|
||||
}
|
||||
return { id: d.id, op };
|
||||
}
|
||||
case 'message.send': {
|
||||
@@ -978,15 +1002,7 @@ export class Broker {
|
||||
s.human || s.tester ? null : this.#holder(s.business, s.role),
|
||||
s.human || s.tester ? 'cli' : 'broker',
|
||||
);
|
||||
return rows.map((m) => ({
|
||||
...m,
|
||||
request:
|
||||
this.#store.get(
|
||||
"SELECT id FROM events WHERE business=? AND kind='human.input' AND subject=? ORDER BY seq LIMIT 1",
|
||||
s.business,
|
||||
m.id,
|
||||
)?.id ?? null,
|
||||
}));
|
||||
return rows.map((m) => ({ ...m, request: this.#request(s.business, m) }));
|
||||
}
|
||||
case 'message.read': {
|
||||
keys(a, ['id'], ['id']);
|
||||
@@ -1292,6 +1308,24 @@ export class Broker {
|
||||
decision,
|
||||
);
|
||||
}
|
||||
// A message's originating human.input: a human instruction's own, or for the
|
||||
// message a human decision close sends (row 115, #1609), the close's answer.
|
||||
// A decision has one close, so the answer is unique.
|
||||
#request(business, m) {
|
||||
const own = this.#store.get(
|
||||
"SELECT id FROM events WHERE business=? AND kind='human.input' AND subject=? ORDER BY seq LIMIT 1",
|
||||
business,
|
||||
m.id,
|
||||
);
|
||||
if (own || m.class !== 'DECISION' || m.from_role !== 'human' || !m.decision) return own?.id ?? null;
|
||||
return (
|
||||
this.#store.get(
|
||||
"SELECT id FROM events WHERE business=? AND kind='human.input' AND subject=? AND json_extract(body,'$.kind')='answer' AND json_extract(body,'$.op') IN ('resolved','withdrawn') ORDER BY seq LIMIT 1",
|
||||
business,
|
||||
m.decision,
|
||||
)?.id ?? null
|
||||
);
|
||||
}
|
||||
// The message a review verb sends, routed like message.send's.
|
||||
#reviewMessage(s, mid, { to, cls, in_reply_to = null, decision = null, body }) {
|
||||
this.#store.run(
|
||||
|
||||
Reference in New Issue
Block a user