harness: a new PM run reads current launch state and open tasks before each turn, and old messages show their age, row 109 (#1603)
The broker gains a read-only state verb (launch state with the event that set it, open tasks capped). The runner fetches it before each turn and puts a "Current state" block at the top of the request; a failed read fails the turn. turnRequest() adds the message time. roles/pm.md gets one rule: the block is the truth for launch state and task status. Seat session, seat sandbox and cli launcher tests re-anchored to the new request shape. Built by Researcher, packet875c515e9. Filbert approved round 3, comment 28333, candidate manifest 0a62225e (11 paths). Sage gate on9774d70dbplus row109.patch: every package node suite and every scripts/test-*.sh green on the first run, skipped 0 in bus, harness and seat. Vikunja #259. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -62,7 +62,10 @@ export function founderCheck(policy, env) {
|
||||
|
||||
// The request text one message becomes.
|
||||
export function turnRequest(m) {
|
||||
const head = [`Message ${m.id} from ${m.from_role}`, `class ${m.class}`];
|
||||
const head = [`Message ${m.id} from ${m.from_role}`];
|
||||
// Row 109 (#1603): when the sender sent it, so a message that waited reads as old.
|
||||
if (m.at) head.push(`sent ${m.at}`);
|
||||
head.push(`class ${m.class}`);
|
||||
if (m.in_reply_to) head.push(`in reply to ${m.in_reply_to}`);
|
||||
if (m.decision) head.push(`citing decision ${m.decision}`);
|
||||
// Row 90 (#1584): the human input id task_create cites as its request.
|
||||
@@ -70,6 +73,22 @@ export function turnRequest(m) {
|
||||
return `${head.join(", ")}:\n\n${m.body}`;
|
||||
}
|
||||
|
||||
// Row 109 (#1603): the block at the top of a turn's request. It is what the broker said at `readAt`,
|
||||
// the truth for launch state and task status over any older message.
|
||||
export function stateBlock(state, readAt) {
|
||||
const { launch, tasks } = state;
|
||||
const since = launch.at ? ` since ${launch.at} (event ${launch.event})` : " (never switched)";
|
||||
const shown = tasks.open.length < tasks.total ? `, the first ${tasks.open.length} of cap ${tasks.cap}` : "";
|
||||
const open = tasks.open.map((t) => `- ${t.task_ref} ${t.title ?? ""}`.trimEnd());
|
||||
return [
|
||||
`Current state, read at ${readAt}:`,
|
||||
`Launches: ${launch.state}${since}.`,
|
||||
`Open tasks: ${tasks.total}${shown}.`,
|
||||
...open,
|
||||
"",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function clip(text) {
|
||||
return text.length > BODY_MAX ? text.slice(0, BODY_MAX) + "\n[answer cut at 32000 characters]" : text;
|
||||
}
|
||||
@@ -339,7 +358,23 @@ export async function main(runDir, { stdin = process.stdin, env = process.env, l
|
||||
}
|
||||
for (const m of messages) {
|
||||
if (stopping) break loop;
|
||||
const turn = await runTurn({ session, request: turnRequest(m), n: ++n, env: turnEnv, onChild: (c) => (child = c) });
|
||||
// Row 109 (#1603): read the state now, not when the message was sent. A failed read fails the turn.
|
||||
let block = null;
|
||||
let unread = null;
|
||||
try {
|
||||
block = stateBlock(await client.call("state"), new Date().toISOString());
|
||||
} catch (e) {
|
||||
if (SESSION_GONE.has(e.code)) {
|
||||
log(`runner: session stopped working: ${e.code}`);
|
||||
code = EXIT.session;
|
||||
break loop;
|
||||
}
|
||||
unread = `could not read the current state: ${e.code ?? e.message}`;
|
||||
}
|
||||
++n;
|
||||
const turn = unread
|
||||
? { ok: false, reason: unread }
|
||||
: await runTurn({ session, request: `${block}\n${turnRequest(m)}`, n, env: turnEnv, onChild: (c) => (child = c) });
|
||||
if (stopping) break loop;
|
||||
if (!turn.ok) log(`runner: turn ${n} for message ${m.id} failed: ${turn.reason}`);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user