From d636cae93c5a67509365358439d680f4ce473979 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN Date: Wed, 30 Sep 2026 16:08:30 -0400 Subject: [PATCH 1/4] Fix scalar systemd WorkingDirectory with native parser regression --- docs/TEST_INTEGRITY.md | 22 ++++++++++++++++++++++ src/schedules/launcher.mjs | 14 +++++++++++++- test/fleet/schedules.test.mjs | 21 ++++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/docs/TEST_INTEGRITY.md b/docs/TEST_INTEGRITY.md index e6e5832..c426613 100644 --- a/docs/TEST_INTEGRITY.md +++ b/docs/TEST_INTEGRITY.md @@ -2268,3 +2268,25 @@ test_integrity_note: did_relax_any_assertion: false if_true_explain_spec_basis: "" ``` +# First live scheduler pilot: native systemd parser (2026-09-30) + +```yaml +test_integrity_note: + change_type: new_tests + scenarios: + - id: SCN-systemd-native-unit + given: "A generated dispatcher with a repository path containing spaces, percent and quote characters" + when: "The real Linux systemd-analyze parser verifies the emitted service" + then: "The service parses successfully and WorkingDirectory remains an absolute scalar path" + observable_outcomes: + - "Actual native parser exit status and generated unit bytes" + determinism_controls: + - "Temporary project path alias and unit file; /usr/bin/true command; verify-only, no service activation" + anti_cheat_rationale: + prevents: + - "Mocked systemctl success masking invalid unit syntax" + - "Treating quoted ExecStart syntax as valid for scalar WorkingDirectory" + relaxation: + did_relax_any_assertion: false + if_true_explain_spec_basis: "" +``` diff --git a/src/schedules/launcher.mjs b/src/schedules/launcher.mjs index d3a3222..d59e9e6 100644 --- a/src/schedules/launcher.mjs +++ b/src/schedules/launcher.mjs @@ -15,6 +15,18 @@ function unitQuote(value) { return `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`; } +// WorkingDirectory is a scalar path, not an ExecStart-style quoted word. +// C-escape special path bytes and escape systemd specifiers without adding quotes. +function unitPath(value) { + return [...String(value)].map(character => { + if (character === '%') return '%%'; + if (character.charCodeAt(0) <= 0x20 || character === '\\' || character === '"') { + return `\\x${character.charCodeAt(0).toString(16).padStart(2, '0')}`; + } + return character; + }).join(''); +} + function configPath(repositoryRoot) { return join(repositoryRoot, '.torch', 'torch.yaml'); } @@ -39,7 +51,7 @@ function render({ repositoryRoot, projectId, command, digest, env }) { ? `Environment=${unitQuote(`XDG_DATA_HOME=${resolve(env.XDG_DATA_HOME)}`)}\n` : ''; const service = [ '[Unit]', `Description=TORCH system schedules for ${projectId}`, '', '[Service]', 'Type=oneshot', - `WorkingDirectory=${unitQuote(repositoryRoot)}`, environment.trimEnd(), + `WorkingDirectory=${unitPath(repositoryRoot)}`, environment.trimEnd(), `ExecStart=${args.map(unitQuote).join(' ')}`, '', ].filter((line) => line !== '').join('\n') + '\n'; const timer = [ diff --git a/test/fleet/schedules.test.mjs b/test/fleet/schedules.test.mjs index 536ae04..77e8159 100644 --- a/test/fleet/schedules.test.mjs +++ b/test/fleet/schedules.test.mjs @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import { execFileSync, spawnSync } from 'node:child_process'; -import { existsSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'; +import { existsSync, mkdtempSync, readFileSync, symlinkSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import test from 'node:test'; @@ -16,6 +16,25 @@ import { loadProjectConfig, validateProjectConfig } from '../../src/kernel/confi import { CheckService } from '../../src/checks/service.mjs'; import { IntegrationService } from '../../src/integration/service.mjs'; import { createWorktrees } from '../../src/kernel/worktrees.mjs'; + +test('SCN-systemd-native-unit: generated dispatcher is accepted by the real systemd parser', (t) => { + if (process.platform !== 'linux') return t.skip('Native systemd qualification is Linux-specific'); + const context = fixture(); + const alias = join(mkdtempSync(join(tmpdir(), 'torch-unit-path-')), 'project space % "quoted"'); + symlinkSync(context.root, alias, 'dir'); + const launcher = new ScheduleLauncherService({ repositoryRoot: alias, env: context.env, command: ['/usr/bin/true'] }); + const plan = launcher.plan(); + const service = plan.files.find(file => file.name.endsWith('.service')); + const directory = mkdtempSync(join(tmpdir(), 'torch-systemd-verify-')); + const path = join(directory, service.name); + writeFileSync(path, service.content); + assert.match(service.content, /WorkingDirectory=\//); + assert.ok(!service.content.includes('WorkingDirectory="')); + assert.ok(service.content.includes('project\\x20space\\x20%%\\x20\\x22quoted\\x22')); + const checked = spawnSync('systemd-analyze', ['--user', 'verify', path], { encoding: 'utf8' }); + assert.equal(checked.error, undefined, checked.error?.message); + assert.equal(checked.status, 0, checked.stderr); +}); import { createRuntimeAdapterRegistry } from '../../src/adapters/registry.mjs'; import { planAreaUp } from '../../src/runtime/lifecycle.mjs'; import { observeProject } from '../../src/observability/snapshot.mjs'; From 94062020c767ab7ef12647860e8afed00055b7fd Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN Date: Wed, 30 Sep 2026 16:25:03 -0400 Subject: [PATCH 2/4] Record live self-development pilot and guard automatic wake outcomes --- .torch/install-manifest.json | 14 +++ .torch/prompts/COMMON.md | 5 +- .torch/torch.yaml | 2 +- docs/TODO.md | 24 ++++- .../first-development-pilot-2026-09-30.md | 96 +++++++++++++++++++ 5 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 reports/audit/first-development-pilot-2026-09-30.md diff --git a/.torch/install-manifest.json b/.torch/install-manifest.json index 59b1033..bd3f204 100644 --- a/.torch/install-manifest.json +++ b/.torch/install-manifest.json @@ -123,6 +123,20 @@ "area": "qa", "path": "/home/user/TORCHWorktrees/3dc11932-6e3c-42aa-9a8a-ca7b8f818b54/qa", "branch": "torch/qa" + }, + { + "type": "systemd-user-unit", + "name": "torch-3dc11932-6e3c-42aa-9a8a-ca7b8f818b54-schedules.service", + "path": "/home/user/.config/systemd/user/torch-3dc11932-6e3c-42aa-9a8a-ca7b8f818b54-schedules.service", + "sha256": "d1f6318f91e7b64847e52e415db52c66b60150b0c809fc1e0acb283d8bc0926c", + "configDigest": "4e21d6bf76dbcb983d984af2bf44a199ffb56d65e88a5c5f03cd678f196b329f" + }, + { + "type": "systemd-user-unit", + "name": "torch-3dc11932-6e3c-42aa-9a8a-ca7b8f818b54-schedules.timer", + "path": "/home/user/.config/systemd/user/torch-3dc11932-6e3c-42aa-9a8a-ca7b8f818b54-schedules.timer", + "sha256": "7c878a828c3f2a04cc597b12de3702f2dfd2ac6ac8228099ada27c59e00151f0", + "configDigest": "4e21d6bf76dbcb983d984af2bf44a199ffb56d65e88a5c5f03cd678f196b329f" } ] } diff --git a/.torch/prompts/COMMON.md b/.torch/prompts/COMMON.md index 973199d..c4d27fd 100644 --- a/.torch/prompts/COMMON.md +++ b/.torch/prompts/COMMON.md @@ -32,7 +32,10 @@ credentials. Keep pipeline work and Claude/Pi migration queued for later. Machine manager check-ins are owner-approved hourly with a maximum of 12 automatic invocations per UTC day in subscription invocation-count mode (not a -dollar cap). They inspect direct reports/approval waits and report exceptions; +dollar cap). Automatic AI wake execution is currently PAUSED pending +TASK-pilot-executor-outcomes qualification; the machine coordination timer still +queues check-ins. Do not re-enable wakes yourself. They inspect direct +reports/approval waits and report exceptions; they must not launch specialists or expand this slice. Provider-runtime turns are launched by the arbiter, not automatically by the manager. Do not change timers or budgets yourself. When blocked or finished, record evidence and end the turn; diff --git a/.torch/torch.yaml b/.torch/torch.yaml index e0adec4..ea2d420 100644 --- a/.torch/torch.yaml +++ b/.torch/torch.yaml @@ -126,7 +126,7 @@ "action": { "type": "manager-check-in", "manager_id": "session-manager", - "wake": { "enabled": true, "budget_mode": "invocation-count" } + "wake": { "enabled": false, "budget_mode": "invocation-count" } }, "required_authority": [ "owner" diff --git a/docs/TODO.md b/docs/TODO.md index e33adf3..bc0e0e6 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -4,7 +4,10 @@ TORCH is installed and healthy at `0.1.0-alpha.2`. Setup/restore and all seven worktree briefs have been verified. The active organization is Session Manager -plus six specialists; all providers are offline. The default Git branch remains +plus six specialists. A first bounded development pilot is now active through +Session Manager, Provider Runtime and QA; the remaining domains have not started. +See `reports/audit/first-development-pilot-2026-09-30.md` for current live state. +The default Git branch remains legacy TORCH; the configured development/integration boundary is `rewrite/portable-agent-fleet`. No publication or default-branch promotion is authorized. Later sections retain historical audit snapshots, including old @@ -54,10 +57,21 @@ dependencies, and remain unassigned for manager triage. See No COMBATRIG schedules, Vercel deployment, credentials, LAN exposure or automatic phone notifications are assumed. -These are proposed development tasks, **not implemented automatic setup**. The -existing 15-minute manager schedule is configured, but no TORCH host timer is -installed. Common instructions still authorize only the smoke test; development -activation, self-claim and recurring provider wakes remain separately reviewed. +These are proposed development tasks, **not implemented automatic init/install**. +For the first pilot the arbiter explicitly installed an hourly machine manager +coordination timer and verified real systemd parsing/activation. Automatic AI +wakes are paused pending the timeout/outcome guard; self-claim remains disabled. +COMMON.md authorizes only the diagnostics development slice, not broad autonomous +dispatch. The original 34 proposals remain the handoff; three newly observed +pilot reliability issues have also been added to the same backlog: + +- [ ] `TASK-pilot-starting-presence`: publish guarded startup state before a + blocking provider launch and prevent duplicate manual/timer execution. +- [ ] `TASK-pilot-systemd-rendering`: native scalar-path rendering is fixed and + qualified in source; finish active/failed unit evidence and false-success + handling instead of equating installed files with an operational timer. +- [ ] `TASK-pilot-executor-outcomes`: zero exit plus timeout/error/signal cannot + produce a successful lifecycle/wake receipt; qualify before automatic wakes. ## First real self-host run (2026-09-30) diff --git a/reports/audit/first-development-pilot-2026-09-30.md b/reports/audit/first-development-pilot-2026-09-30.md new file mode 100644 index 0000000..b5e430a --- /dev/null +++ b/reports/audit/first-development-pilot-2026-09-30.md @@ -0,0 +1,96 @@ +# First TORCH-building-TORCH development pilot + +## Owner authorization and live monitor + +The owner requested a staged self-development fleet, with the outer assistant +as arbiter through Session Manager, then a quality release/update and a later +Claude Code/Pi substitution test. COMMON.md records the bounded first slice: +`TASK-conversation-runtime-diagnostics`. Other development work, remote pushes, +landing, public release, deployment, default-main promotion and provider migration +are not authorized by this pilot. + +Monitor: **http://127.0.0.1:4174/console**. The snapshot is installed mode for +project `3dc11932-6e3c-42aa-9a8a-ca7b8f818b54`, not demo state. The original +Console process died during an outer-daemon restart. It was restored as the +loopback-only transient user service +`torch-3dc11932-6e3c-42aa-9a8a-ca7b8f818b54-console-pilot.service`, outside the +assistant process lifetime. This is not yet a reboot-persistent installed +Console integration; automatic service provisioning remains backlog work. + +## Real agent evidence + +- Session Manager resumed the same Sol runtime and assigned the real task to + Provider Runtime with revision-checked proposed → ready → assigned transitions. +- Provider Runtime started as the configured Terra model, retained runtime + session `01a0f3e1-9106-72c3-b8b2-2f1cc5322ccc`, queried ownership and requested + QA consent before editing tests. It committed production changes at + `c362ccd5cc8fff94c59d4a0c8f178628dc5e7596` on its own branch. That is not landed + or accepted release evidence. +- QA ran real Terra coordination turns and gave exact additive-test consent for + `test/fleet/lifecycle.test.mjs` and `docs/TEST_INTEGRITY.md`, retaining existing + assertions and requiring independent exact-candidate review. +- A daemon restart interrupted implementation. The arbiter checked owned live + Codex process absence, preserved dirty code/tests/generated screenshots and + resumed the same assignment via a transient `runtime-pilot-turn` user service. + No unrelated process was killed or user work reverted. + +Provider-reported focused checks passed, but its generic full-suite failures and +final regression evidence still require exact-source qualification. Do not infer +completion from a commit, active process or green controller receipt. Generated +test screenshots remain agent work and must not be blindly committed as changed +expectations. First launch presence lag and assigned-versus-working task state +need review; startup badges are not independent process-liveness proof. + +## Machine scheduler: qualified setup, guarded AI execution + +The owner-approved hourly check-in policy retains a maximum of 12 automatic +invocations per UTC day in subscription invocation-count mode, not a USD cap. +The project-owned persistent systemd timer was explicitly installed for the +pilot. This manual qualification does not implement automatic init/install yet. + +Initial live install exposed invalid `WorkingDirectory="/absolute/path"` output. +The actual parser rejected it, while file-based status said installed and the +timer was enabled but inactive. Minimal source repair `d636cae` emits an absolute +scalar path with C escaping and literal specifier handling. A new native-parser +scenario includes spaces, percent and quote characters. Fourteen scheduling tests +and all 39 source acceptance groups PASS; no existing test was weakened. + +Reconciliation used the tested source CLI and preserved owned-file hashes. The +timer is active and its first dispatch produced a durable check-in and wake +receipt. Installed stable tool remains alpha.2; the repaired scheduler currently +executes the source CLI, not a newly packaged stable release. A later qualified +tool update must incorporate the repair and reconcile the launcher. + +The first automatic manager invocation lasted exactly the 300-second executor +limit and was recorded succeeded/invoked. Independent model-turn completion was +not established. A native local child trapping SIGTERM reproduced +`status: 0`, `signal: null`, `error: ETIMEDOUT`; current startFleet ignores the +error when exit status is zero. This confirms a false-success boundary, not +independent proof of how that particular model turn ended. Historical receipts +were preserved, not rewritten. + +Automatic AI wakes are therefore **disabled** until `TASK-pilot-executor-outcomes` +is fixed and qualified. The timer remains active for durable machine coordination; +the arbiter explicitly drives the bounded specialist turns. Do not claim the +fleet is ready for unattended autonomy. + +## Backlog and next gates + +The original 34 conversation follow-ups are preserved. Three observed pilot +issues were added through audited owner intake: starting presence, native unit +rendering/operational status and executor timeout/outcome semantics. The manager +backlog remains the authoritative queue, separate from this narrative report. + +1. Finish diagnostics regression evidence and independent QA review. +2. Fix executor outcome/launch-presence guards and qualify one real bounded wake. +3. Complete reviewed automatic init/install onboarding and persistent operations. +4. Expand the fleet's scope only after a successful bounded work/verification/ + landing/resume cycle; never directly push competing specialist branches. +5. Build/accept/update a quality artifact with current receipts, then separately + test Claude/Pi handover of the same durable identities, tasks, messages and + checkpoints. No model/provider swap occurred in this pilot. + +Receipts are under `/tmp/torch-pilot-*`, including repaired full-source acceptance, +native parser tests, scheduler installation/reconciliation, durable wake/run +state and owner-request messages. The live dashboard and actual branch state +take precedence over historical progress descriptions in this report. From ed1f7614d1ea2634c24c9967a673d753f32c8080 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN Date: Wed, 30 Sep 2026 16:32:52 -0400 Subject: [PATCH 3/4] Record first pilot verification blocker and provider handover gate --- docs/TODO.md | 6 ++++++ reports/audit/first-development-pilot-2026-09-30.md | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/TODO.md b/docs/TODO.md index bc0e0e6..a7c764a 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -72,6 +72,12 @@ pilot reliability issues have also been added to the same backlog: handling instead of equating installed files with an operational timer. - [ ] `TASK-pilot-executor-outcomes`: zero exit plus timeout/error/signal cannot produce a successful lifecycle/wake receipt; qualify before automatic wakes. +- [ ] `TASK-pilot-generated-check-artifacts`: retain exact-commit check artifacts + outside the tested tree and safely recover proven generated outputs so a gate + does not make the following required gate fail WORKTREE_DIRTY. +- [ ] `TASK-provider-handover`: after release qualification, checkpoint and safely + switch one session then the fleet to reviewed Claude Code/Pi profiles; preserve + TORCH identity/tasks/messages but never reuse another harness's native session ID. ## First real self-host run (2026-09-30) diff --git a/reports/audit/first-development-pilot-2026-09-30.md b/reports/audit/first-development-pilot-2026-09-30.md index b5e430a..e2f3e9a 100644 --- a/reports/audit/first-development-pilot-2026-09-30.md +++ b/reports/audit/first-development-pilot-2026-09-30.md @@ -81,6 +81,16 @@ issues were added through audited owner intake: starting presence, native unit rendering/operational status and executor timeout/outcome semantics. The manager backlog remains the authoritative queue, separate from this narrative report. +Recovery follow-up: the specialist produced regression commit `2f1ca15` but exact +TORCH check planning was blocked by five tracked report images generated by the +dashboard gate. The owner/arbiter gave narrow reversible archive-and-restore +directions for proven check-owned outputs, not permission to rewrite expectations. +`TASK-pilot-generated-check-artifacts` records the durable fix. The explicitly +requested post-quality-release whole-fleet provider migration now has its own +`TASK-provider-handover`, dependent on release qualification. Total intake is +39 items (34 original follow-ups plus five later pilot/handover items); one real +implementation assignment does not imply the rest are dispatched. + 1. Finish diagnostics regression evidence and independent QA review. 2. Fix executor outcome/launch-presence guards and qualify one real bounded wake. 3. Complete reviewed automatic init/install onboarding and persistent operations. From 0224abf2e08de0b9e9d44ebddb3cea66b5076c13 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN Date: Wed, 30 Sep 2026 18:20:05 -0400 Subject: [PATCH 4/4] Record owner-approved computer-use Console audit and chat roadmap --- .torch/prompts/COMMON.md | 17 +++++++++++ docs/TODO.md | 17 +++++++++++ .../first-development-pilot-2026-09-30.md | 28 +++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/.torch/prompts/COMMON.md b/.torch/prompts/COMMON.md index c4d27fd..7ce64c4 100644 --- a/.torch/prompts/COMMON.md +++ b/.torch/prompts/COMMON.md @@ -19,6 +19,23 @@ coordinate its Release/QA boundaries and request independent review. Do not dispatch other development items in this first pilot. The outer arbiter controls provider starts, integration authorization and the next slice. +## Owner-approved parallel Console audit (2026-09-30) + +The owner additionally authorizes TASK-console-ux-audit for owner-console. +Session Manager may triage and assign that exact task while diagnostics QA +continues. The arbiter launches the assigned UI session; managers still must not +launch providers. This is a browser-based audit and design proposal, not a +dashboard rewrite or release. Audit the actual live Console through computer +use: navigate every area, inspect screenshots and interactions at desktop and +mobile sizes, and document coverage. Read applicable frontend-design and browser +skills before browser use. If computer-use tools are unavailable, report that +specific blocker; source inspection alone is not completion. Do not substitute +generated screenshots for actually viewing the interface. Keep live state +read-only; use the isolated demo for exploratory mutations. Publish task-owned +screenshots/evidence and an audit report proposing focused separate views rather +than one long anchor-linked page. Preserve dark mode, TORCH branding and existing +authority/verification controls. No other implementation slice is authorized. + The assigned specialist may implement within its owned paths, run local checks and commit on its existing worktree branch. Ask the responsible owner before editing shared/other-owned files. Name the task in commits, preserve all existing diff --git a/docs/TODO.md b/docs/TODO.md index a7c764a..a722d56 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -78,6 +78,23 @@ pilot reliability issues have also been added to the same backlog: - [ ] `TASK-provider-handover`: after release qualification, checkpoint and safely switch one session then the fleet to reviewed Claude Code/Pi profiles; preserve TORCH identity/tasks/messages but never reuse another harness's native session ID. +- [ ] `TASK-owner-agent-chat`: persistent per-agent owner conversations for the + Manager and every activated domain, durable replies and clear queued/delivered/ + running/failed outcomes. Reviewed wake-on-message must respect pauses, budgets + and runtime leases; qualify executor outcomes and starting presence first. + +The owner approved continuing the controlled pilot. Independent QA now has exact +candidate `ae18a98e2911aa515d0484518013061b05ff6adf` and the Manager has a bounded +next-slice planning request. The candidate's test/check/lint receipts pass; its +dashboard receipt remains incomplete because the test writes tracked reports. +Neither review launch nor controller exit is acceptance or landing evidence. +There are now 41 owner-intake items, including agent chat and the UI audit. + +- [ ] `TASK-console-ux-audit`: Session Manager assigns Owner Console to audit + every live dashboard area through actual computer use and inspected screenshots, + including desktop/mobile and keyboard/refresh behavior. Owner rejects the long + single-page/anchor-navigation layout. Propose focused separate screens, concise + overview and contextual drilldowns. Audit/proposal only; no UI rewrite yet. ## First real self-host run (2026-09-30) diff --git a/reports/audit/first-development-pilot-2026-09-30.md b/reports/audit/first-development-pilot-2026-09-30.md index e2f3e9a..8c11e83 100644 --- a/reports/audit/first-development-pilot-2026-09-30.md +++ b/reports/audit/first-development-pilot-2026-09-30.md @@ -104,3 +104,31 @@ Receipts are under `/tmp/torch-pilot-*`, including repaired full-source acceptan native parser tests, scheduler installation/reconciliation, durable wake/run state and owner-request messages. The live dashboard and actual branch state take precedence over historical progress descriptions in this report. + +## Continued pilot and owner UX feedback + +The owner approved continuing the controlled pilot and is watching the Console. +Independent QA reviewed candidate `ae18a98e2911aa515d0484518013061b05ff6adf`: +the lifecycle scenario independently PASSed 7/7, with no assertion relaxation. +QA accepted code/scenario content, **not integration qualification**. Required +dashboard receipt remains incomplete and canonical convergence remains required. +QA's durable report is message `8275d64a-7b28-49ee-a379-2c6d0bdcacb3`. + +Two additional owner-intake proposals bring the backlog to 41: + +- `TASK-owner-agent-chat`: threaded durable owner conversations and reviewed + wake-on-message after executor/presence qualification. Current agent message + recipient validation does not permit the owner as a recipient; owner replies + need a distinct authorized API, not an invented specialist identity. +- `TASK-console-ux-audit`: the owner rejects excessive scrolling and a generic + all-in-one-page dashboard. Session Manager has an explicit bounded assignment + request for existing Owner Console. Require actual computer-use navigation, + inspected screenshots of every current area, desktop/mobile and interaction + coverage, and a focused multi-screen proposal. Live state is read-only; demo + may exercise mutations. Missing browser tools are a blocker, not permission + to claim a source-only audit completed the request. + +Canonical COMMON now permits this audit in parallel with diagnostics QA, without +authorizing UI implementation, unrelated dispatch, automatic wakes or release. +The arbiter launches Owner Console only after actual manager assignment; a +request in an inbox is not itself assignment evidence.