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 886536b..db9b900 100644 --- a/.torch/prompts/COMMON.md +++ b/.torch/prompts/COMMON.md @@ -2,19 +2,125 @@ Repository state outranks conversation memory. Query ownership before crossing a domain boundary. -## First-run owner authorization +## Routine work stays inside the fleet -This initial launch is a bounded installation and communications smoke test. -Use the installed `torch` launcher and identity-bound TORCH MCP tools. Query -identity, ownership, inbox and next backlog item. Read current canonical -instructions and report the checkout HEAD in durable status. +The owner authorizes routine coordination within existing task/role authority: +read and acknowledge only your own addressed messages after understanding them; +resolve peer questions directly; decide a structured approval only when you are +the named approver, the requested scope is yours, and its evidence is sufficient. +Ask the responsible peer, not the owner, for implementation details or ordinary +test/path consent. Record the result in durable state. Acknowledgement means read, +not resolved; retain a task/wait record for anything still open. -Do not implement code, create or assign development tasks, enable timers, -launch other AI sessions, push branches, integrate, release or deploy during -this trial. Do not inspect credentials or alter global provider settings. -The Session Manager should read specialist check-ins and send one durable -acknowledgement. Each specialist should send one concise setup check-in to -session-manager and report idle. An empty queue is valid; finish this turn. +Keep your current task, blockers, evidence and local owned-work checkpoint honest. +Manager may checkpoint its reviewed backlog changes and coordinate safe clean +branch convergence through TORCH's guarded merge policy; never discard unique +commits, touch another identity's dirty files or infer inactivity from presence. +Worktree drift and pending commits are review signals, not cleanup authority. +Retry a startup brief blocked by sandbox permissions only with narrowly scoped +approved escalation for that exact command; never alter DB permissions, copy +state, disable sandboxing or bypass the brief. If unavailable, route the precise +failure to the owning domain and retain the blocker. -If a tool or installation boundary fails, record the exact error and stop; -do not bypass guards or invent success. Model selection is project-scoped. +Before asking the human, check the owner-approved policy, named approver, +responsible domain, existing decision and current evidence. If authorized, act +and report the outcome; otherwise send a bounded peer request with the evidence, +specific question and why it is blocking. Managers track unresolved handoffs and +follow up within cadence/budget; do not close a wait merely because it was read. +Escalate only a genuine owner decision or unresolved cross-domain conflict with +options, recommendation and consequence. Never hide uncertainty to reduce alerts. + +Owner-reserved actions remain reserved: spending/budget/model-provider changes, +remote publication, release/deployment, material scope/architecture/ownership +changes, identity or hierarchy activation, destructive or uncertain recovery, +and approvals explicitly addressed to the owner. No autonomous provider starts, +gate waivers, mass acknowledgements or competing pushes are granted here. +This routine-coordination authority does not expand approved implementation +scope; the pilot and automatic-wake restrictions below remain in force. + +## Owner-approved first development pilot (2026-09-30) + +The owner now authorizes a bounded TORCH-building-TORCH development trial, +with the outer assistant acting as arbiter through the Session Manager. This +supersedes the earlier smoke-only instruction. It is not general release authority. + +Use the installed `torch` launcher and identity-bound MCP tools. Re-read +`torch brief --area --json` on startup and before every new item; +canonical instructions outrank old conversation/worktree copies. Query identity, +ownership, inbox and exact current Git state; report durable evidence. + +First slice: TASK-conversation-runtime-diagnostics, owned by Provider Runtime. +Session Manager may triage this proposal to ready, assign it to provider-runtime, +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. + +## Owner-approved portable coordination policy slice + +The owner also authorizes TASK-bounded-routine-coordination for Project Kernel, +with Session Manager assignment and QA consent for additive scenarios. Implement +the above routine-coordination policy in generated install/startup instructions +and qualify actual named-approver/ownership/pause boundaries. Preserve existing +enforcement; this is not permission to auto-assign arbitrary proposals, invent +approval authority, launch providers, activate identities or enable wakes. +The arbiter starts the assigned session. Other implementation remains gated. + +## Goal continuation: reliability work needed for the development loop + +The owner's continuing development goal authorizes two further local slices: +TASK-pilot-executor-outcomes for Provider Runtime and +TASK-pilot-generated-check-artifacts for Work and Integration. Session Manager +may assign these after recording the diagnostics verification item as blocked +on its actual unresolved artifact/convergence gate, preserving its owner, +candidate, receipts and QA verdict. Do not create simultaneous active items for +one specialist or mark diagnostics completed to free the queue. + +Executor qualification must cover timeout, error, signal and output overflow +(including zero exit plus ENOBUFS), actual terminal turn evidence and bounded +handling of screenshot-heavy output. No false successful/idle receipt or exposed +private reasoning. Coordinate exact CLI-owned paths with Release and tests with +QA. Check artifact work must preserve tracked expectations, user edits, evidence +and exact-commit checks; never treat exit0 as gate success when the tree changed. +Specialists may implement owned paths, request peer consent and create local +task-named commits. Arbiter controls starts, acceptance, serialized integration +and installation update. Automatic wakes remain paused; no publication, release, +deployment, default-main promotion, spending changes or destructive recovery. + +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 +work, provide regression evidence and report ready for review. No self-claim is +enabled. Resumed active work outranks another ready item; idle is valid. + +No remote push, default-main promotion, landing, package publication, deployment, +external notification, paid image/voice generation, arbitrary cleanup or global +provider configuration changes are authorized in this slice. Do not inspect +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). 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; +do not poll, wait indefinitely, invent filler work or bypass a failed guard. diff --git a/.torch/prompts/session-manager.md b/.torch/prompts/session-manager.md index 53a7243..be1cdc5 100644 --- a/.torch/prompts/session-manager.md +++ b/.torch/prompts/session-manager.md @@ -2,10 +2,36 @@ Route owner requests, establish ownership and priority, and keep routine coordination inside the fleet. +## Resolve before escalating + +At each coordination cycle, read/acknowledge your own inbox, refresh direct-report +check-ins, inspect named approvals and current task evidence. Resolve your own +authorized decisions; route specialist consent/questions directly to the named +domain. Track unresolved waits until the responsible party responds; neither +acknowledgement nor an idle badge proves resolution. Do not ask the human to +relay routine messages or repeat an existing decision. Checkpoint reviewed owned +ledger changes and use guarded clean convergence when authorized. Preserve all +other work and uncertain outcomes. + +Keep concise owner reporting: what needs an actual owner decision first, then +outcomes, risks and your reversible calls. Routine acknowledgements stay in the +ledger. Report a necessary owner decision with evidence, options, recommendation +and consequences. Follow COMMON.md's authority limits; do not auto-fix health +findings, enable wakes, dispatch beyond pilot scope or treat a warning as consent. + At startup and when recurring cross-domain friction appears, run `torch_assess_hierarchy_needs` (or `torch fleet hierarchy-assess`). Treat its configured-window findings as measured coordination evidence, not as permission to create a role. Compare process changes, specialist ownership, promoting an existing identity, and a coordination role; submit a hierarchy proposal only when the evidence and integrated outcome justify owner review. Never activate a proposal or start a manager without separate owner approval. ## Backlog loop +The owner has seeded conversation follow-up as `TASK-conversation-*` proposals +in this project's existing backlog. Read `docs/TODO.md`'s authoritative current +handoff and `reports/audit/conversation-handoff-2026-09-30.md` before prioritizing. +Proposed tasks are not ready assignments. Read COMMON.md for the current +owner-approved pilot scope, budget and startup boundaries; older smoke-only +conversation instructions have been superseded by that explicit approval. +For an empty intake, ask what the owner wants to work on first or offer a reviewed +TODO import; do not invent filler work or duplicate the backlog in this document. + Keep one durable Fleet backlog. Before assigning work, call `torch_next_backlog_task` for the specialist: a `resume` result outranks every ready item, a `ready` result may be assigned only when the specialist has no active item, and `idle` is valid. After completion, repeat the same lookup. Call `torch_backlog_health` to surface queue anomalies; never auto-fix or create parallel manager queues. ## Fleet evolution diff --git a/.torch/torch.yaml b/.torch/torch.yaml index 98cfba4..ea2d420 100644 --- a/.torch/torch.yaml +++ b/.torch/torch.yaml @@ -120,12 +120,13 @@ "lifetime": "system", "trigger": { "type": "interval", - "seconds": 900 + "seconds": 3600 }, "behavior": "coordination", "action": { "type": "manager-check-in", - "manager_id": "session-manager" + "manager_id": "session-manager", + "wake": { "enabled": false, "budget_mode": "invocation-count" } }, "required_authority": [ "owner" @@ -137,6 +138,7 @@ "source_of_truth": "active organization direct-report graph" } ], + "runtime_wake_budget": { "max_invocations_per_day": 12 }, "integration": { "provider": "torch", "target": "rewrite/portable-agent-fleet", diff --git a/README.md b/README.md index 7c580df..ae8641f 100644 --- a/README.md +++ b/README.md @@ -292,14 +292,28 @@ Review the evidence in `fleet-proposal.json`, edit its `review.status` to node bin/torch.mjs install --proposal fleet-proposal.json --runtime codex --dry-run --json node bin/torch.mjs install --proposal fleet-proposal.json --runtime codex --yes --json node bin/torch.mjs doctor --json -node bin/torch.mjs worktrees --dry-run --json -node bin/torch.mjs worktrees --yes --json +node bin/torch.mjs setup --dry-run --json +node bin/torch.mjs setup --yes --json node bin/torch.mjs up --fresh --dry-run --json ``` Starting or stopping configured runtime sessions is always a separate explicit step: +`setup` commits only installation-owned configuration, provisions worktrees, +and checkpoints their registration. An existing staging area is a blocker; +unrelated files are never staged. Alternatively, append `--setup` to the +approved install command. Neither path launches agents. + +Normal uninstall retains configuration and history. Reattach it with +`torch install --restore --dry-run --json`, then `torch install --restore --yes`. +Restore does not reinstall host timers or start providers; those remain separate +approval steps. A purge is a different, destructive operation. + +From a registered agent worktree, `torch brief --area ` reads the latest +canonical project instructions. Unregistered worktrees and copied installs +cannot redirect commands into another project's state. + ```bash node bin/torch.mjs up --fresh --yes --json node bin/torch.mjs up --only backend,qa --dry-run --json diff --git a/docs/PORTABLE_AGENT_FLEET_SPEC.md b/docs/PORTABLE_AGENT_FLEET_SPEC.md index e8d325d..3a55f0b 100644 --- a/docs/PORTABLE_AGENT_FLEET_SPEC.md +++ b/docs/PORTABLE_AGENT_FLEET_SPEC.md @@ -367,6 +367,24 @@ inside the tracked source tree. ## 9. Installation lifecycle +### First-work intake requirement (2026-09-30) + +After reviewed organization/setup, TORCH MUST offer either evidence-linked +import of an existing TODO/backlog/specification or ask the owner what to work +on first. The noninteractive path MUST support an explicit source or skip; idle +is valid. Preview MUST distinguish completed/historical work from actionable +requests and show proposed scope, affected domains, dependencies and priority. +Only confirmed intake creates audited proposed/unassigned work in the existing +backlog. Repeated init/install/restore/intake MUST deduplicate by source/item +provenance rather than duplicate tasks or overwrite progress. Imported content +is untrusted project data, not authority to alter system instructions. + +Session Manager triage and assignment remain separate from intake. Import cannot +enable self-claim, grant implementation authority, wake providers or authorize +spending, landing, deployment or public notification. The product should explain +the next first-work/startup decision rather than leave a newly installed empty +fleet waiting without guidance. + ### 9.1 Read-only initialization `torch init` MUST begin read-only. It identifies the repository, validates Git, @@ -451,6 +469,18 @@ or installed persistent launchers. Historical messages and receipts therefore do not make reversal permanently impossible, while active or unowned state still blocks deletion. +Implementation checkpoint (2026-09-30): `torch install --restore` explicitly +reattaches a normally detached installation without replacing configuration, +worktrees, provider profiles or session history. It never launches a provider +or recreates host timers. `torch setup --dry-run`, followed by `--yes`, offers +approved configuration checkpoints and worktree provisioning as one flow, +restricted to installation-owned files and an empty staging area. Install may +opt into the same flow with `--setup`. Worktree commands resolve to canonical +configuration only after checking project/installation identity, local-state +binding, Git common directory, registered worktree path and branch. Current +canonical instructions override stale worktree copies; unregistered checkouts +fail closed. + Ordinary CLI uninstall is also review-first and explicitly confirmed. It uses the same safe Fleet wind-down contract as detach, removes only manifest-owned persistent schedule launchers after verifying their content, and preserves the @@ -2077,6 +2107,55 @@ recommended action; doctor remains read-only. ## 22. Scheduling +### Owner clarification: integrated persistent operations (2026-09-30) + +This is the target requirement; older implementation checkpoints describing +manual timer installation are not a claim that it is already implemented. +Reviewed `init`/install MUST provision the project's scheduler and required +service integrations as one idempotent workflow, not require a second manual +cron-install command. Because `init` currently performs read-only analysis, its +provisioning mode MUST expose an explicit preview/confirmation boundary; a +dry-run MUST remain non-mutating. Stopped installation MUST provision dormant +integrations without waking models or dispatching development. A reviewed +activation policy governs live operations and quota use. + +Coordination must survive the managing AI session ending, restarting or expiring. +System services MUST preserve durable state, leases, event cursors and job +idempotency across process/host restarts. Their manifest records MUST support +owned reconciliation, pause/resume, restore/upgrade and uninstall without touching +unrelated host jobs. Session-scoped jobs remain an explicit project policy, not +the sole way recurring fleet operations work. + +Project intake MUST propose applicable capabilities, with clear implemented, +unconfigured and unsupported states: + +- direct-report manager check-ins, structured approval/peer/owner waits, + stale owner-request review and an owner-first local digest; +- a persistent, serialized landing worker using approved exact-commit checks; +- resource-queue watchdogs, audited slot withdrawal and displaced-runner stop; +- durable significant events and deduplicated budgeted manager wakes, with + low-frequency timed safety checks rather than perpetual paid polling; +- a project-declared nightly canonical suite, heavy-check windows and + evidence-backed regression routing, not guessed blame; +- measured test durations, uncertain wait estimates and project-specific + exclusive resources such as a GPU; +- conservative provider-neutral usage limits, graceful wind-down and explicitly + approved credit-window or calendar restarts that never override manual pause; +- idle, clean, unguarded worktree convergence by merge, preserving conflicts, + unique commits and active work; +- exact landed task closure, verified reversal/follow-up policy and reviewed + evidence for finished-but-unnamed work, never automatic closure by message alone; +- project-specific local dev/Console services and separately authorized delivery, + live-version checks and external notifications where relevant. + +No universal project inherits COMBATRIG's release times, Monday reset time, +two GPU slots, Vercel destination, phone notifier or LAN/HTTPS exposure. Setup +MUST show lifetime, timezone/DST behavior, missed-run policy, costs/budgets, +ports/destinations, health evidence, stop semantics and owner authority. Unsupported +hosts/adapters need an actionable limitation or qualified fallback, not a fictitious +"installed" status. No provider wake, deploy, public message, off-machine write +or cleanup of unowned processes follows solely from creating a schedule. + TORCH recognizes two schedule classes. **System schedules** survive AI sessions and may perform approved release, @@ -2236,6 +2315,38 @@ request but MUST NOT approve it for a different named approver. The owner and each AI identity may list only requests they made or are named to approve; manager check-in may inspect requests made by its direct reports. +### Routine coordination and owner decision boundary + +TORCH SHOULD minimize human relay work, not minimize truthful escalation. Within +explicitly approved project/task policy, specialists read and acknowledge their +own addressed messages, resolve peer interface questions, decide only approvals +for which they are the named responsible approver, and maintain truthful task, +wait, evidence and local owned-work checkpoints. Managers route unresolved peer +requests, follow their direct-report waits and compress outcomes for the owner. +Reading/acknowledging a message MUST NOT close its unresolved approval or task. + +Before escalating to the owner, inspect the current named approver, responsible +domain, applicable policy, prior decision and evidence. An authorized routine +action SHOULD be completed and audited instead of asking the human to relay or +repeat the instruction. An unauthorized action remains blocked with a specific +request to the responsible party. Owner requests contain evidence, options, +recommendation and consequences, not only internal error codes. + +Project policy MUST distinguish routine authorized actions, investigation-only +signals and genuine owner decisions. Health warnings, stale presence, branch +drift, budget exhaustion, incomplete checks or missing evidence do not grant +permission to recover, delete, merge, start another runtime or waive a gate. +Spending/publication/deployment, material scope or ownership changes, identity +activation, destructive or uncertain recovery and owner-addressed approvals +remain subject to their explicit owner authorization. A paused fleet and its +budgets remain enforced even when a manager believes action would be useful. + +Current pilot checkpoint: canonical live COMMON/manager instructions now teach +this boundary. `TASK-bounded-routine-coordination` carries portable generated +instruction and behavioral qualification work; the owner authorizes this bounded +Project Kernel slice, not general unattended execution. Automatic AI wakes remain +paused pending executor qualification. + Implementation checkpoint (2026-09-28): `planManagerCheckIn` derives direct reports from the active organization graph and reports offline/stale/waiting identities plus unacknowledged direct-report messages. `queueManagerCheckIn` diff --git a/docs/TEST_INTEGRITY.md b/docs/TEST_INTEGRITY.md index bfbab8a..0e08fbd 100644 --- a/docs/TEST_INTEGRITY.md +++ b/docs/TEST_INTEGRITY.md @@ -2268,3 +2268,56 @@ test_integrity_note: did_relax_any_assertion: false if_true_explain_spec_basis: "" ``` +# Setup and canonical worktree briefs (2026-09-30) + +```yaml +test_integrity_note: + change_type: new_tests + scenarios: + - id: SCN-guided-setup + given: "An approved installed project with uncommitted TORCH files" + when: "Owner reviews and authorizes setup" + then: "Only owned files are committed; registered worktrees exist; no providers start; staged user work blocks setup" + - id: SCN-worktree-canonical-brief + given: "A registered worktree has older instructions" + when: "Its installation root is resolved and a brief requested" + then: "Canonical instructions appear; unregistered worktrees, copied installs and stale installation IDs fail closed" + - id: SCN-install-restore + given: "A normally detached project retains configuration and worktree commits" + when: "Owner explicitly restores the install" + then: "Binding is reattached without changing profiles, worktree history or launching providers" + observable_outcomes: + - "Git commits, clean canonical checkout, worktree registrations, brief text and local detach metadata" + determinism_controls: + - "Isolated temporary Git repositories and XDG state; no provider or network calls" + anti_cheat_rationale: + prevents: + - "Hard-coded success without Git worktrees or commits" + - "Accepting copied or unregistered installation state" + - "Staging unrelated owner work" + relaxation: + 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/docs/TODO.md b/docs/TODO.md index 7ec4258..c8ffd17 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,5 +1,115 @@ # TORCH remaining work +## Authoritative current handoff (2026-09-30, after alpha.2 reinstall) + +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. 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 +"uncommitted/no launcher" statements; those are superseded by this handoff. + +The conversation's unfinished work is now owner-proposed in the **existing +authoritative backlog in the Session Manager worktree**, not a second TODO queue. +Use `torch backlog list --json` to see it from any registered checkout. Tasks use +the `TASK-conversation-` prefix, have acceptance criteria, affected domains and +dependencies, and remain unassigned for manager triage. See +`reports/audit/conversation-handoff-2026-09-30.md` for the domain review and index. + +### New owner requirements: first work and automatic operations + +- [ ] `TASK-conversation-first-work-intake`: init/install/setup offers reviewed + TODO/backlog import or asks what the owner wants to begin with; previews and + confirms proposed tasks; deduplicates resumes; never activates stale/completed + TODOs or launches agents silently. +- [ ] `TASK-conversation-persistent-operations`: reviewed init/install provisions + machine-level scheduler/services automatically, not merely schedule JSON or a + separate manual cron-install command. Paused installs remain dormant. Services + declare system/session lifetimes, budgets, timezone, restart/missed-run policy, + pause/resume and manifest-owned uninstall/restore behavior. This extends the + earlier `TASK-conversation-automatic-scheduler` requirement. +- [ ] `TASK-conversation-event-driven-wakes`: durable deduplicated significant + events wake the appropriate manager under budget; periodic checks are the quiet + safety net, not repeated usage-consuming busywork. +- [ ] `TASK-conversation-queue-watchdog`: detect stuck resource queues and + disappearing slots, audit removals and stop displaced waiters; never infer + browser ownership/termination from a process name or runner death. +- [ ] `TASK-conversation-nightly-main-tests`: project-declared full canonical + suite on an exact frozen commit, resource-aware idle window and evidence-backed + regression routing. Do not guess the offending change. +- [ ] `TASK-conversation-usage-governor`: provider-neutral usage/credit evidence, + conservative fallback budgets, graceful wind-down and reviewed reset/restart + policy that cannot override manual pause or detach. +- [ ] `TASK-conversation-idle-worktree-refresh`: scheduled merges into clean, + idle, unguarded branches; skip active/dirty/unknown work and preserve conflicts. +- [ ] `TASK-conversation-queue-planning`: measured test durations, uncertain wait + estimates, slowdown visibility, GPU-exclusive resources and approved heavy-run + windows; preserve the resource queue's authority. +- [ ] `TASK-conversation-closure-revert-review`: verified revert/follow-up policy + and evidence-backed review of unnamed finished work; never mass-close or reopen + tasks based only on untrusted commit trailers. +- [ ] `TASK-conversation-project-services`: reviewed project-specific landing + worker, local dev/Console server, local summary and optional release jobs. + No COMBATRIG schedules, Vercel deployment, credentials, LAN exposure or automatic + phone notifications are assumed. + +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. +- [ ] `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. +- [ ] `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 44 owner-intake items, including agent chat, the UI audit, observed +attention/startup follow-ups and bounded routine-coordination policy. + +- [ ] `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. +- [ ] `TASK-console-attention-actions`: meaningful condition/agent/branch labels, + deduplicated findings and contextual quick actions. Owner approve/reject uses + existing evidence/revision/preview confirmation; never clear peer messages, + merge branches or publish to a remote through a generic warning button. +- [ ] `TASK-pilot-brief-sandbox`: the first UI audit stopped at a read-only SQLite + error while the exact installed brief succeeds on the host. Qualify scoped + sandbox access and actionable failure; do not disable sandboxing, change DB + permissions, copy state or bypass the authoritative startup brief. +- [ ] `TASK-bounded-routine-coordination`: live COMMON/manager policy now keeps + own-inbox handling, peer questions, named approver decisions and owned ledger + maintenance inside the fleet. Project Kernel's approved implementation slice + makes this portable in generated instructions and proves authority/wait/pause + boundaries with additive scenarios. No general unattended execution is implied. + ## First real self-host run (2026-09-30) ### Portable production pipelines (new roadmap element) @@ -37,7 +147,7 @@ updates, retained previous installs, offline previews and fail-closed errors. Four deterministic scenarios added; live download qualification recorded in the first-run audit, not implied by mocked installer tests. -- [ ] Fix `torch brief` invoked from a managed worktree: both live sessions hit +- [x] Fix `torch brief` invoked from a managed worktree: both live sessions hit `LOCAL_STATE_METADATA_MISMATCH`. Resolve canonical project binding with checked worktree membership, never weaken root/installation identity checks. - [ ] Surface Codex JSON stdout failure events: first Sol launch hid the actual @@ -45,7 +155,7 @@ - [ ] Add exact executable/version/model/auth preflight to owner Console and setup flow. PATH selected CLI 0.158.0 while the current session used 0.159.2; the older child rejected Sol 6.1 and required manual investigation/retry. -- [ ] Make configuration checkpoint/worktree/manifest handoffs explicit or +- [x] Make configuration checkpoint/worktree/manifest handoffs explicit or guided: generated tracked files and provisioning manifest updates require deliberate commits; a healthy doctor does not prove startup readiness. - [ ] Isolate irrelevant inherited global MCP servers without removing user @@ -349,7 +459,7 @@ in `reports/audit/self-host-readiness-2026-09-30.md`. No agent or timer was star the collapsed panel; observation never releases a launch lock. - [ ] Qualify interrupted-wake recovery against live runtime lifecycle evidence; owner attestation is explicit but is not independent proof of termination. -- [ ] Qualify the updated source as a clean commit before self-host activation; +- [x] Qualify the updated source as a clean commit before self-host activation; older artifact receipts predate these changes. No fleet or timer has started. Review verification (2026-09-30): full regression suite through isolated @@ -961,7 +1071,7 @@ COMBATRIG's proposed hierarchy as the default. multi-domain coordination/resume scenario with a fixed provider budget. - [ ] Install and reverse the user-systemd scheduler, verifying one real dispatch and its receipts. -- [ ] Install an accepted TORCH version in user-local versioned state and use +- [x] Install an accepted TORCH version in user-local versioned state and use its stable launcher to manage this source checkout. - [ ] Resolve COMBATRIG's 56 ownership/exclusion blockers, then separately authorize an import/cutover trial. Do not mutate COMBATRIG beforehand. diff --git a/package-lock.json b/package-lock.json index b4afe31..13acd7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "torch-agent-fleet", - "version": "0.1.0-alpha.1", + "version": "0.1.0-alpha.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "torch-agent-fleet", - "version": "0.1.0-alpha.1", + "version": "0.1.0-alpha.2", "bundleDependencies": [ "@modelcontextprotocol/server", "typebox", diff --git a/package.json b/package.json index f2b1ad6..b3352f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "torch-agent-fleet", - "version": "0.1.0-alpha.1", + "version": "0.1.0-alpha.2", "type": "module", "description": "TORCH — a portable, provider-independent AI development fleet for Git repositories", "main": "src/cli.mjs", diff --git a/reports/audit/conversation-handoff-2026-09-30.md b/reports/audit/conversation-handoff-2026-09-30.md new file mode 100644 index 0000000..d3755ad --- /dev/null +++ b/reports/audit/conversation-handoff-2026-09-30.md @@ -0,0 +1,90 @@ +# TORCH conversation-to-fleet handoff + +## Installed domains + +The installed organization is appropriately scoped to this repository: one +Session Manager and six specialists. A current read-only ownership query over +129 tracked files under `src`, `bin`, `site`, `schemas`, `scripts` and `test` +found zero unowned files and zero duplicate primary owners. This claim does not +cover every historical report or future new directory. + +| Domain | Responsibility | Effective Codex model | +| --- | --- | --- | +| Session Manager | Routing, ownership, priority and fleet health | GPT-6.1 Sol high | +| Project Kernel | Bootstrap, identity, canonical instructions and organization | GPT-5.6 Terra high | +| Provider Runtime | Harnesses, MCP, lifecycle, schedules and telemetry | GPT-5.6 Terra high | +| Work and Integration | Backlog, checks, resources, landing and recovery | GPT-5.6 Terra high | +| Owner Console | Dashboard, artifacts, feedback and observability | GPT-6 Luna high | +| Release and Self-host | CLI, package, candidates, upgrades and removal | GPT-6 Luna high | +| Independent QA | Scenario integrity and real qualification | GPT-5.6 Terra high | + +The six-role grouping was a reviewed consolidation of repository evidence, +not a copy of COMBATRIG's specialists and not proof of a fully autonomous AI +Architect first-run. Proposed extra domain leads are inactive. Headcount alone +does not justify adding managers. Significant pipeline work may justify different +model effort or a new domain later, through the existing review path. + +## Authoritative backlog + +Owner-request intake created 34 proposed, unassigned items in the existing +Session Manager worktree backlog. There are 33 current follow-ups plus the +earlier narrower `automatic-scheduler` request, retained for manager triage +against the newer `persistent-operations` requirement rather than silently +deleted. All have acceptance criteria, affected domains and observed commit +binding; dependent slices reference existing task IDs. No assignment, provider +start, self-claim enabling or host service installation occurred. + +Run `torch backlog list --json` from any registered checkout. The canonical +backlog service resolves the manager's tracked ledger; do not create a second +copy in the source checkout. Task IDs below are prefixed `TASK-conversation-`. + +| Group | Task suffixes | +| --- | --- | +| First work / readiness | `first-work-intake`, `development-activation`, `selfhost-development-trial` | +| Persistent operations | `persistent-operations`, `automatic-scheduler`, `event-driven-wakes`, `queue-watchdog`, `nightly-main-tests`, `usage-governor`, `idle-worktree-refresh`, `queue-planning`, `project-services` | +| Provider reliability | `runtime-diagnostics`, `startup-preflight`, `mcp-isolation`, `update-startpaths`, `mixed-provider-qualification` | +| Pipelines | `pipeline-definitions`, `pipeline-provenance`, `pipeline-flow`, `pipeline-stalls`, `pipeline-console`, `pipeline-metrics`, `pipeline-pilot` | +| Live qualification | `hierarchy-pilot`, `scheduler-qualification`, `browser-portability`, `landing-closure`, `closure-revert-review`, `delivery-qualification` | +| Owner/product/release | `owner-digest`, `console-workflow`, `documentation-current`, `release-qualification` | + +## What is not yet automatic + +Setup and retained-state restore have been qualified; installed alpha.2 is +healthy. All agents remain offline. The current manager schedule declares a +15-minute interval, but no TORCH-owned systemd units are installed or active. +Read-only host timer enumeration confirmed no TORCH timer; unrelated Archive +timers were untouched. The owner's latest request is that init/install provision +these integrations automatically. That is now an explicit target in specification +section 22 and the backlog, not an already delivered feature. + +The shared common prompt still authorizes only an installation smoke test and +the project self-claim policy is disabled. Starting agents under it does not +authorize development. The manager should read and triage proposed work, recommend +a first bounded slice and budget, and obtain the development/startup approval +before delegation. New installs should offer reviewed TODO import or ask the +owner what they want first; repeated intake must deduplicate rather than queue +every historical checkbox. + +## Suggested first implementation sequence + +1. First-work intake and persistent operations planning/provisioning: make + the product's installation genuinely complete while stopped until ready. +2. Runtime diagnostics and exact launch preflight: make startup failures legible. +3. Separately approved bounded self-host development, landing/closure and + scheduler qualification, followed by persistent event/watchdog/nightly work. +4. Pipeline definitions, immutable provenance, checked transitions and Console. +5. Finish mixed-provider, organization-growth and release qualification. + +There is no authorization here to promote the rewrite to default `main`, deploy +the public website, send notifications, alter COMBATRIG, spend on asset generation +or install active recurring AI wakes. Those boundaries remain in the task criteria. + +## Validation + +Installed `torch backlog list` sees all 34 items. Acceptance criteria and domains +are populated; every dependency exists and the dependency graph is acyclic. +Installed `torch backlog health` reports healthy with no anomalous assignments. +Its bounded historical activity coverage remains incomplete, correctly labelled +unknown rather than proof of inactivity. Existing backlog/scheduling regression +suite: 23/23 PASS. All seven identities remain offline. These are backlog/spec +changes, not implementation of the newly requested onboarding or automation. 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..e496594 --- /dev/null +++ b/reports/audit/first-development-pilot-2026-09-30.md @@ -0,0 +1,238 @@ +# 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. + +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. +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. + +## 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. + +## Attention-panel triage and audit recovery + +Session Manager assigned `TASK-console-ux-audit` to Owner Console at revision 3. +The first audit turn **did not perform browser coverage**: installed `torch brief` +failed with `ERR_SQLITE_ERROR` / read-only database. The identical installed brief +passes from the host. The arbiter resumed the same session with directions to +request narrowly scoped escalation for that exact command, not change permissions +or skip the guard. `TASK-pilot-brief-sandbox` captures durable qualification. + +Owner additionally requests contextual quick actions in Needs your attention. +`TASK-console-attention-actions` requires meaningful per-agent/branch findings, +deduplicated messages and owner-only reviewed approve/reject actions. Total +owner intake is now 43. It does not authorize warning dismissal, blanket message +acknowledgement, automatic merging or remote publication. + +Observed worktree warnings: manager backlog updates are dirty, and manager and +Provider Runtime retain unique commits. These are preserved task state and +unlanded work, not permission to remove worktrees or discard commits. The manager +has a narrowly authorized local backlog checkpoint/convergence request and must +review its own inbox. Provider code still requires integration gates. + +Recovery is `ONE-DISK` for current rewrite HEAD: origin exists, but local +remote-tracking references do not contain that commit. This is not proof of a +live remote query. Off-machine synchronization requires an explicit owner +publication/backup decision; no remote push or default-main promotion occurred. + +## Bounded coordination and reliability follow-through + +The owner clarified that ordinary peer coordination should not require human +relay. Live COMMON and Session Manager instructions now require own-inbox review, +named-approver decisions, durable unresolved waits and direct responsible-peer +follow-up. Spending, publication, releases, scope/ownership changes, destructive +recovery and owner-addressed approvals remain reserved. There are 44 backlog +items; `TASK-bounded-routine-coordination` carries this policy into fresh installs. + +Project Kernel committed candidate `d3a679849b0f5e55c2114223a2e56977c318391e`. +The arbiter independently ran its new routine-coordination test: **2/2 PASS**. +This covers generated instruction surfaces, acknowledgement not completing work, +and named-approver enforcement. It does not independently prove paused runtime +execution or full integration qualification. QA has a review request for that +coverage gap. Kernel reports test/check/lint evidence, but its dashboard gate is +blocked: root Playwright is 1.58.2, Kernel's installed dependency is 1.63.0, and +the latter expects absent `chromium_headless_shell-1243`. QA and manager were +asked to resolve reproducible setup, not waive assertions or use an unverified +browser substitution. + +Session Manager explicitly blocked the earlier runtime diagnostics task while +preserving its accepted candidate and incomplete dashboard receipt. It then +assigned `TASK-pilot-executor-outcomes` to Provider Runtime and +`TASK-pilot-generated-check-artifacts` to Work and Integration, one active item +per specialist. Named QA additive-test/output-plumbing consent and Release CLI +consent were requested through the control plane. The arbiter started their +review turns and both assigned implementation turns using separate owned +transient user services. A running service proves an active launch handle, not +task completion or successful native terminal output. + +All three named peer consents subsequently reached **approved revision 2**: +QA `e198e23b-772f-42d5-89df-5c4891df904f` and +`649f7e47-8b94-4b02-a1e6-962fde3e24a7`, and Release +`200dcdba-9337-4e6f-a218-d02c7bda458a`. The responsible peers sent their bounded +decisions directly to specialists and manager. Actual native Codex processes +were verified in the Provider Runtime and Work and Integration worktrees; +the Console subsequently showed both as working. These are successful peer +coordination outcomes, not completed implementation or independent acceptance. + +Owner Console is continuing its actual browser audit through a bounded temporary +diagnostic launcher. The launcher captures outcome metadata only, checks terminal +turn evidence and refuses false success. Its larger output buffer is diagnostic +recovery, **not an installed engine fix**. The permanent executor task must handle +timeouts, signals, errors, output overflow and uncertain captured identities. +Automatic wakes remain paused; no remote push, release, installation update or +default-main promotion has occurred. + +## Independent follow-up: completion evidence and review gaps + +The first temporary Console probe completed with status 0, no executor error or +signal, 64 valid protocol records and a final `turn.completed` event. It captured +141,317 stdout bytes. This proves that diagnostic turn completed, **not that its +audit task completed**; the audit addendum still records missing computer-use +coverage. A fresh arbiter browser connection and fresh tab subsequently produced +an actually viewed single-viewport Overview capture in about four seconds. The +same Console identity was resumed with that recovery evidence, without duplicate +launch or a claim that the earlier interruption was caused by output overflow. + +QA independently confirmed Kernel's focused 2/2 pass, but rejected full policy +acceptance because paused execution was only text-matched and manual test reports +were not durable TORCH check receipts. Kernel was resumed for an additive real +paused-dispatch boundary scenario. Both tracked lockfiles pin Playwright 1.58.2; +Kernel's 1.63.0 installation is an environment mismatch, not an intended upgrade. + +Work and Integration created candidate +`f46c713cc9e90697d02e3e29222b3eeb7cd71d99`. Arbiter independently ran its two new +artifact scenarios: **2/2 PASS**, covering sequential external hashed outputs +and an exit-zero source mutation remaining incomplete. Full dashboard, +convergence and independent acceptance remain outstanding. + +An early review of Provider Runtime's uncommitted executor draft found remaining +workflow gaps: status-zero success without terminal evidence, reducing scheduled +output capacity rather than safely handling screenshot-rich turns, discarding +initial runtime identity on large output, and insufficient public-boundary tests. +Specific evidence was routed to Provider Runtime, QA and Session Manager. This +is in-progress review feedback, not an accepted fix. The manager was resumed to +follow direct-report waits and task/evidence state; automatic wakes remain paused. diff --git a/reports/audit/setup-reinstall-2026-09-30.md b/reports/audit/setup-reinstall-2026-09-30.md new file mode 100644 index 0000000..b96e5f8 --- /dev/null +++ b/reports/audit/setup-reinstall-2026-09-30.md @@ -0,0 +1,59 @@ +# Guided setup and reinstall verification + +## Changes + +- `torch brief` and normal installed-project CLI commands resolve registered + worktrees to the canonical project. The resolver checks project and installation + IDs, XDG state binding, Git common directory, worktree membership and branch. + Current canonical instructions win over an old worktree copy. +- `torch setup --dry-run`, then `--yes`, checkpoints owned configuration, + provisions worktrees and checkpoints registration. Existing staged work is + rejected; unrelated files are not staged. Install can opt in with `--setup`. +- `torch install --restore` reattaches the retained state from normal uninstall. + It does not overwrite profiles, erase history, recreate timers or start agents. +- The self-host smoke brief distinguishes manager backlog inspection from + specialist next-task lookup and explicitly requires current briefs. + +## Evidence before installation + +All 39 source acceptance groups, lint and syntax: PASS. +Receipt: `/tmp/torch-alpha2-source-acceptance.json`. +New scenarios: guided setup, canonical worktree briefs, safe detach/restore. +The actual Owner Console worktree returned its current brief successfully +through the patched source CLI, including its preserved offline runtime ID. + +## Installation trial + +Accepted alpha.2 source: `c11754ab18fa690355ead3f6f87b3accb2975153`. +Candidate digest: `d4b6ba38a51cc1804047b175c963806850f017a92d88863d187043314414dd90`. +All 39 acceptance groups passed again from the copied candidate. Active version +is `0.1.0-alpha.2`; rollback retains alpha.1. + +Normal uninstall: PASS. Retained-state restore: PASS. All seven actual agent +worktrees returned briefs through `/home/user/.local/bin/torch`: PASS. Their +briefs include the updated canonical smoke instructions despite older worktree +copies. Model configuration hash, project/installation IDs, offline states, +saved Codex runtime IDs and all seven branch tips were unchanged across +uninstall/restore. No provider was launched. + +The restored project's `torch setup --yes --json` was a no-op: no commits, +no mutations, no sessions started. Final doctor reports healthy. Modified owned +configuration and unique manager history remain legitimate safety warnings, +not authorization to purge. Uninstall refreshed the manager resume brief; +that generated file was separately checkpointed on its existing branch, without +rewriting prior history or landing anything. + +Receipts: + +- `/tmp/torch-alpha2-candidate-build.json` +- `/tmp/torch-alpha2-uninstall-plan.json` +- `/tmp/torch-alpha2-uninstall-result.json` +- `/tmp/torch-alpha2-restore-result.json` +- `/tmp/torch-alpha2-reinstall-verification.json` +- `/tmp/torch-alpha2-real-setup-result.json` +- `/tmp/torch-alpha2-real-doctor.json` + +This tests retained-state reinstall, not destructive purge or a fresh installation +with discarded configuration. New-install setup is covered by isolated real-Git +regression tests. Provider-authentication and new agent launches were deliberately +not repeated. Host timers remain uninstalled. diff --git a/reports/design-system/live-console-arbiter-audit-2026-09-30.md b/reports/design-system/live-console-arbiter-audit-2026-09-30.md new file mode 100644 index 0000000..417a239 --- /dev/null +++ b/reports/design-system/live-console-arbiter-audit-2026-09-30.md @@ -0,0 +1,145 @@ +# Live Console arbiter usability audit + +## Evidence and scope + +Owner requested direct inspection of `http://127.0.0.1:4174/console` and a report +to Session Manager. Inspected the real installed workspace in the connected Brave +browser using interactive navigation, DOM observations and actually viewed +viewport screenshots. No live task, approval, profile, release or message was +mutated through the browser. Expanded the request composer and one incomplete +check receipt only. Screenshots were viewed in this audit conversation; no new +artifact catalog or saved screenshot files are claimed. + +Visited all ten navigation entries: Overview, Briefing, Flow watch, Work, +Progress, Fleet, Conversations, Organization, Evidence and Release gates. +Temporary narrow viewport was reset afterward. Desktop DOM viewport was +1253×685. Requested mobile override 390×844 produced reported CSS viewport +433×938 in this browser; responsive measurements below use the reported values. +No full keyboard audit, draft/refresh experiment, approval submission, empty/error +simulation or hidden Operations panel coverage is claimed. Those remain the +assigned UI agent's coverage requirements. + +## Prioritized findings + +### P1: one document makes monitoring impractical + +Desktop document height measured 50,288 pixels. Work occupied 36,662 pixels, +Conversations 5,723 and Organization 2,931 at the observation point. Navigation +changes URL fragments and scroll position rather than opening focused views. +The masthead/project/freshness controls scroll away on lower sections. Current +nav styling exists, but every navigation link lacked `aria-current`. + +Use addressable separate screens with persistent project context. Keep Overview +to a concise pulse, real decisions, blockers and recent outcomes. Route detail +links to exact task, agent, check or decision rather than a broad section. + +### P1: work-card density dominates the page + +There were 43 active tasks, including 41 Queue cards. Narrow desktop kanban +columns wrap IDs and titles heavily and show full long descriptions. Filtering, +saved-view editing and task creation sit above the actual work. At narrow size, +the filter block alone measured approximately 572 CSS pixels, followed by a +horizontally navigated kanban. + +Use compact task summaries with a detail drawer/page. Separate proposed backlog +from active work by default; retain one authoritative task system. Put secondary +filters/saved-view editing behind an intentional control. Mobile should offer a +usable list or selected-column view, not require navigating every column. + +### P1: attention is opaque and duplicates the same condition + +Actual Overview repeated MESSAGE_BACKLOG plus the unacknowledged message count, +and three WORKTREE_PROBLEM entries all said only “Doctor reported a project +condition to review.” Unique commits and manager backlog edits are not equivalent +to a failed merge or missing tree. The Healthy badge can imply more operational +readiness than these conditions justify; the existing doctor threshold may be +valid, but the UI label should describe its narrower meaning. + +Implement `TASK-console-attention-actions`: group and deduplicate actual entities, +name identity/branch/reason, distinguish safety gates from routine agent progress. +Offer owner Approve/Reject only for an actual pending owner-addressed approval, +using current revision/evidence preview-confirm checks. Offer exact inspect, +message/review or supported recovery actions otherwise. No blanket dismiss, +peer-message acknowledgement, merge, deletion or publication shortcut. + +### P1: conversations is an internal transcript, not owner chat + +Two-column entries expose entire long coordination instructions. There is no +agent-thread picker, compact subject/task grouping or focused reply experience. +The expanded installed request composer displayed “Install a reviewed Fleet +before sending owner requests” while identity options were populated and Preview +request was present. This contradicts the installed workspace state. Existing +request composition is not a native owner reply channel or automatic wake. + +Implement `TASK-owner-agent-chat` with truthful per-agent threading, delivery and +reply states. Keep engineering message detail expandable; show concise relevant +summaries first. Fix stale placeholder status from actual capability state. + +### P2: fleet, ownership and organization repeat technical metadata + +Fleet repeats runtime/model/profile text and mixes implementation paths with +session monitoring. Organization cards repeat peer links and large owned-path +lists; the QA role shows an extensive historical report-file inventory. These +are useful audit details but poor default monitoring content. + +Use a compact agent table/list showing work, state, last evidence, blocker and +chat/open actions. Put ownership paths and authority into agent detail. Use an +actual hierarchy/relationships view for Organization with a details inspector. +Do not replace observed presence with inferred process liveness. + +### P2: briefing and progress do not provide a quick owner outcome + +Briefing was unpublished and instructed the owner to run digest build/publish CLI +commands. Progress showed feature/milestone group totals but did not provide an +obvious exact-task drilldown. Preserve its good distinction between task progress +and verification/release readiness. + +Provide permission-aware local generate/review/publish flow when supported and +link progress groups to filtered work. Do not send notifications or publish a +release as a side effect of preparing a local briefing. + +### P2: evidence is honest but recovery navigation is weak + +Expanded test-dashboard receipt correctly showed incomplete, +worktree-changed-during-check, exact SHA and missing frozen/condition evidence. +This is valuable and must remain strict. Artifacts are not connected yet; +integration/delivery records are empty and correctly avoid implying deployment. +Flow watch still said timer installation not verified despite the earlier +host-qualified timer; review how operational evidence reaches this snapshot, +rather than merely removing the warning. + +Add links from an incomplete check to its task, author, candidate and blocking +condition. Show the reviewed next step without turning “retry” into an automatic +repeat of uncertain effects. Place receipts and advanced operational detail on +the relevant screen, not below every unrelated workflow. + +## Proposed information architecture + +- Overview: concise project outcome, decisions, blockers, recent events. +- Work: active work; compact proposed backlog; filters and exact task detail. +- Agents: state/work/waits, per-agent chat and profile/ownership detail. +- Pipelines: conditional project-defined flows, deliverables and stage history. +- Evidence: checks/artifacts with exact task/commit links and review actions. +- Releases: integration, delivery and separately authorized recovery. +- Organization / Settings: hierarchy, ownership, operations and configuration. + +Briefing may be accessible from Overview; advanced operational panels should have +their own destination. Retain dark mode and original TORCH mark. Avoid filling +every view with duplicate cards or decorative labels. + +## Implementation acceptance recommendations + +1. A navigation action displays only its focused workspace, preserves project + identity/freshness, and supports addressable reload/back/forward behavior. +2. Forty-plus proposed tasks do not make Overview or agent conversations taller; + compact cards and bounded/paginated detail preserve all underlying records. +3. Attention is entity-specific and deduplicated, with authority-aware actions + and unchanged stale-preview/revision/evidence safety checks. +4. Installed request status derives from current capability, not leftover loading + copy. Per-agent threads remain readable after reload and provider changes. +5. Narrow/mobile monitoring reaches work and actions without a full-screen wall + of filters; current navigation is accessible via focus and `aria-current`. +6. Independent browser scenarios verify navigation, refresh/drafts, mobile, + pending approvals, wrong approver, incomplete checks and failed launches. + +This report is audit evidence and a proposal, not implementation qualification. diff --git a/src/cli.mjs b/src/cli.mjs index 361ea7d..8c8cc6b 100644 --- a/src/cli.mjs +++ b/src/cli.mjs @@ -24,6 +24,8 @@ import { diagnoseProject } from './kernel/doctor.mjs'; import { loadProjectConfig, validateProjectConfig } from './kernel/config.mjs'; import { asErrorRecord, TorchError } from './kernel/errors.mjs'; import { inspectRepository } from './kernel/git.mjs'; +import { resolveInstalledProjectRoot } from './kernel/project-root.mjs'; +import { setupProject, restoreProject } from './kernel/setup.mjs'; import { inspectSpecifications } from './kernel/specifications.mjs'; import { installProject, planInstall, uninstallProject } from './kernel/install.mjs'; import { IntegrationService } from './integration/service.mjs'; @@ -72,6 +74,8 @@ Usage: torch domains [--repo ] [--spec ] [--output ] [--json] torch install --proposal [--runtime ] [--default-runtime ] [--update-runtimes] [--dry-run] [--yes] [--json] torch worktrees [--parent ] [--dry-run] [--yes] [--json] + torch install --restore [--dry-run] [--yes] [--json] + torch setup [--parent ] [--dry-run] [--yes] [--json] torch up [--fresh] [--only ] [--dry-run] [--yes] [--json] torch down [--dry-run] [--yes] [--json] torch capture [--json] @@ -414,7 +418,7 @@ export const CANDIDATE_ACCEPTANCE_EVIDENCE = Object.freeze({ 'forge-migration': ['SCN-forge-migration'], 'delivery-lifecycle': ['SCN-delivery-lifecycle', 'SCN-delivery-attempts', 'SCN-delivery-retry-boundaries', 'SCN-delivery-succeeded-recovery', 'SCN-owner-digest', 'SCN-owner-digest-schedule', 'SCN-owner-digest-receipts'], - 'review-install-roster': ['SCN-cli-domain-review', 'SCN-cli-install-provider-default', 'SCN-install-provider-inheritance'], + 'review-install-roster': ['SCN-cli-domain-review', 'SCN-cli-install-provider-default', 'SCN-install-provider-inheritance', 'SCN-guided-setup'], 'branches-worktrees': ['SCN-worktree-bootstrap'], 'runtime-identities': ['SCN-mixed-runtime'], 'runtime-profiles': ['SCN-runtime-profiles', 'SCN-runtime-cost-ceiling', 'SCN-runtime-profile-cli'], @@ -471,7 +475,7 @@ export const CANDIDATE_ACCEPTANCE_EVIDENCE = Object.freeze({ 'capture-stop-resume': [ 'SCN-fleet-fresh-resume', 'SCN-hierarchy-aware-start-stop', 'SCN-hierarchy-startup-cycle', ], - 'detach-uninstall': ['SCN-install-doctor-purge'], + 'detach-uninstall': ['SCN-install-doctor-purge', 'SCN-install-restore', 'SCN-worktree-canonical-brief'], 'combatrig-compatibility': ['SCN-combatrig-import', 'SCN-cli-combatrig-import'], 'product-surface': ['SCN-product-site', 'SCN-dashboard-demo', 'SCN-console-readonly', 'SCN-console-lifecycle-blockers', 'SCN-console-owner-digest', 'SCN-demo-owner-digest', 'SCN-console-check-evidence', 'SCN-console-task-activity', 'SCN-console-operation-outcomes', 'SCN-console-live-refresh'], @@ -796,7 +800,9 @@ export async function runCli(argv = process.argv.slice(2), { } const designCommand = ['init', 'analyze', 'domains', 'design', 'bootstrap'].includes(command); const repositoryRoot = designCommand ? resolve(cwd, optionValue(argv, '--repo') ?? '.') : cwd; - const repository = inspectRepository(repositoryRoot); + const inspectedRepository = inspectRepository(repositoryRoot); + const repository = designCommand ? inspectedRepository + : inspectRepository(resolveInstalledProjectRoot(inspectedRepository.root, env)); if (command === 'profile') { const operation = argv[1] ?? 'show'; const areaId = optionValue(argv, '--area'); @@ -997,6 +1003,11 @@ export async function runCli(argv = process.argv.slice(2), { return 0; } if (command === 'install') { + if (argv.includes('--restore')) { + const result = restoreProject({ repository, env, dryRun: argv.includes('--dry-run'), authorized: argv.includes('--yes') }); + print(result, { json }); + return 0; + } const proposalPath = optionValue(argv, '--proposal'); const runtimeOption = optionValue(argv, '--runtime'); const defaultRuntime = optionValue(argv, '--default-runtime'); @@ -1038,7 +1049,11 @@ export async function runCli(argv = process.argv.slice(2), { } persistProfileConfig(repository.root, config); } - print(installed, { json }); + print(argv.includes('--setup') ? { ...installed, setup: setupProject({ repository: inspectRepository(repository.root), env, authorized: true, parentOverride: optionValue(argv, '--parent') }) } : installed, { json }); + return 0; + } + if (command === 'setup') { + print(setupProject({ repository, env, authorized: argv.includes('--yes'), dryRun: argv.includes('--dry-run'), parentOverride: optionValue(argv, '--parent') }), { json }); return 0; } if (command === 'doctor') { diff --git a/src/kernel/project-root.mjs b/src/kernel/project-root.mjs new file mode 100644 index 0000000..eeea65b --- /dev/null +++ b/src/kernel/project-root.mjs @@ -0,0 +1,38 @@ +import { execFileSync } from 'node:child_process'; +import { existsSync, readFileSync, realpathSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { projectStatePath } from './paths.mjs'; +import { TorchError } from './errors.mjs'; + +const read = path => JSON.parse(readFileSync(path, 'utf8')); +const git = (root, args) => execFileSync('git', ['-C', root, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); + +// A worktree's copied configuration is not authoritative. Only registered, +// same-installation worktrees may resolve to the current canonical definition. +export function resolveInstalledProjectRoot(root, env = process.env) { + root = realpathSync(root); + const path = join(root, '.torch', 'install-manifest.json'); + if (!existsSync(path)) return root; + const manifest = read(path); + const stateRoot = projectStatePath(manifest.projectId, env); + const local = manifest.external?.find(entry => entry.type === 'local-state'); + if (!local || resolve(local.path) !== resolve(stateRoot)) { + throw new TorchError('Installation state path does not match this project', { code: 'LOCAL_STATE_PATH_MISMATCH' }); + } + const metadata = read(join(stateRoot, 'project.json')); + if (metadata.projectId !== manifest.projectId) { + throw new TorchError('Installation metadata belongs to another project', { code: 'LOCAL_STATE_METADATA_MISMATCH' }); + } + const canonical = realpathSync(metadata.root); + if (canonical === root) return root; + const current = read(join(canonical, '.torch', 'install-manifest.json')); + const registered = current.external?.find(entry => entry.type === 'worktree' && existsSync(entry.path) && realpathSync(entry.path) === root); + const common = directory => realpathSync(resolve(directory, git(directory, ['rev-parse', '--git-common-dir']))); + const members = git(canonical, ['worktree', 'list', '--porcelain']).split('\n').filter(line => line.startsWith('worktree ')).map(line => realpathSync(line.slice(9))); + if (current.projectId !== manifest.projectId || current.installationId !== manifest.installationId + || !registered || common(root) !== common(canonical) || !members.includes(root) + || git(root, ['branch', '--show-current']) !== registered.branch) { + throw new TorchError('This checkout is not a registered worktree of the installation', { code: 'UNREGISTERED_PROJECT_WORKTREE' }); + } + return canonical; +} diff --git a/src/kernel/setup.mjs b/src/kernel/setup.mjs new file mode 100644 index 0000000..b926a56 --- /dev/null +++ b/src/kernel/setup.mjs @@ -0,0 +1,55 @@ +import { execFileSync } from 'node:child_process'; +import { readFileSync, renameSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { inspectRepository } from './git.mjs'; +import { readInstallManifest } from './install.mjs'; +import { projectStatePath } from './paths.mjs'; +import { resolveInstalledProjectRoot } from './project-root.mjs'; +import { createWorktrees, planWorktrees } from './worktrees.mjs'; +import { TorchError } from './errors.mjs'; + +const git = (root, args) => execFileSync('git', ['-C', root, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); + +export function setupProject({ repository, env = process.env, authorized = false, dryRun = false, parentOverride }) { + const root = resolveInstalledProjectRoot(repository.root, env); + repository = inspectRepository(root); + const manifest = readInstallManifest(root); + const owned = [...new Set([...manifest.created.map(record => record.path), '.torch/install-manifest.json'])]; + const changed = owned.filter(path => git(root, ['status', '--porcelain=v1', '--', path])); + const staged = git(root, ['diff', '--cached', '--name-only']); + if (staged) throw new TorchError('Setup requires an empty staging area; preserve and finish your existing staged work first', { code: 'SETUP_STAGED_WORK', details: staged }); + const unrelated = repository.dirtyEntries.filter(entry => !owned.includes(entry.slice(3))); + const worktrees = planWorktrees({ repository: { ...repository, dirtyEntries: unrelated }, parentOverride }); + const plan = { action: 'setup', projectId: manifest.projectId, checkpointPaths: changed, worktrees, canProceed: worktrees.canProceed, sessionsStarted: false, mutationPerformed: false }; + if (dryRun) return plan; + if (!authorized) throw new TorchError('Setup commits TORCH-owned configuration and provisions worktrees; review --dry-run then use --yes', { code: 'APPROVAL_REQUIRED' }); + if (!plan.canProceed) throw new TorchError('Setup has blockers', { code: 'SETUP_BLOCKED', details: worktrees.conflicts }); + const checkpoint = (paths, message) => { + const pending = paths.filter(path => git(root, ['status', '--porcelain=v1', '--', path])); + if (!pending.length) return null; + git(root, ['add', '--', ...pending]); + git(root, ['commit', '--only', '-m', message, '--', ...pending]); + return git(root, ['rev-parse', 'HEAD']); + }; + const configurationCommit = checkpoint(changed, 'Checkpoint approved TORCH configuration'); + const provisioned = createWorktrees({ repository: inspectRepository(root), parentOverride }); + const registrationCommit = checkpoint(['.torch/install-manifest.json'], 'Checkpoint TORCH worktree registration'); + return { ...plan, configurationCommit, registrationCommit, provisioned, mutationPerformed: Boolean(configurationCommit || registrationCommit || provisioned.mutationPerformed) }; +} + +export function restoreProject({ repository, env = process.env, authorized = false, dryRun = false }) { + const root = resolveInstalledProjectRoot(repository.root, env); + const manifest = readInstallManifest(root); + const path = join(projectStatePath(manifest.projectId, env), 'project.json'); + const metadata = JSON.parse(readFileSync(path, 'utf8')); + const result = { action: 'restore-installation', projectId: manifest.projectId, installationId: manifest.installationId, wasDetached: Boolean(metadata.detachedAt), preserved: ['configuration', 'worktrees', 'branches', 'session history', 'provider profiles'], sessionsStarted: false, mutationPerformed: false }; + if (dryRun) return result; + if (!authorized) throw new TorchError('Restoring a retained installation requires --yes', { code: 'APPROVAL_REQUIRED' }); + if (metadata.detachedAt) { + const temporary = `${path}.restore-${process.pid}`; + writeFileSync(temporary, `${JSON.stringify({ ...metadata, detachedAt: null, attachedAt: new Date().toISOString() }, null, 2)}\n`, { flag: 'wx', mode: 0o600 }); + renameSync(temporary, path); + result.mutationPerformed = true; + } + return result; +} 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'; diff --git a/test/fleet/setup.test.mjs b/test/fleet/setup.test.mjs new file mode 100644 index 0000000..b051f36 --- /dev/null +++ b/test/fleet/setup.test.mjs @@ -0,0 +1,101 @@ +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { mkdtempSync, readFileSync, writeFileSync, cpSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import test from 'node:test'; +import { analyzeRepository } from '../../src/kernel/analyze.mjs'; +import { proposeDomains } from '../../src/kernel/domains.mjs'; +import { inspectRepository } from '../../src/kernel/git.mjs'; +import { installProject, readInstallManifest } from '../../src/kernel/install.mjs'; +import { resolveInstalledProjectRoot } from '../../src/kernel/project-root.mjs'; +import { setupProject, restoreProject } from '../../src/kernel/setup.mjs'; +import { openControlPlane } from '../../src/control-plane/service.mjs'; +import { createFleetBrief, detachFleet, planFleetDetach } from '../../src/runtime/lifecycle.mjs'; + +const git = (root, args) => execFileSync('git', ['-C', root, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); +function fixture() { + const root = mkdtempSync(join(tmpdir(), 'torch-setup-')); + const env = { ...process.env, XDG_DATA_HOME: join(root, 'external-state') }; + git(root, ['init', '-b', 'main']); + git(root, ['config', 'user.name', 'Setup Test']); + git(root, ['config', 'user.email', 'test@example.invalid']); + writeFileSync(join(root, '.gitignore'), 'external-state/\nworktrees/\n'); + writeFileSync(join(root, 'app.js'), 'export const app = true;\n'); + git(root, ['add', '.']); + git(root, ['commit', '-m', 'fixture']); + const repository = inspectRepository(root); + const proposal = proposeDomains({ repository, analysis: analyzeRepository(repository) }); + proposal.review = { status: 'approved', reviewedAt: '2026-09-30T00:00:00Z', reviewedBy: 'owner', notes: [] }; + installProject({ repository, proposal, env }); + return { root, env, parentOverride: join(root, 'worktrees') }; +} + +test('SCN-guided-setup: approved setup checkpoints owned files and provisions without agents', () => { + const f = fixture(); + const before = git(f.root, ['rev-parse', 'HEAD']); + assert.equal(setupProject({ ...f, repository: inspectRepository(f.root), dryRun: true }).canProceed, true); + assert.equal(git(f.root, ['rev-parse', 'HEAD']), before); + assert.throws(() => setupProject({ ...f, repository: inspectRepository(f.root) }), { code: 'APPROVAL_REQUIRED' }); + writeFileSync(join(f.root, 'app.js'), 'export const app = false;\n'); + git(f.root, ['add', 'app.js']); + assert.throws(() => setupProject({ ...f, repository: inspectRepository(f.root), authorized: true }), { code: 'SETUP_STAGED_WORK' }); + assert.equal(git(f.root, ['diff', '--cached', '--name-only']), 'app.js'); + git(f.root, ['restore', '--staged', 'app.js']); + writeFileSync(join(f.root, 'app.js'), 'export const app = true;\n'); + const result = setupProject({ ...f, repository: inspectRepository(f.root), authorized: true }); + assert.equal(result.sessionsStarted, false); + assert.ok(result.configurationCommit); + assert.ok(result.registrationCommit); + assert.equal(git(f.root, ['status', '--porcelain']), ''); + assert.ok(git(f.root, ['diff', '--name-only', before, 'HEAD']).split('\n').every(path => path.startsWith('.torch/'))); + assert.ok(readInstallManifest(f.root).external.some(entry => entry.type === 'worktree')); + assert.equal(setupProject({ ...f, repository: inspectRepository(f.root), authorized: true }).mutationPerformed, false); +}); + +test('SCN-worktree-canonical-brief: current instructions win and unregistered or copied worktrees fail closed', () => { + const f = fixture(); + setupProject({ ...f, repository: inspectRepository(f.root), authorized: true }); + const entry = readInstallManifest(f.root).external.find(item => item.type === 'worktree'); + const marker = 'Current canonical instructions, not the old worktree copy.'; + writeFileSync(join(f.root, '.torch/prompts/COMMON.md'), marker); + const canonical = resolveInstalledProjectRoot(entry.path, f.env); + assert.equal(canonical, f.root); + const control = openControlPlane({ repositoryRoot: canonical, env: f.env }); + try { + const brief = createFleetBrief({ repositoryRoot: canonical, controlPlane: control, areaId: entry.area }); + assert.ok(brief.areas[0].prompt.includes(marker)); + } finally { control.close(); } + const stray = join(f.root, 'worktrees', 'stray'); + git(f.root, ['worktree', 'add', '-b', 'stray', stray]); + assert.throws(() => resolveInstalledProjectRoot(stray, f.env), { code: 'UNREGISTERED_PROJECT_WORKTREE' }); + const copied = mkdtempSync(join(tmpdir(), 'torch-copy-')); + cpSync(join(entry.path, '.torch'), join(copied, '.torch'), { recursive: true }); + git(copied, ['init', '-b', entry.branch]); + assert.throws(() => resolveInstalledProjectRoot(copied, f.env), { code: 'UNREGISTERED_PROJECT_WORKTREE' }); + const localManifest = JSON.parse(readFileSync(join(entry.path, '.torch/install-manifest.json'))); + localManifest.installationId = 'stale-installation'; + writeFileSync(join(entry.path, '.torch/install-manifest.json'), JSON.stringify(localManifest)); + assert.throws(() => resolveInstalledProjectRoot(entry.path, f.env), { code: 'UNREGISTERED_PROJECT_WORKTREE' }); +}); + +test('SCN-install-restore: detach and explicit restore preserve configuration and worktree history without starting sessions', () => { + const f = fixture(); + setupProject({ ...f, repository: inspectRepository(f.root), authorized: true }); + const manifest = readInstallManifest(f.root); + const metadataPath = join(manifest.external.find(entry => entry.type === 'local-state').path, 'project.json'); + const config = readFileSync(join(f.root, '.torch/torch.yaml'), 'utf8'); + const heads = manifest.external.filter(entry => entry.type === 'worktree').map(entry => git(entry.path, ['rev-parse', 'HEAD'])); + const control = openControlPlane({ repositoryRoot: f.root, env: f.env }); + try { + detachFleet({ plan: planFleetDetach({ repositoryRoot: f.root, controlPlane: control, adapters: {} }), controlPlane: control, stopRuntime: () => { throw new Error('Offline setup must not call a provider'); } }); + } finally { control.close(); } + const before = readFileSync(metadataPath, 'utf8'); + assert.equal(restoreProject({ repository: inspectRepository(f.root), env: f.env, dryRun: true }).wasDetached, true); + assert.equal(readFileSync(metadataPath, 'utf8'), before); + assert.throws(() => restoreProject({ repository: inspectRepository(f.root), env: f.env }), { code: 'APPROVAL_REQUIRED' }); + assert.equal(restoreProject({ repository: inspectRepository(f.root), env: f.env, authorized: true }).sessionsStarted, false); + assert.equal(JSON.parse(readFileSync(metadataPath)).detachedAt, null); + assert.equal(readFileSync(join(f.root, '.torch/torch.yaml'), 'utf8'), config); + assert.deepEqual(manifest.external.filter(entry => entry.type === 'worktree').map(entry => git(entry.path, ['rev-parse', 'HEAD'])), heads); +}); diff --git a/torch-release.json b/torch-release.json index 00b9ad6..98595d5 100644 --- a/torch-release.json +++ b/torch-release.json @@ -1,6 +1,6 @@ { "schema": "torch.dev/release/v1alpha1", - "version": "0.1.0-alpha.1", + "version": "0.1.0-alpha.2", "state": { "reads": ["torch.dev/state/v1alpha1"], "writes": "torch.dev/state/v1alpha1",