diff --git a/reports/test-audit/console-attention-integrity-report-2026-10-01.md b/reports/test-audit/console-attention-integrity-report-2026-10-01.md
index 390e0f5..7c6c4d8 100644
--- a/reports/test-audit/console-attention-integrity-report-2026-10-01.md
+++ b/reports/test-audit/console-attention-integrity-report-2026-10-01.md
@@ -24,12 +24,17 @@ test_integrity_note:
given: "Approval evidence is unavailable and no queue or doctor finding is observed"
when: "Attention items are projected"
then: "No synthetic owner decision or operational issue is created"
+ - id: SCN-console-attention-authority
+ given: "Integration records may name a manager as authorizer while other runtime records lack an arbiter grant"
+ when: "Attention ownership is projected from the live roster"
+ then: "Roster titles supply display names, integration remains Fleet handling, and no arbiter-only authority is inferred"
observable_outcomes:
- "Attention groups distinguish owner decisions, Fleet work, and arbiter work"
- "Approval action ownership follows the named approver"
- "Repeated message queue evidence produces one item"
- "Worktree progress and unsafe Git state have distinct descriptions"
- "Unknown evidence stays explicit rather than gaining an invented owner"
+ - "Manager-authorized integration is not mislabeled as an owner or arbiter decision"
determinism_controls:
- "Tests pass fixed in-memory snapshots; no clock, network, or filesystem is consulted"
anti_cheat_rationale:
diff --git a/site/attention-projection.js b/site/attention-projection.js
index 77618f6..54c27e1 100644
--- a/site/attention-projection.js
+++ b/site/attention-projection.js
@@ -1,12 +1,5 @@
(function (global) {
const shortCommit = (value) => value ? String(value).slice(0, 9) : '—';
-const ownerDomainNames = {
- 'release-self-host': 'Release and Self-host',
- 'work-integration': 'Work and Integration',
- 'project-kernel': 'Project Kernel',
- 'session-manager': 'Session Manager',
-};
-
function worktreeProblemLabel(problem) {
if (problem === 'worktree-dirty') return 'Uncommitted changes are present';
if (problem === 'worktree-missing') return 'The managed worktree is missing';
@@ -19,7 +12,7 @@ function worktreeProblemLabel(problem) {
function attentionGroups(snapshot) {
const groups = { owner: [], fleet: [], arbiter: [] };
const agents = new Map((snapshot.agents ?? []).map((agent) => [agent.areaId, agent]));
- const agentName = (id) => ownerDomainNames[id] ?? agents.get(id)?.title ?? id ?? 'Owner not recorded';
+ 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');
@@ -31,6 +24,7 @@ function attentionGroups(snapshot) {
owner: isOwner ? 'Project owner' : agentName(approval.approver),
detail: `${agentName(approval.requester)} is waiting on ${isOwner ? 'your decision' : agentName(approval.approver)}. ${approval.summary || 'Request consequence and options are not recorded.'}`,
evidence: [approval.task, approval.evidence].filter(Boolean).join(' · ') || 'Request evidence not recorded',
+ waitSince: approval.createdAt ?? null,
href: `#approval-${encodeURIComponent(approval.id)}`,
action: isOwner ? 'Review request and evidence' : 'Open approval details',
});
@@ -73,7 +67,7 @@ function attentionGroups(snapshot) {
tone: !hasUnsafeProblem ? 'info' : item.problems.some((problem) => problem.startsWith('git-operation:') || problem === 'worktree-missing') ? 'urgent' : 'review',
title: !hasUnsafeProblem ? `${identity} has work ahead of main` : `${identity} worktree needs review`,
owner: identity,
- detail: `${labels.join('; ')}.${hasAhead ? ' Ahead commits are retained work progress, not a broken repository.' : ''} ${hasUnsafeProblem ? 'Review the exact worktree before deciding on recovery.' : 'Ask the owner to review a safe convergence plan.'}`,
+ detail: `${labels.join('; ')}.${hasAhead ? ' Ahead commits are retained work progress, not a broken repository.' : ''} ${hasUnsafeProblem ? 'Review the exact worktree before deciding on recovery.' : 'Ask the owning specialist or manager to review a safe convergence plan.'}`,
evidence: [worktree?.branch, worktree?.path ?? item.path, worktree?.ahead != null ? `${worktree.ahead} ahead / ${worktree.behind ?? 0} behind` : null].filter(Boolean).join(' · ') || 'Branch and path not recorded',
href: `#worktree-${encodeURIComponent(item.area)}`, action: 'Inspect this worktree',
requestOwner: item.area !== 'unknown-owner' ? item.area : null,
@@ -88,7 +82,7 @@ function attentionGroups(snapshot) {
: finding.code.replaceAll('_', ' ').toLowerCase().replace(/^./, (letter) => letter.toUpperCase());
add('fleet', {
tone: finding.severity === 'error' ? 'urgent' : 'review', title: humanTitle,
- owner: recoveryGap ? 'Release and Self-host' : finding.area ? agentName(finding.area) : 'Responsible domain not recorded',
+ owner: finding.area ? agentName(finding.area) : 'Responsible domain not recorded',
detail: recoveryGap
? `${finding.recommendation ?? 'Configure and synchronize an off-machine canonical Git boundary.'} A single disk failure could remove the only recorded recovery copy.`
: finding.message ?? finding.recommendation ?? 'Review this condition with the responsible domain; its consequence is not recorded.',
@@ -105,25 +99,31 @@ function attentionGroups(snapshot) {
});
const waitingIntegration = (snapshot.integration ?? []).filter((item) => !['landed', 'rejected', 'superseded'].includes(item.state));
- for (const item of waitingIntegration) add('arbiter', {
- tone: 'review', title: `Integration ${item.state}`, owner: 'Session Manager / integration authority',
- detail: 'Landing is serialized. The arbiter must check the exact candidate and required receipts before proceeding.',
- evidence: `${item.sourceArea} · ${shortCommit(item.sourceCommit)} · ${item.reason ?? 'reason not recorded'}`,
+ for (const item of waitingIntegration) add('fleet', {
+ tone: 'review', title: `Integration ${item.state}`, owner: item.authorizedBy ? agentName(item.authorizedBy) : 'Integration authority not recorded',
+ detail: 'Review the exact candidate and required receipts under the current named landing authority. Queue state does not establish that a separate owner decision is required.',
+ evidence: [item.sourceArea, shortCommit(item.sourceCommit), item.authorizedBy ? `authorized by ${agentName(item.authorizedBy)}` : null, item.reason ?? 'reason not recorded'].filter(Boolean).join(' · '),
href: '#delivery', action: 'Inspect integration queue',
});
const unknownWakes = snapshot.managerWakes?.blockingCount ?? 0;
- if (unknownWakes) add('arbiter', {
- tone: 'urgent', title: `${unknownWakes} manager launch${unknownWakes === 1 ? '' : 'es'} need inspection`,
- owner: 'Session Manager', detail: 'An unknown launch blocks another scheduled wake. Inspect recorded runtime evidence; no automatic restart is available.',
- evidence: 'A reserved launch does not prove the provider is still running.',
- href: '#manager-wakes', action: 'Inspect manager launches',
- });
+ if (unknownWakes) {
+ const reservations = (snapshot.managerWakes?.reservations ?? []).filter((item) => item.blocksManagerWake);
+ const wakeOwners = [...new Set(reservations.map((item) => agentName(item.managerId)))];
+ add('fleet', {
+ tone: 'urgent', title: `${unknownWakes} scheduled manager wake${unknownWakes === 1 ? '' : 's'} need inspection`,
+ owner: wakeOwners.join(', ') || 'Scheduled wake owner not recorded',
+ detail: 'A recorded reservation blocks another wake. Inspect current runtime evidence before acting; this record does not prove a provider is still running or establish who may restart it.',
+ evidence: reservations.map((item) => [item.scheduleId, item.reservedAt, item.outcome].filter(Boolean).join(' · ')).join('; ') || 'Reservation detail unavailable',
+ href: '#manager-wakes', action: 'Inspect manager launches',
+ });
+ }
const unresolvedDelivery = (snapshot.deliveryOperations ?? []).filter((operation) => ['running', 'unknown', 'succeeded'].includes(operation.state));
- if (unresolvedDelivery.length) add('arbiter', {
+ if (unresolvedDelivery.length) add('fleet', {
tone: 'urgent', title: `${unresolvedDelivery.length} delivery operation${unresolvedDelivery.length === 1 ? '' : 's'} need review`,
- owner: 'Release and Self-host', detail: 'Inspect recorded attempts and external state before considering recovery. Do not repeat effects automatically.',
+ owner: [...new Set(unresolvedDelivery.map((operation) => operation.actor ? agentName(operation.actor) : 'Operation owner not recorded'))].join(', '),
+ detail: 'Inspect recorded attempts and external state before considering recovery. Do not repeat effects automatically.',
evidence: unresolvedDelivery.map((operation) => `${operation.operation} · ${operation.state} · ${shortCommit(operation.commit)}`).join('; '),
href: '#operation-outcomes', action: 'Inspect operation outcomes',
});
diff --git a/site/console.js b/site/console.js
index baf16e9..d502b4c 100644
--- a/site/console.js
+++ b/site/console.js
@@ -732,7 +732,7 @@ function renderAttention(groups) {
const definitions = [
['owner', 'Waiting on you', 'Only requests that name the project owner as decision-maker.'],
['fleet', 'Fleet handling', 'A named domain owns the next step; unknown ownership stays explicit.'],
- ['arbiter', 'Arbiter handling', 'Serialized execution and installation decisions stay with the arbiter.'],
+ ['arbiter', 'Arbiter handling', 'No observed item has structured evidence assigning its next step exclusively to an arbiter.'],
];
const total = Object.values(groups).reduce((sum, items) => sum + items.length, 0);
setText('#attention-count', `${total} ${total === 1 ? 'item' : 'items'}`);
@@ -743,7 +743,7 @@ function renderAttention(groups) {
${items.length ? items.map((item) => `
${escapeHtml(item.title)}${escapeHtml(item.detail)}
-
Severity: ${escapeHtml(({ urgent: 'Urgent', review: 'Review', decision: 'Decision required', info: 'Progress' })[item.tone] ?? 'Unknown')} · Owner: ${escapeHtml(item.owner)} · ${escapeHtml(item.evidence)}
+
Severity: ${escapeHtml(({ urgent: 'Urgent', review: 'Review', decision: 'Decision required', info: 'Progress' })[item.tone] ?? 'Unknown')} · Owner: ${escapeHtml(item.owner)}${item.waitSince ? ` · Waiting ${escapeHtml(ageLabel(item.waitSince))}` : ''} · ${escapeHtml(item.evidence)}
${escapeHtml(item.action)}
${item.requestOwner ? `
Request review from ${escapeHtml(item.owner)}` : ''}
`).join('') : `
${escapeHtml(description)}
`}
diff --git a/test/fleet/console-attention.test.mjs b/test/fleet/console-attention.test.mjs
index 3f3e266..0355af4 100644
--- a/test/fleet/console-attention.test.mjs
+++ b/test/fleet/console-attention.test.mjs
@@ -12,7 +12,7 @@ test('SCN-console-attention-ownership: only owner-addressed pending decisions en
],
approvalRequests: { items: [
{ id: 'owner-review', status: 'pending', approver: 'owner', requester: 'provider-runtime',
- title: 'Review executor boundary', task: 'TASK-RUNTIME', evidence: 'receipt:17', summary: 'Confirm the bounded scope.' },
+ title: 'Review executor boundary', task: 'TASK-RUNTIME', evidence: 'receipt:17', createdAt: '2026-10-01T06:00:00Z', summary: 'Confirm the bounded scope.' },
{ id: 'qa-review', status: 'pending', approver: 'qa', requester: 'owner-console',
title: 'Confirm scenario path', summary: 'Review the owned test path.' },
] },
@@ -21,6 +21,7 @@ test('SCN-console-attention-ownership: only owner-addressed pending decisions en
assert.deepEqual(groups.owner.map((item) => item.title), ['Review executor boundary']);
assert.equal(groups.owner[0].href, '#approval-owner-review');
assert.match(groups.owner[0].detail, /bounded scope/);
+ assert.equal(groups.owner[0].waitSince, '2026-10-01T06:00:00Z');
assert.equal(groups.fleet[0].owner, 'Independent QA');
assert.equal(groups.fleet[0].action, 'Open approval details');
assert.doesNotMatch(JSON.stringify(groups.fleet), /Approve|Reject/);
@@ -47,20 +48,25 @@ test('SCN-console-attention-worktrees: findings group by owning area and disting
doctor: { findings: [
{ severity: 'warning', code: 'WORKTREE_PROBLEM', area: 'provider-runtime', path: '/work/provider', problem: 'unique-commits:2' },
{ severity: 'warning', code: 'WORKTREE_PROBLEM', areaId: 'provider-runtime', path: '/work/provider', problem: 'worktree-dirty' },
+ { severity: 'warning', code: 'WORKTREE_PROBLEM', area: 'project-kernel', path: '/work/kernel', problem: 'unique-commits:1' },
{ severity: 'error', code: 'WORKTREE_PROBLEM', area: 'qa', path: '/work/qa', problem: 'git-operation:REBASE_HEAD' },
] },
worktrees: [
{ area: 'provider-runtime', branch: 'runtime/fix', path: '/work/provider', ahead: 2, behind: 0 },
+ { area: 'project-kernel', branch: 'kernel/change', path: '/work/kernel', ahead: 1, behind: 0 },
{ area: 'qa', branch: 'qa/audit', path: '/work/qa', ahead: 0, behind: 0 },
],
});
- assert.equal(groups.fleet.length, 2);
+ assert.equal(groups.fleet.length, 3);
const runtime = groups.fleet.find((item) => item.owner === 'Provider Runtime');
assert.equal(runtime.tone, 'review');
- assert.match(runtime.detail, /retained work progress, not a broken repository/);
+ assert.match(runtime.detail, /Ahead commits are retained work progress, not a broken repository/);
assert.match(runtime.evidence, /runtime\/fix · \/work\/provider/);
assert.equal(runtime.requestOwner, 'provider-runtime');
+ const kernel = groups.fleet.find((item) => item.owner === 'project-kernel');
+ assert.equal(kernel.tone, 'info');
+ assert.match(kernel.detail, /owning specialist or manager/);
const qa = groups.fleet.find((item) => item.owner === 'qa');
assert.equal(qa.tone, 'urgent');
assert.match(qa.title, /worktree needs review/);
@@ -76,12 +82,31 @@ test('SCN-console-attention-wait-reasons: recovery and blocked task evidence kee
});
const recovery = groups.fleet.find((item) => item.title === 'Off-machine recovery copy is not verified');
- assert.equal(recovery.owner, 'Release and Self-host');
+ assert.equal(recovery.owner, 'Responsible domain not recorded');
assert.match(recovery.detail, /single disk failure could remove/);
assert.match(recovery.evidence, /ONE-DISK · abcdef012/);
assert.match(groups.fleet.find((item) => item.evidence === 'TASK-BLOCKED').detail, /no image was saved/);
});
+test('SCN-console-attention-authority: roster titles name owners and integration stays with Fleet unless arbiter authority is explicit', () => {
+ const groups = project({
+ agents: [{ areaId: 'coordinator-7', title: 'Delivery Coordinator' }],
+ integration: [
+ { state: 'awaiting-checks', sourceArea: 'build-team', sourceCommit: '1234567890abcdef', authorizedBy: 'coordinator-7' },
+ { state: 'queued', sourceArea: 'docs-team', sourceCommit: 'abcdef0123456789' },
+ ],
+ managerWakes: { blockingCount: 1, reservations: [{ managerId: 'coordinator-7', scheduleId: 'nightly-review',
+ outcome: 'reserved', blocksManagerWake: true }] },
+ });
+
+ assert.equal(groups.fleet.length, 3);
+ assert.equal(groups.fleet[0].owner, 'Delivery Coordinator');
+ assert.match(groups.fleet[0].evidence, /authorized by Delivery Coordinator/);
+ assert.equal(groups.fleet[1].owner, 'Integration authority not recorded');
+ assert.equal(groups.fleet[2].owner, 'Delivery Coordinator');
+ assert.deepEqual(groups.arbiter, []);
+});
+
test('SCN-console-attention-empty: an unavailable signal does not manufacture owner work', () => {
const groups = project({
approvalRequests: { available: false, reason: 'Structured approval service unavailable.' },