mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-11-05 12:21:56 +00:00
Merge commit '28ee61bf62c3c99a845c97540570401a47bd6aa3' into torch/session-manager
This commit is contained in:
commit
e758f1bfca
15 files changed
+567
-15
No files matched your search
@@ -6,7 +6,10 @@ Repository state outranks conversation memory. Query ownership before crossing a
|
||||
|
||||
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
|
||||
identity, ownership and inbox. Specialists query their next backlog item;
|
||||
the Session Manager inspects fleet backlog rather than claiming specialist work.
|
||||
Run `torch brief --area <your-area> --json` at startup and before each new item.
|
||||
Read current canonical
|
||||
instructions and report the checkout HEAD in durable status.
|
||||
|
||||
Do not implement code, create or assign development tasks, enable timers,
|
||||
|
||||
@@ -6,6 +6,14 @@ At startup and when recurring cross-domain friction appears, run `torch_assess_h
|
||||
|
||||
## 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. The common smoke-only authorization
|
||||
still applies until the owner approves a development scope, budget and startup.
|
||||
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
|
||||
|
||||
@@ -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 <id>` 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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2237,3 +2237,34 @@ 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: ""
|
||||
```
|
||||
+63
-4
@@ -1,5 +1,64 @@
|
||||
# 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; all providers are offline. 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 setup**. The
|
||||
existing 15-minute manager schedule is configured, but no TORCH host timer is
|
||||
installed. Common instructions still authorize only the smoke test; development
|
||||
activation, self-claim and recurring provider wakes remain separately reviewed.
|
||||
|
||||
## First real self-host run (2026-09-30)
|
||||
|
||||
### Portable production pipelines (new roadmap element)
|
||||
@@ -37,7 +96,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 +104,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 +408,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 +1020,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.
|
||||
|
||||
Generated
+2
-2
@@ -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",
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
+19
-4
@@ -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 <path>] [--spec <path>] [--output <path>] [--json]
|
||||
torch install --proposal <path> [--runtime <adapter,adapter>] [--default-runtime <adapter>] [--update-runtimes] [--dry-run] [--yes] [--json]
|
||||
torch worktrees [--parent <path>] [--dry-run] [--yes] [--json]
|
||||
torch install --restore [--dry-run] [--yes] [--json]
|
||||
torch setup [--parent <path>] [--dry-run] [--yes] [--json]
|
||||
torch up [--fresh] [--only <id,id>] [--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') {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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', '[email protected]']);
|
||||
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);
|
||||
});
|
||||
+1
-1
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user