diff --git a/CLAUDE.md b/CLAUDE.md index 83044dd6..6e0dd517 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -66,14 +66,38 @@ Commit freely (per task + the spec/plan docs). Never push without asking. ## Building & Testing -`dotnet build ClaudeDo.slnx` requires .NET 9; on .NET 8 build individual projects with `-c Release` (a running Worker locks the `Debug` output). +**Build the whole solution, test only what you touched.** Always `-c Release` — a running Worker locks the `Debug` output. ```bash -dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release # pulls in Ui + Data -dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release -dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release # also: Data.Tests, Ui.Tests, Localization.Tests, Installer.Tests, Releases.Tests +dotnet build ClaudeDo.slnx -c Release # ~40s, catches cross-project breaks +dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --no-build \ + --filter "FullyQualifiedName~Handoff" # seconds, not minutes ``` +`.slnx` needs SDK 9+ (this machine is on 10). On an older SDK build the projects individually: +`src/ClaudeDo.App` (pulls in Ui + Data) and `src/ClaudeDo.Worker`. + +### Do NOT run the full test suite to finish a task + +Measured 2026-09-02, sequentially after one build: **Worker.Tests 507s**, Ui.Tests 33s, Data.Tests 8s, +Localization/Installer/Releases 3s each, build 42s — ~10 min for all six. Run in parallel across +projects it is *worse*, not better (Worker.Tests 801s, Ui.Tests 82s): these tests create real git +worktrees and spawn processes, so they thrash rather than share. + +With up to 5 implementer tasks running at once, a full suite per task costs hours — and **cannot find +what it costs that for.** Each task works in its own worktree containing only its own change, so a +collision between two parallel tasks is invisible to both. Collisions surface after the merge, which +is exactly where `verify_merges` runs the list's verify command **once per batch**. The full suite is +the merge gate's job, not the task's. + +So, per task: +- **Build:** the whole solution, always. It's cheap and it catches the compile break your change + caused three projects over. +- **Tests:** `--no-build --filter` scoped to what you touched. Add a filter for the classes covering + your change; if you genuinely can't scope it, run the one relevant test project, never all six. +- **Report** what you ran and what you did not, so the merge session knows what is still unverified. +- Never report "all tests pass" when you ran a filtered subset. Say which filter you used. + ### Gotchas - **Subagents:** use the `sonnet` model; stage files explicitly by path — never `git add -A` (parallel sessions often leave unrelated WIP in the tree). - **Icons:** `PathIcon` *fills* its geometry — line-art icons must be filled geometry or a `Path Classes="icon-stroke"`, else they render invisible. Colour comes from the `ICON PAINTING` block in `IslandStyles.axaml` (class + state); setting `Foreground`/`Stroke` inline kills the hover state. diff --git a/docs/explore-notes/conpty-sessions.md b/docs/explore-notes/conpty-sessions.md index cc500e55..e4251c70 100644 --- a/docs/explore-notes/conpty-sessions.md +++ b/docs/explore-notes/conpty-sessions.md @@ -115,6 +115,22 @@ share the same prompt/model as their non-final counterpart and differ only in on rendered into the handoff kickoff file (`PromptKind.MergeHelperHandoff`) marking the final round and forbidding further reruns. +The chain length is bounded server-side by `HandoffRoundTracker`, whose cap comes from +`AppSettingsEntity.ListHandlerMaxHandoffs` (default 4 = two merge rounds; `HandoffMcpTools` reads it +per call and `Register` clamps to min 1, so 0 can never mean "unbounded"). Past the cap a +`wait`/`merge` request is silently coerced to its `_final` counterpart — check the result's +`coerced` flag — and any handoff after a `merge_final` round throws. + +**PowerUse.** When `AppSettingsEntity.PowerUseEnabled` (toggled on the usage monitor) **and** the +list's own `ListConfigEntity.PowerUseAllowed` are both set, `InteractiveLaunchSpecService. +ComposeHandlerPromptAsync` appends `PromptKind.PowerUseAddendum` to whichever handler prompt the +phase resolved to. The addendum is written as an *override* of the base prompt's interactive rules +(never ask, decide and record instead; resolve or abort conflicts rather than leaving them in the +tree; harvest new tasks when a round would starve). Both keys are required on purpose: the global +switch can stay on overnight without turning an unrelated list's handler run unattended. A run that +is not in PowerUse gets a byte-identical prompt to the pre-PowerUse behaviour — there is deliberately +no second set of handler prompts to keep in sync. + Each handoff hands the SAME handler task id to a NEW tile, and `MissionControlViewModel. OpenMergeHelperHandoffConPtySessionAsync` closes the outgoing phase's pane (`CloseConPtySession`, which disposes its `PtyTerminalSession` and kills the underlying `claude` process) before opening