diff --git a/docs/TEST_INTEGRITY.md b/docs/TEST_INTEGRITY.md index 6c8c9e6..3840716 100644 --- a/docs/TEST_INTEGRITY.md +++ b/docs/TEST_INTEGRITY.md @@ -18,10 +18,15 @@ test_integrity_note: given: An assigned task, an approved named-peer request, and a pending owner provider decision when: A public runtime start is attempted without an authorized executor then: The start is refused, no provider adapter runs, and the assigned task and owner wait remain unresolved + - id: SCN-routine-coordination-paused-schedule-dispatch + given: An assigned waiting task, a pending owner provider decision, and an installed manager check-in schedule with wake.enabled false + when: The public system scheduler dispatches the due owner-authorized coordination schedule + then: It queues a durable manager review without planning or invoking a provider, while the task and owner wait remain unresolved observable_outcomes: - Generated tracked instruction text for fresh-install and resume surfaces - Durable task state, approval state, named-approver identity, and authority errors - Public start refusal, zero adapter invocations, offline identity state, and retained owner wait + - Installed wake policy, public scheduled-dispatch receipt, zero provider or command invocations, retained assignment and owner wait determinism_controls: - Disposable local Git repository, SQLite state, and fixed clock - No provider calls, network, retries, sleeps, or host timers @@ -31,6 +36,7 @@ test_integrity_note: - Letting a manager decide an owner or peer approval - Omitting policy boundaries from one generated startup surface - Re-enabling paused execution or granting starts, spending, publication, destructive recovery, or arbitrary dispatch + - Treating a queued manager check-in as authorization to start a provider or resolve the underlying wait relaxation: did_relax_any_assertion: false if_true_explain_spec_basis: "" diff --git a/test/fleet/routine-coordination.test.mjs b/test/fleet/routine-coordination.test.mjs index 4784ead..b5f2bfe 100644 --- a/test/fleet/routine-coordination.test.mjs +++ b/test/fleet/routine-coordination.test.mjs @@ -13,8 +13,9 @@ import { inspectRepository } from '../../src/kernel/git.mjs'; import { installProject } from '../../src/kernel/install.mjs'; import { createWorktrees } from '../../src/kernel/worktrees.mjs'; import { planAreaUp, startFleet } from '../../src/runtime/lifecycle.mjs'; +import { ScheduleService } from '../../src/schedules/service.mjs'; -function fixture() { +function fixture({ pausedManagerWake = false } = {}) { const root = mkdtempSync(join(tmpdir(), 'torch-routine-coordination-')); execFileSync('git', ['init', '-b', 'main', root]); execFileSync('git', ['-C', root, 'config', 'user.email', 'torch-test@example.invalid']); @@ -27,6 +28,11 @@ function fixture() { execFileSync('git', ['-C', root, 'commit', '-m', 'fixture']); const repository = inspectRepository(root); const proposal = proposeDomains({ repository, analysis: analyzeRepository(repository) }); + if (pausedManagerWake) { + const schedule = proposal.schedules.find((entry) => entry.action?.type === 'manager-check-in'); + assert.ok(schedule, 'fixture must configure the Session Manager check-in schedule'); + schedule.action = { ...schedule.action, wake: { enabled: false } }; + } proposal.review = { status: 'approved', reviewedAt: '2026-09-30T00:00:00Z', reviewedBy: 'fixture-owner', notes: [], }; @@ -149,3 +155,56 @@ test('SCN-routine-coordination-paused-dispatch: an unapproved public start leave control.close(); } }); + +test('SCN-routine-coordination-paused-schedule-dispatch: configured paused wakes queue attention without a provider invocation', () => { + const context = fixture({ pausedManagerWake: true }); + const at = new Date('2026-09-30T12:00:00Z'); + let providerWakeCalls = 0; + let commandCalls = 0; + const control = openControlPlane({ repositoryRoot: context.root, env: context.env, clock: () => at }); + try { + const backlog = createBacklogService({ repositoryRoot: context.root, controlPlane: control }); + const created = backlog.create({ actorId: 'session-manager', title: 'Paused scheduled dispatch', + description: 'Keep assigned work and the owner wait visible while wakes are paused.', + acceptanceCriteria: ['The paused schedule queues review without a provider invocation.'], + affectedDomains: [context.worker] }); + const ready = backlog.transition({ taskId: created.id, actorId: 'session-manager', to: 'ready', + expectedRevision: created.revision }); + const assigned = backlog.transition({ taskId: ready.id, actorId: 'session-manager', to: 'assigned', + owner: context.worker, expectedRevision: ready.revision }); + control.reportStatus({ areaId: context.worker, state: 'waiting', task: assigned.id, + summary: 'Waiting for the owner provider decision.' }); + const ownerWait = control.requestApproval({ requester: context.worker, approver: 'owner', task: assigned.id, + title: 'Owner provider decision', summary: 'Automatic provider execution is paused.' }); + const schedules = new ScheduleService({ + repositoryRoot: context.root, controlPlane: control, clock: () => at, + executor: () => { commandCalls++; throw new Error('Manager check-in must not invoke a shell command.'); }, + wakeManager: { + plan: () => { providerWakeCalls++; throw new Error('A paused schedule must not plan a provider wake.'); }, + invoke: () => { providerWakeCalls++; throw new Error('A paused schedule must not invoke a provider.'); }, + }, + }); + + const plan = schedules.plan({ scheduleId: 'session-manager-check-in', actorId: 'owner', at }); + assert.equal(plan.wakeEnabled, false, 'the installed schedule must expose an explicit paused wake policy'); + assert.deepEqual(plan.schedule.action.wake, { enabled: false }); + const dispatched = schedules.dispatchSystem({ actorId: 'owner', approved: true, at }); + assert.deepEqual(dispatched.due, ['session-manager-check-in']); + assert.equal(dispatched.runs[0].result, 'succeeded'); + const observed = JSON.parse(dispatched.runs[0].stdout); + assert.equal(observed.checkIn.queued, true); + assert.equal(observed.wake.attempted, false); + assert.equal(observed.wake.reason, 'wake-disabled'); + assert.equal(providerWakeCalls, 0, 'the public scheduled dispatch cannot plan or invoke a paused provider'); + assert.equal(commandCalls, 0, 'the typed check-in cannot fall through to a shell command'); + assert.equal(backlog.get(assigned.id).state, 'assigned', 'the scheduled review cannot complete assigned work'); + assert.equal(control.listApprovals({ actorId: context.worker, status: 'pending' }).some((item) => + item.id === ownerWait.id), true, 'the owner decision remains a durable wait'); + assert.equal(control.identity(context.worker).state, 'waiting', 'the worker cannot report false idle or success'); + assert.equal(control.identity('session-manager').state, 'offline', 'queued review cannot report a manager provider turn'); + assert.equal(control.readMessages({ recipient: 'session-manager', unacknowledgedOnly: true }) + .some((message) => message.kind === 'manager-check-in'), true, 'the manager receives durable attention instead of a wake'); + } finally { + control.close(); + } +});