docs(v0.6): Phase 2 closeout in notes.md
This commit is contained in:
@@ -253,6 +253,59 @@ Theme: **infrastructure foundation for v0.6** — global config file, schema ver
|
||||
- **Validation lives in `ReadMeta`, not in callers.** The schema-version and workspace-mode checks happen at read time — so any path that gets a `*TaskMeta` has already passed validation. Callers do not need to re-check.
|
||||
- **`omitempty` is the load-bearing primitive for "no opportunistic writes."** If a future change needs to track another optional field, follow the same pattern: zero value must round-trip without serialization, and only `workspace.Create` (or an explicit migration) writes a non-zero value.
|
||||
|
||||
### What v0.6 Phase 2 delivered (branch `feat/v0.6-multi-agent-config`, NOT merged)
|
||||
|
||||
Theme: **the multi-agent layer** — ctask is now agent-agnostic. task.yaml carries an agent profile (`type` / `command` / `args` / `env`), `ctask new --agent` selects the agent type, the launch path carries a resolved command + args + env end-to-end, new workspaces get an `AGENTS.md` canonical instruction file (CLAUDE.md becomes a thin shim), and `ctask agents check` validates agent configuration. Six commits on the feature branch; no version bump (lands at end of Phase 3). All Phase 2 spec items (`v0.6-spec.md` §5–§7, plus the §8 seed scaffolding that rides along with the template change) covered.
|
||||
|
||||
#### Commit list (oldest → newest)
|
||||
|
||||
- `8120c39` `feat(v0.6): AgentSpec field on TaskMeta with backward-compat unmarshal`
|
||||
- `TaskMeta.Agent` changes from `string` to `workspace.AgentSpec{Type, Command, Args, Env}`. Custom `UnmarshalYAML` accepts both the v0.6 mapping form and the legacy scalar form. **Legacy scalar handling (corrected at plan review):** a scalar matching a built-in name (`claude`, `opencode`) maps to `AgentSpec{Type: <scalar>}`; any other scalar (a path, `aider`, etc.) maps to `AgentSpec{Type: "custom", Command: <scalar>}`; a missing `agent:` key leaves `Type` empty so the resolver fills in `default_agent`. The scalar value is NEVER dropped — a legacy workspace keeps launching the agent it was created with.
|
||||
- `IsBuiltinAgentType(name)` helper in `internal/workspace` (true for `claude`, `opencode`) — the built-in/custom discriminator used by the unmarshaler. `internal/agent.BuiltinProfiles` mirrors the same set.
|
||||
- `ValidateAgentSpec` (run inside `ReadMeta`): known type only; `type=custom` requires `command`; `command` rejects whitespace and shell metacharacters (`|&;<>()$\``) with a hint pointing at `args` — ctask does not shell-split.
|
||||
- `CreateOpts.Agent string` → `CreateOpts.AgentSpec workspace.AgentSpec`. cmd-layer call sites patched to the minimum needed to compile (full wiring lands in commit 3).
|
||||
- `24f2134` `feat(v0.6): internal/agent package — Resolve + BuiltinProfiles`
|
||||
- New `internal/agent` package: `Profile`, `BuiltinProfiles` (`claude`, `opencode`), `IsKnownType` (those two + `custom`), `Resolved{Type, Command, Args, Env}`, and `Resolve(spec, defaultAgent)`. Resolution: empty `Type` falls through to `defaultAgent`; `custom` requires `command`; otherwise command is `spec.Command` or the built-in default. **No I/O** — PATH lookup stays in `shell.ExecAgent` and `ctask agents check`, so `Resolve` is trivially testable.
|
||||
- `b75b82e` `feat(v0.6): launch path carries ResolvedAgent (command + args + env)`
|
||||
- `LaunchOpts.Agent string` and `WorkspaceEntryOptions.Agent string` → `*agent.Resolved`. All five entry commands (`new`, `resume`, `last`, `open`, `attach`) construct an `AgentSpec`, apply `--agent` as a one-shot `agent.command` override (Open Q 1 — resume/last/attach `--agent` stays a command override, NOT a type selector), call `agent.Resolve`, and pass the result through. `cmd/entry.go::resolveEntryAgent` centralises the resume/last/open/attach path.
|
||||
- `shell.ExecAgent` and `shell.ExecTmuxAgent` gain an `args []string` parameter. `agent.env` is merged into the child environment at the `session.Run` launch switch, AFTER ctask's own `CTASK_*` exports — `agent.env` wins on collision per spec §5. `session.mergeAgentEnv` is the centralised merge; `var execAgent = shell.ExecAgent` is a new test seam.
|
||||
- Lease, manifest, write lock, heartbeat, summary, and provisional cleanup are untouched. The `Agent string` fields on `Lease` / `SessionSummary` / `SessionInfo` still record the resolved command string for diagnostics.
|
||||
- `a61f900` `feat(v0.6): --agent flag on ctask new selects agent type`
|
||||
- `Resolver` gains `cliFlagAgent` + `SetCLIFlagAgent`; `DefaultAgent()` layers `CLIFlag` above `EnvVar` so doctor/info render the full precedence chain. `SettingSource.CLIFlag` (reserved in Phase 1) is now reachable.
|
||||
- `ctask new --agent <type>` writes `agent.type` into the new workspace's task.yaml. Resolution + validation run BEFORE `workspace.Create`, so `--agent custom` with no companion command refuses (`type "custom" requires command`) without leaving a half-created workspace on disk. The deferred Phase 1 test `TestCLIFlagOverridesEnvVar` landed here.
|
||||
- `0c6ed0c` `feat(v0.6): AGENTS.md seed + CLAUDE.md shim + handoff + context-archive scaffold`
|
||||
- New workspaces get `AGENTS.md` (canonical, always — handoff workflow, notes-archive convention with a ~300-500 line trigger, cross-workspace discovery, do-not-touch warnings; project variant adds workspace-structure + git-conventions sections), a `CLAUDE.md` shim (claude type only — opencode shim deferred), `handoff.md` (minimal current-state template), and `context/notes-archive/.gitkeep`.
|
||||
- `seed.ClaudeMD` and `seed.ClaudeMDProject` deleted — no callers remain. `seed.NotesMD` retained. Existing workspaces are NOT retroactively modified (pinned by `TestCreateDoesNotModifyExistingWorkspace`).
|
||||
- `0f96d20` `feat(v0.6): ctask agents check + doctor integration`
|
||||
- `ctask agents check [workspace]` — pure validation, no launch: agent type known, command resolvable on PATH, launch_dir valid, AGENTS.md present, CLAUDE.md shim present (WARN, claude only). `agent.env` keys displayed informationally; a WARN line names any key shadowing a `CTASK_*` export. Non-zero exit on any FAIL.
|
||||
- `ctask doctor` runs the same sweep against the most-recently-active workspace (`workspace.MostRecentActive`); skips with `Agent check: skipped (no workspace context)` when none exists. `runAgentsCheckOnWorkspace` is shared between the standalone command and doctor.
|
||||
- `TestCompletionSubcommandViaExecute` was made order-independent: cobra's default `completion` command captures the root output writer once, on the first `Execute()` in the process — the new agents-check tests run an `Execute()` earlier in the suite, so the test now drops any pre-created completion command before its own `Execute()`.
|
||||
|
||||
#### Verification (run on `feat/v0.6-multi-agent-config` tip `0f96d20`)
|
||||
|
||||
- `go test ./... -count=1` — all 8 packages `ok` (the new `internal/agent` package included), 0 failures.
|
||||
- `go vet ./...` — exit 0.
|
||||
- `just build` — `ctask.exe` builds locally.
|
||||
- `just build-linux` — produces `dist/ctask-linux-amd64`, statically linked ELF.
|
||||
- Version remains `v0.5.4`; bump deferred to the end-of-Phase-3 commit.
|
||||
|
||||
#### Phase 2 constraints held
|
||||
|
||||
- **No PID liveness / lazy-cleanup / adoption changes.** `internal/session/lease.go` untouched; `internal/session/adopt.go` changed only for the mechanical `ResolvedAgent` passthrough. The 60s `StaleLeaseAfter` threshold is unchanged.
|
||||
- **No per-workspace `session_mode`.** No new field on `TaskMeta` beyond the `AgentSpec`.
|
||||
- **No user-defined named agent profiles in config.** `internal/config/configfile.go` is unchanged in Phase 2 — agent profiles live in task.yaml, not config.yaml.
|
||||
- **No opencode-specific shim.** `writeBuiltinDefaults` writes `CLAUDE.md` only when `agent.Type == "claude"`.
|
||||
- **No auto-modification of existing workspaces.** All new seed files are written only by `workspace.Create`.
|
||||
- **No version bump, no merge.** `version.go` untouched; branch stays `feat/v0.6-multi-agent-config`.
|
||||
|
||||
#### Architecture notes (worth preserving)
|
||||
|
||||
- **`internal/agent.Resolve` is I/O-free.** PATH validation is deliberately NOT in `Resolve` — it lives in `shell.ExecAgent` (fail-fast at launch) and `ctask agents check`. This keeps resolution a pure function and lets `agents check` validate without launching.
|
||||
- **`agent.env` precedence is intentional.** It merges AFTER ctask's `CTASK_*` exports and wins on collision (spec §5). This is a feature, not a bug — `agents check` surfaces the shadowing with a WARN so the user is not surprised.
|
||||
- **Two parallel built-in-type lists, kept in sync deliberately.** `workspace.IsBuiltinAgentType` / `workspace.knownAgentTypes` and `internal/agent.BuiltinProfiles` both enumerate the built-ins. `internal/workspace` cannot import `internal/agent` (the dependency runs the other way), so the lists are mirrored, not shared. When a new built-in lands, update all three.
|
||||
- **AGENTS.md is canonical; CLAUDE.md is a shim.** The shim exists only to point Claude Code at AGENTS.md. The seed-overlay rule still applies — a user seed dir's `AGENTS.md` / `CLAUDE.md` overrides the built-in.
|
||||
- **cobra's default `completion` command captures the output writer once.** `InitDefaultCompletionCmd` snapshots `c.OutOrStdout()` into the `bash`/`zsh`/etc. subcommand closures on the first `Execute()` anywhere in the process. Tests that drive `rootCmd.Execute()` with a redirected output buffer and then assert on completion output must drop the pre-created completion command first. `cmd/agents_check_test.go::captureRootCmd` restores `rootCmd`'s out/err/args on cleanup to limit this class of cross-test contamination.
|
||||
|
||||
### Historical: original Phase 1 plan (now shipped — kept for traceability)
|
||||
|
||||
**Phase 1 scope (only thing to start next):**
|
||||
|
||||
Reference in New Issue
Block a user