console: make continuation budget stops and coordination holds explicit

This commit is contained in:
thePR0M3TH3AN committed 2026-10-03 00:47:42 -04:00
1 parent 981bc6933f
commit 6c35c961aa
10 files changed
+127 -1

No files matched your search

+28
View File
@@ -1,5 +1,33 @@
# Test Integrity Notes
## 2026-10-03 — Visible automatic-work stops
```yaml
test_integrity_note:
change_type: new_tests
scenarios:
- id: SCN-runtime-continuation-health
given: "An enabled pilot exhausts its daily attempt allowance"
when: "Observation reads the ledger, next UTC day, pause or malformed state"
then: "The exact stop and remaining allowance are visible without resetting or modifying state, and no timer/token qualification is fabricated"
- id: SCN-console-continuation-stop
given: "A daily cap stop and a specialist no-progress hold"
when: "Attention items are projected"
then: "The budget decision belongs to the owner, coordination belongs to the manager, and no fake approval action or automatic budget increase is offered"
observable_outcomes:
- "Live snapshot health, byte-identical ledger and owner/manager attention routing"
determinism_controls:
- "Hermetic repository, fixed UTC clock, injected executor and plain snapshot fixtures"
anti_cheat_rationale:
prevents:
- "Silent budget exhaustion disguised as healthy idle"
- "Resetting a ledger to make work appear available"
- "Treating declared policy as a verified running timer or measured usage"
relaxation:
did_relax_any_assertion: false
if_true_explain_spec_basis: ""
```
## 2026-10-03 — Rolling continuation and bounded no-progress escalation
```yaml
+11
View File
@@ -2,6 +2,17 @@
## Current owner priority: reliability first (2026-10-03 UTC)
End-to-end repair qualification is still open. Runtime reported exact converged
candidate `d2d9845` with four fresh native check PASS receipts; QA independent
acceptance is not yet recorded. Owner source fixes cover guarded presence,
rolling/fair dispatch, no-progress escalation and visible daily-cap stops. The
first automatic allowance of 12 turns was exhausted; any increase needs an
explicit owner choice, not a manual-start bypass. Next dispatch should prioritize
QA's exact verdict/support decisions, Release's declared-inventory consumer
decision, safe Kernel/Work composition and actual Console implementation.
Require evidence of specialist work, independent review/checks, serialized
integration and next-work continuation before calling the autonomous loop ready.
Live all-fleet resume exposed a new urgent proposal:
`TASK-d95e72d0-816d-4fc2-acaf-b73642fd4881` — refill continuation capacity
when individual turns exit instead of waiting for the longest batch member.
@@ -14,4 +14,10 @@ Status exposes occupied guard identities and no-progress holds. Installed releas
Eligible identities are ordered least-recently-dispatched within the current UTC day, with the Manager first only on ties. A repeatedly revised specialist task must not consume every freed slot while an eligible peer has never started.
## Visible operating stops
The read-only project observation includes the automatic-turn budget, remaining attempts, exact pause/cap stop, retained capacity guards and current no-progress holds. It never resets the ledger, changes authority or treats declared policy as verified timer installation. No-progress holds are displayed only while the same active task IDs/revisions remain; resolved or revised tasks do not retain stale alerts.
An exhausted automatic allowance appears as an owner policy decision in Overview and as an explicit Fleet status line. Manager-owned no-progress waits remain with the Manager, rather than being falsely routed to the owner. There is no fabricated approval button or silent allowance increase. Manual owner turns are not included in this ledger; token/cost governance remains unknown.
Deterministic acceptance scenarios and anti-cheat notes are in docs/TEST_INTEGRITY.md. Independent live qualification remains necessary; do not close broader event-driven or persistent-operations umbrellas based on this source fix alone.
+12
View File
@@ -16,6 +16,18 @@ function attentionGroups(snapshot) {
const agentName = (id) => agents.get(id)?.title ?? id ?? 'Owner not recorded';
const add = (group, item) => groups[group].push(item);
const approvals = (snapshot.approvalRequests?.items ?? []).filter((approval) => approval.status === 'pending');
const continuation = snapshot.continuation;
if (continuation?.available && continuation.stopReason === 'daily-turn-cap') add('owner', {
tone: 'decision', title: 'Automatic fleet work stopped at its daily limit', owner: 'Project owner',
detail: 'No additional automatic agent turns can start today unless you explicitly change the limit. Existing turns may finish; idle does not mean all work is done.',
evidence: `${continuation.attempts}/${continuation.maxTurnsPerDay} automatic attempts · UTC day ${continuation.day} · not measured token/cost usage`,
href: '#fleet', action: 'Review continuation policy', decisionApprovalId: null,
});
for (const hold of continuation?.held ?? []) add('fleet', {
tone: 'review', title: `${agentName(hold.areaId)} needs a concrete coordination decision`, owner: agentName('session-manager'),
detail: 'Two completed turns left the assigned task state unchanged. Unchanged-work retries are held; inspect evidence and resolve the actual blocker rather than restarting blindly.',
evidence: hold.areaId, href: '#communications', action: 'Open coordination messages', requestOwner: 'session-manager',
});
const advisory = [];
const recheckCodes = new Set(['BACKLOG_OBSERVED_COMMIT_STALE', 'BACKLOG_OBSERVED_COMMIT_MISSING']);
+1
View File
@@ -183,6 +183,7 @@
<section id="fleet" class="console-section fleet-section" data-console-view="fleet" aria-labelledby="fleet-title">
<div class="section-heading"><div><p class="section-kicker">Persistent identities</p><h2 id="fleet-title">Fleet and ownership</h2></div><span id="agent-health-note">Session state from latest observation</span></div>
<p id="continuation-status" class="surface-note" role="status">Reading automatic continuation policy…</p>
<div class="fleet-columns">
<article class="work-surface"><div class="panel-heading"><h3>Sessions</h3><span>One context per identity</span></div><div id="agent-list" class="agent-list"></div>
<details class="runtime-profile-tools">
+6
View File
@@ -1039,6 +1039,12 @@ function renderAttention(groups) {
}
function render(snapshot) {
const continuation = snapshot.continuation;
setText('#continuation-status', continuation?.available
? `Automatic turns: ${continuation.attempts}/${continuation.maxTurnsPerDay} used today (UTC); ${continuation.remainingTurns} left. ${continuation.stopReason === 'daily-turn-cap'
? 'No further automatic starts: the owner-controlled daily cap is reached. Running turns may finish.'
: continuation.stopReason === 'paused' ? 'Owner policy is paused; no new automatic starts.' : 'Policy permits new eligible turns; this does not verify timer installation.'} ${continuation.held?.length ?? 0} unchanged-work hold(s) need Manager coordination. This is not measured token or cost usage.`
: 'Automatic continuation policy is unavailable; do not assume the fleet can start more work.');
const project = snapshot.project ?? snapshot.repository;
const agents = snapshot.agents ?? [];
const tasks = snapshot.backlog ?? [];
+2
View File
@@ -17,6 +17,7 @@ import { observeTaskActivity } from '../backlog/activity.mjs';
import { ScheduleLauncherService } from '../schedules/launcher.mjs';
import { hierarchyOrder } from '../runtime/hierarchy-order.mjs';
import { observeRuntimeTurn } from '../runtime/turn-guard.mjs';
import { observeContinuation } from '../runtime/continuation.mjs';
function json(path) {
return JSON.parse(readFileSync(path, 'utf8'));
@@ -404,6 +405,7 @@ export function observeProject({ repositoryRoot, env = process.env, now = () =>
metrics: ['cachedInput', 'uncachedInput', 'cacheCreation', 'cacheRead', 'compactions', 'resumedPromptBytes', 'costPerVerifiedItem'],
},
};
result.continuation = observeContinuation({ stateRoot, tasks: result.backlog, now });
result.backlogHealth = backlogHealth(repository.root, result.backlog, result.agents, now);
result.backlogActivity = observeBacklogActivity({ repositoryRoot: repository.root,
tasks: result.backlog, config, manifest, now: now() });
+21
View File
@@ -43,6 +43,27 @@ export function configureContinuation(control, { actorId, enabled, maxTurnsPerDa
return { enabled, maxTurnsPerDay, areas: state.areas, sessionsStarted: false };
}
export function observeContinuation({ stateRoot, tasks = [], now = () => new Date() } = {}) {
try {
const state = read({ stateRoot });
const day = now().toISOString().slice(0, 10);
const attempts = state.day === day ? state.attempts : 0;
const remainingTurns = Math.max(0, state.maxTurnsPerDay - attempts);
const held = Object.entries(state.progress).filter(([area, progress]) => {
const active = tasks.filter(t => t.owner === area && ['assigned', 'in_progress'].includes(t.state));
return active.length && progress?.status === 0 && progress.noProgress >= 2 && progress.taskKey === taskKey(active);
}).map(([areaId]) => ({ areaId, reason: 'no-progress-needs-coordination' }));
return { available: existsSync(join(stateRoot, 'continuation-policy.json')), enabled: state.enabled,
day, dayTimezone: 'UTC', attempts, maxTurnsPerDay: state.maxTurnsPerDay, remainingTurns,
stopReason: !state.enabled ? 'paused' : remainingTurns === 0 ? 'daily-turn-cap' : null,
capacityHeldBy: occupiedAreas({ stateRoot }), held,
budgetKind: 'automatic-turn-cap', manualTurnsIncluded: false, tokenOrCostGovernance: 'unknown',
timerInstallationVerified: false, mutationPerformed: false };
} catch {
return { available: false, stopReason: 'policy-or-ledger-unreadable', mutationPerformed: false };
}
}
export function planContinuation(control, backlog, { at = new Date(), limit = 3 } = {}) {
const state = read(control);
const day = at.toISOString().slice(0, 10);
+12
View File
@@ -6,6 +6,18 @@ import '../../site/live-refresh.js';
const project = (snapshot) => globalThis.TorchAttentionProjection.groups(snapshot);
test('SCN-console-continuation-stop: exhausted budget is an owner policy decision; no-progress remains manager-owned', () => {
const groups = project({ continuation: { available: true, stopReason: 'daily-turn-cap', attempts: 12,
maxTurnsPerDay: 12, day: '2026-10-03', held: [{ areaId: 'owner-console' }] } });
assert.equal(groups.owner.length, 1);
assert.match(groups.owner[0].detail, /explicitly change the limit/);
assert.equal(groups.owner[0].decisionApprovalId, null, 'No fake approval or automatic budget expansion');
assert.equal(groups.fleet.length, 1);
assert.equal(groups.fleet[0].owner, 'session-manager');
assert.equal(groups.fleet[0].requestOwner, 'session-manager');
assert.equal(project({ continuation: { available: true, stopReason: 'paused', held: [] } }).owner.length, 0);
});
test('SCN-console-attention-ownership: only owner-addressed pending decisions enter Waiting on you', () => {
const groups = project({
agents: [
+28 -1
View File
@@ -12,7 +12,7 @@ import { openControlPlane } from '../../src/control-plane/service.mjs';
import { issueStoppedExecutorEvidence } from '../../src/runtime/stopped-executor-evidence.mjs';
import { observeRuntimeTurn, withRuntimeTurnGuard } from '../../src/runtime/turn-guard.mjs';
import { observeProject } from '../../src/observability/snapshot.mjs';
import { configureContinuation, planContinuation, runContinuation } from '../../src/runtime/continuation.mjs';
import { configureContinuation, observeContinuation, planContinuation, runContinuation } from '../../src/runtime/continuation.mjs';
function fixture({ recoveryWorktree = true, extraWorker = false } = {}) {
const root = mkdtempSync(join(tmpdir(), 'torch-legacy-recovery-'));
@@ -332,6 +332,33 @@ test('SCN-runtime-continuation-fairness: a fresh task revision does not let one
} finally { f.control.close(); }
});
test('SCN-runtime-continuation-health: budget stops, day reset, pause and unreadable ledgers are visible without mutation', async () => {
const f = fixture({ recoveryWorktree: false });
const at = new Date('2026-10-03T00:00:00Z');
try {
f.control.reportStatus({ areaId: f.areaId, state: 'idle', summary: 'Idle' });
f.control.sendOwnerRequest({ actorId: 'owner', recipient: f.areaId, body: 'Work' });
configureContinuation(f.control, { actorId: 'owner', enabled: true, maxTurnsPerDay: 1 });
await runContinuation(f.control, { list: () => [] }, { actorId: 'owner', at, launch: () => 0 });
const path = join(f.control.stateRoot, 'continuation.json');
const before = readFileSync(path, 'utf8');
const health = observeContinuation({ stateRoot: f.control.stateRoot, now: () => at });
assert.equal(health.stopReason, 'daily-turn-cap');
assert.equal(health.remainingTurns, 0);
assert.equal(health.manualTurnsIncluded, false);
assert.equal(health.timerInstallationVerified, false);
const snapshot = observeProject({ repositoryRoot: f.root, env: f.env, now: () => at });
assert.equal(snapshot.continuation.stopReason, 'daily-turn-cap');
assert.equal(readFileSync(path, 'utf8'), before);
assert.equal(observeContinuation({ stateRoot: f.control.stateRoot, now: () => new Date('2026-10-04T00:00:00Z') }).remainingTurns, 1);
configureContinuation(f.control, { actorId: 'owner', enabled: false, maxTurnsPerDay: 1 });
assert.equal(observeContinuation({ stateRoot: f.control.stateRoot, now: () => at }).stopReason, 'paused');
writeFileSync(path, 'not-json');
assert.equal(observeContinuation({ stateRoot: f.control.stateRoot, now: () => at }).stopReason, 'policy-or-ledger-unreadable');
assert.equal(readFileSync(path, 'utf8'), 'not-json', 'Observation must not repair or reset the attempt ledger');
} finally { f.control.close(); }
});
test('SCN-legacy-runtime-recovery-project: evidence cannot be applied to a different checkout with cloned project/session fields', () => {
const first = fixture();
const second = fixture();