mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
fix(startup): tell a seat that starts with held messages about them (#2437)
A seat that starts with held messages is now told about them once: it gets one notice saying how many were kept and not delivered, and to read them with `rig seat held-messages <seat>`. The same line appears in the launch's warnings. Nothing is replayed. Handover and runtime-switch successors don't reach this point yet, and a follow-up gives them the same notice. Reported in #2431 by @billbenetti.
This commit is contained in:
1 parent
c9e73d363c
commit
e3c461a7e3
2 files changed
+30
No files matched your search
@@ -31,6 +31,7 @@ import { join } from "node:path";
|
||||
import { guidanceFileIsLink, readFootprintAnswer, readGuidanceFile, routerLineSkipReason, type FootprintAnswer, type RouterLineWriteResult } from "./footprint-choice.js";
|
||||
import { SEAT_SKILLS_BOOTSTRAP_BLOCK_ID, describeSkillRouterFootprint, seatSkillsLineEdited, refusedSeatSkillsRootForNode, seatCatalogKeptNote, seatRouterKeptNote, seatSkillPathsForNode, seatSkillPluginDirForNode, writeSeatSkillCatalog, writeSeatSkillRouter } from "./seat-skill-storage.js";
|
||||
import { recordConversationPromptForNode, recordForkConversationPromptForNode, seatLaunchAdditionsForLaunch, systemPromptPendingNoteForNode } from "./seat-launch-additions.js";
|
||||
import { OutboxHandler } from "./outbox-handler.js";
|
||||
|
||||
// Why a startup prompt's submission could not be confirmed, as told to the operator when the
|
||||
// proof line is held back.
|
||||
@@ -1021,6 +1022,16 @@ export class StartupOrchestrator {
|
||||
: undefined;
|
||||
this.eventBus.emit({ type: "node.startup_ready", rigId: input.rigId, nodeId: input.nodeId, ...(submission ? { submission } : {}) });
|
||||
|
||||
// Held messages are never replayed, so a seat that starts with some is told once, as the launch is (#2431).
|
||||
let heldMessages = 0;
|
||||
try { heldMessages = new OutboxHandler(this.db).retentionUsage(input.nodeId).count; } catch { /* no held-message store: nothing to tell */ }
|
||||
if (heldMessages && input.binding.tmuxSession) {
|
||||
const seat = input.sessionName ?? input.binding.tmuxSession;
|
||||
const notice = `${heldMessages} held message${heldMessages === 1 ? " was" : "s were"} kept for this seat and not delivered: read ${heldMessages === 1 ? "it" : "them"} with rig seat held-messages ${seat}.`;
|
||||
warnings.push(notice);
|
||||
await this.sessionTransport.send(input.binding.tmuxSession, notice, { openrigNotice: true }).catch(() => undefined);
|
||||
}
|
||||
|
||||
return { ok: true, startupStatus: "ready", continuityOutcome, ...(submission ? { submission } : {}), ...(routerLine ? { routerLine } : {}), ...(resumedProcess ? { resumedProcess } : {}) };
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ import { COMMUNITY_HARNESS_RUNG_INVENTORY, COMMUNITY_RUNNER_RUNG_INVENTORY, runt
|
||||
import { EventBus } from "../src/domain/event-bus.js";
|
||||
import { RigRepository } from "../src/domain/rig-repository.js";
|
||||
import { StartupOrchestrator, isRecoverableStartupFailure, type StartupInput, type StartupResult } from "../src/domain/startup-orchestrator.js";
|
||||
import { OutboxHandler } from "../src/domain/outbox-handler.js";
|
||||
import { SessionTransport } from "../src/domain/session-transport.js";
|
||||
import { outboxEntriesSchema } from "../src/db/migrations/027_outbox_entries.js";
|
||||
import { seatDeliveryGuardSchema } from "../src/db/migrations/087_seat_delivery_guard.js";
|
||||
import type { RuntimeAdapter, NodeBinding, ResolvedStartupFile, ProjectionResult, StartupDeliveryResult, ReadinessResult } from "../src/domain/runtime-adapter.js";
|
||||
import { resolveConcreteHint } from "../src/domain/runtime-adapter.js";
|
||||
import type { ProjectionPlan } from "../src/domain/projection-planner.js";
|
||||
@@ -728,6 +732,21 @@ describe("StartupOrchestrator", () => {
|
||||
expect(result.warnings).toEqual(["AGENTS.md is untracked", "plugin exclude skipped"]);
|
||||
});
|
||||
|
||||
it("tells a seat that starts with held messages, once, and names it in the launch's warnings (#2431)", async () => {
|
||||
const seed = seedSession();
|
||||
db.exec(outboxEntriesSchema.sql); db.exec(seatDeliveryGuardSchema.sql); // as the guard suites add the held-message store
|
||||
new OutboxHandler(db).retain({ outboxId: "held-1", senderSession: "planner@test-rig", destinationSession: "r01-impl", body: "Real work for you" },
|
||||
{ nodeId: seed.nodeId, session: "r01-impl", occupant: null, pane: null });
|
||||
const send = vi.spyOn(SessionTransport.prototype, "send").mockResolvedValue({ ok: true, sent: true } as never);
|
||||
try {
|
||||
const result = await createOrchestrator().startNode(makeInput(seed));
|
||||
const notice = "1 held message was kept for this seat and not delivered: read it with rig seat held-messages r01-impl.";
|
||||
expect(result).toMatchObject({ ok: true, startupStatus: "ready" });
|
||||
expect(result.warnings).toContain(notice);
|
||||
expect(send.mock.calls.filter(([, text]) => text === notice)).toEqual([["r01-impl", notice, { openrigNotice: true }]]);
|
||||
} finally { send.mockRestore(); }
|
||||
});
|
||||
|
||||
it("keeps a challenge-only transport failure best-effort", async () => {
|
||||
const seed = seedSession();
|
||||
const tmux = mockTmux({ sendText: vi.fn(async () => ({ ok: false as const, message: "fixture transport failure" })) });
|
||||
|
||||
Reference in new issue
Block a user