voyage/commands/trekcontinue.md
Kjell Tore Guttormsen 2a35d067d9
chore(cleanup): R-b R3 — profiles, phase signals and the effort dialog removed, one fixed path per command
The four profiles, the resolver, the profile validator and docs/profiles.md go;
/trekbrief Phase 3.5 and the brief's phase_signals checks go (an older brief's
key is ignored); MANIFEST_PROFILE_DRIFT and profile_used go. Every Profile,
Composition rule, Sequencing gate and High-effort section leaves the commands.
Decision 209: /trekresearch runs the full swarm with contrarian-researcher as
wave 2 every run; /trekreview and the coordinator run all four passes with no
high-effort normalization. Voyage agents are spawned with no model (their own
model: opus applies); the two built-in spawns pass model "opus". --profile
stays in the parser as a retired flag that swallows its value. §Model & effort
moves to docs/architecture.md; the sequencing-gate eval case goes.

Files removed: 44 (43 from the R3 list + tests/commands/trekreview.test.mjs,
whose every test pinned removed text). Files edited: 43.
Tests: 2467 (2466 pass / 0 fail / 1 skipped) before, 2309 (2309 pass / 0 fail /
0 skipped) after. Durable check: all three groups green, none pending.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 21:12:57 +01:00

282 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: trekcontinue
description: Resume the next session in a multi-session trekplan project. Reads .session-state.local.json and immediately begins the next session.
argument-hint: "[<project-dir> | --help]"
---
# Ultracontinue Local v1.0
Zero-friction multi-session resumption. In a fresh Claude Code session, type
`/trekcontinue` — the command reads the per-project state file
(`.claude/projects/<project>/.session-state.local.json`), shows a 3-line summary,
and immediately begins executing the next session.
The state file is the contract. Any session-end mechanism may write it
(`/trekexecute` Phase 8 / Phase 2.55 / Phase 4, the
`/trekendsession` helper, or — in the future — `graceful-handoff`).
This command only reads.
Pipeline position:
```
/trekplan → plan.md
/trekexecute → progress.json + .session-state.local.json
... session boundary, fresh chat ...
/trekcontinue → reads .session-state.local.json, starts next session
```
See **Handover 7** in `docs/HANDOVER-CONTRACTS.md` for the full schema.
## Phase 0 — `--help` handling
Parse `$ARGUMENTS` with the shared arg-parser. Run first, exactly like this:
```bash
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekcontinue --stdin <<'VOYAGE_ARGS_EOF_7F3C9E'
$ARGUMENTS
VOYAGE_ARGS_EOF_7F3C9E
```
The arguments reach the parser as the one line inside the quoted heredoc, on
stdin, never as shell words. It prints the parse result as JSON. On exit 1,
print its `errors` and stop. When it prints no JSON, say that the parser did
not run and stop.
Read `flags` and `positional` from the JSON it prints. Dispatch the usage
block ONLY when one of these two conditions equals exactly true (no substring
search, no "contains" check):
- `flags['--help'] === true`, OR
- `positional[0] === '-h'` (single-dash short form — the parser keeps it as
positional because the schema does not declare an alias).
In every other case — including when `$ARGUMENTS` is empty, whitespace-only,
the literal empty string `""`, or a positional project-dir — fall through to
Phase 1. Do NOT print the usage block on empty args.
```
/trekcontinue — Resume the next session in a multi-session trekplan project.
Usage:
/trekcontinue # auto-discover state file under cwd
/trekcontinue <project-dir> # explicit project directory
/trekcontinue --cleanup <project-dir> # dry-run: list stale files
/trekcontinue --cleanup --confirm <project-dir> # actually delete (requires status: completed)
/trekcontinue --help # this message
Reads .claude/projects/<project>/.session-state.local.json (per-project,
gitignored). On a valid resumable state, prints a 3-line summary and begins
executing the next session immediately. No interactive confirmation prompt.
State-file schema (v1):
schema_version 1
project string
next_session_brief_path string (validator soft-checks file existence)
next_session_label string
status in_progress | partial | failed | stopped | completed
(completed → no further sessions to resume)
updated_at ISO-8601 timestamp
(unknown top-level keys are tolerated — forward-compat for graceful-handoff v2.2)
Typical flow:
/trekbrief # produces brief.md
/trekplan --project ... # produces plan.md
/trekexecute --project .. # writes session-state on session-end
... (fresh Claude chat) ...
/trekcontinue # reads session-state, runs next session
```
## Phase 0.5 — Cleanup mode dispatch
After the parser has printed its parse of `$ARGUMENTS`, check the parsed `flags`
object directly (NOT a string contains-check on raw `$ARGUMENTS` — that
substring pattern was the root cause of Bug 1).
If `flags['--cleanup'] === true`, switch into the terminal cleanup
flow and do NOT proceed to Phase 1 or any later phase.
**Required positional:** an explicit `<project-dir>` (`positional[0]`).
There is no "clean all" mode — accidental wholesale deletion would be
irreversible. If `positional[0]` is missing, empty, or starts with `-`,
print this usage block to stderr and exit non-zero:
```
Error: /trekcontinue --cleanup requires <project-dir>.
Usage:
/trekcontinue --cleanup <project-dir> # dry-run: list stale files
/trekcontinue --cleanup --confirm <project-dir> # actually delete (status: completed)
```
**Compute mode from parsed flags:**
```
dryRun = (flags['--confirm'] !== true)
confirm = (flags['--confirm'] === true)
```
**Invoke cleanup inline.** Emit the concrete project-dir path as a literal
token in the Bash command — never a template placeholder — same
anti-substitution rule as Phase 2:
```
node --input-type=module -e "import {cleanupProject} from '${CLAUDE_PLUGIN_ROOT}/lib/util/cleanup.mjs'; const [, dir, mode] = process.argv; const r = cleanupProject(dir, {dryRun: mode !== 'confirm', confirm: mode === 'confirm'}); console.log(JSON.stringify(r, null, 2)); process.exit(r.valid ? 0 : 1)" '<RESOLVED-PROJECT-DIR>' '<MODE>'
```
Substitute `<RESOLVED-PROJECT-DIR>` with the literal `positional[0]`
value you have in your working context, and `<MODE>` with either the
literal string `dryrun` or the literal string `confirm` based on the
booleans above. The validator emits a `{valid, errors, warnings, parsed}`
JSON record. Print it to stdout. Exit with the validator's exit code.
**Cleanup is a terminal mode.** It must not fall through to Phase 1/2/3/4.
Operators who want to resume after cleanup must invoke `/trekcontinue`
again without `--cleanup`.
## Phase 1 — Resolve project directory
The parsed `positional[0]` from Phase 0 is the explicit project-dir argument,
when present. Otherwise (empty `$ARGUMENTS` or whitespace-only) auto-discover.
### Step 1.a — Reject `.md` positional argument (SC-2)
If `positional[0]` is non-empty AND ends in `.md`, the user almost certainly
pasted a `NEXT-SESSION-PROMPT.local.md` path instead of a project directory.
Print the following diagnostic to stderr and exit non-zero. Do NOT proceed.
```
Error: expected <project-dir>, got a markdown file path: <positional[0]>
Did you mean to paste the file path as a project directory?
Usage: /trekcontinue <project-dir>
```
### Step 1.b — Auto-discover candidates
When no explicit project-dir was given, enumerate
`.claude/projects/*/.session-state.local.json` paths with `node -e`
(NOT shell glob — harness-mode safety) and emit each as one JSON line of
`{"path": ..., "updated_at": ...}` so Phase 1 can sort numerically:
```bash
!`node -e "const fs=require('fs'),path=require('path');const root='.claude/projects';if(!fs.existsSync(root))process.exit(0);for(const d of fs.readdirSync(root)){const p=path.join(root,d,'.session-state.local.json');if(!fs.existsSync(p))continue;let u='';try{u=(JSON.parse(fs.readFileSync(p,'utf8'))||{}).updated_at||''}catch(_){};process.stdout.write(JSON.stringify({path:p,updated_at:u})+'\\n');}"`
```
Sort the emitted candidates by `Date.parse(updated_at)` descending (newest
first) — numeric comparison, NOT lexicographic string compare. The newest
resumable state wins.
### Step 1.c — Decision tree
- **0 candidates and no explicit arg:** print SC-2 cold-start message and exit:
```
No active multi-session project here.
Start with /trekbrief or /trekplan.
```
- **1 candidate (or explicit non-`.md` arg):** continue to Phase 2 with that path.
- **>1 candidates and no explicit arg:** with the Date.parse sort applied, the
newest resumable state wins automatically and the command continues to Phase 2
with that path. (Operators who want a different candidate re-invoke as
`/trekcontinue <project-dir>`.)
## Phase 1.5 — Frontmatter consistency check
Bug 3 contract: producers (`/trekexecute`, `/trekendsession`)
write `NEXT-SESSION-PROMPT.local.md` with YAML frontmatter (`produced_by:`,
`produced_at:`). Multiple producers may have written candidates in different
locations; this phase settles which one counts before validating the state file.
After resolving the project directory and state-file path, look for two
`NEXT-SESSION-PROMPT.local.md` candidates:
a. `NEXT-SESSION-PROMPT.local.md` at the repo root (`git rev-parse --show-toplevel`) —
operator-managed master file, written by the operator or an external producer, never by Voyage
b. `<project-dir>/NEXT-SESSION-PROMPT.local.md` — producer-written sibling
**If both exist:** use the project-dir file (b), the one Voyage writes; print one line naming the root file (a) as ignored, and continue to Phase 2.
**If only one exists:** continue to Phase 2. No comparison needed.
**If neither exists:** continue to Phase 2. Legacy projects and first-run
flows have no NEXT-SESSION-PROMPT files.
## Phase 2 — Validate the state file
Phase 1 resolved a concrete state-file path. That path is a real string in
your working context — never a template. Phase 2 must read and validate the
state file without any placeholder substitution.
### Step 2.a — Read the file with the Read tool (no Bash)
Use the **Read tool** on the resolved state-file path from Phase 1. Do NOT use
Bash for the read. The Read tool is deterministic and not subject to
shell-substitution errors. Parse the returned JSON body programmatically.
### Step 2.b — Schema-validate the parsed object
Verify the schema by invoking the existing validator CLI shim. Emit the
resolved absolute path as a literal string token in the Bash command — the
exact same string you just passed to the Read tool. The validator accepts
`--json <path>` and prints a `{valid, errors, warnings}` JSON record:
```
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/session-state-validator.mjs --json <RESOLVED-ABSOLUTE-PATH-FROM-PHASE-1>
```
Replace `<RESOLVED-ABSOLUTE-PATH-FROM-PHASE-1>` with the actual path string at
the time you issue the Bash call. There is no template engine; the string is
substituted by you, the model, before the Bash tool sees the command.
**Anti-substitution invariant.** If you ever find yourself about to emit a
literal angle-bracket placeholder, or any other unresolved variable name, to
the Bash tool — STOP. The resolved path is a concrete value you already have
from Phase 1; emit the value, not a placeholder for it.
### Step 2.c — Interpret the result
- **Validator exit code != 0 OR `valid: false` in JSON output:** print the
structured `errors[]` (each `[code] message` on its own line) and exit. Do
not proceed to narration. Suggest running the validator directly for
follow-up: `node ${CLAUDE_PLUGIN_ROOT}/lib/validators/session-state-validator.mjs <path>`.
- **`valid: true` AND any warning has `code: SESSION_STATE_NOT_RESUMABLE`**
(i.e. `status: completed`): print "no further sessions to resume; project
complete" and exit cleanly.
- **`valid: true` AND status is one of `in_progress | partial | failed | stopped`:**
proceed to Phase 3.
## Phase 3 — Narrate 3-line summary
Print this exact template (using values from the validated `parsed` object):
```
Project: {project}
Next session: {next_session_label}
Brief: {next_session_brief_path}
```
No interactive confirmation prompt — per the brief NFR ("ingen prompts, men la
informasjon synes"). The 3-line block is informational only.
## Phase 4 — Begin execution
Read the file at `next_session_brief_path` (it is the brief that the next
session is supposed to execute — typically the same `brief.md` for
single-brief multi-session plans, or a session-specific spec for parallel
session decomposition). Understand the task and begin executing per the
standard trekplan pipeline. The user did not type a separate "start"
command — `/trekcontinue` is the start.
If the brief file does not exist (validator emits a warning but does not
fail), print: `Warning: next_session_brief_path "{path}" does not exist on
disk. Cannot continue automatically.` and exit. Do not guess.
## Hard rules
- **Idempotent.** Running `/trekcontinue` twice in the same Claude session
does not advance state — the writer (Phase 8 / hook / helper) advances state
only when a session completes.
- **Zero secrets in the state file.** Status, paths, labels — never API keys,
never user content beyond filenames.
- **NEVER auto-load via SessionStart.** The command is operator-invoked only.
Auto-loading would re-introduce the stale-file risk noted in
`feedback_next_session_prompt_manual.md`.
- **No interactive prompts.** Phases 0–4 must run without `AskUserQuestion`.
This keeps the command headless-safe.