From e0504dddb0b8ae737000800ef27dd4cf202cd71f Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 22 Jun 2026 14:45:32 +0400 Subject: [PATCH] [ai] Attempt to automate testing changes. --- .agents/shared/test-loop.md | 280 +++++++++++++ .agents/skills/implement/SKILL.md | 218 ++++++++++ .claude/commands/implement.md | 258 ++++++++++++ .claude/commands/planner.md | 122 ------ .claude/commands/withtest.md | 656 ------------------------------ .claude/iterate.ps1 | 343 ---------------- AGENTS.md | 7 + 7 files changed, 763 insertions(+), 1121 deletions(-) create mode 100644 .agents/shared/test-loop.md create mode 100644 .agents/skills/implement/SKILL.md create mode 100644 .claude/commands/implement.md delete mode 100644 .claude/commands/planner.md delete mode 100644 .claude/commands/withtest.md delete mode 100644 .claude/iterate.ps1 diff --git a/.agents/shared/test-loop.md b/.agents/shared/test-loop.md new file mode 100644 index 0000000000..787af67048 --- /dev/null +++ b/.agents/shared/test-loop.md @@ -0,0 +1,280 @@ +# Test Loop Protocol (harness-neutral) + +The portable core of autonomous, tested implementation. Both `/implement` (Claude Code) +and `$implement` (Codex) read and follow THIS file verbatim for the testing phase, so the +impl⇄test loop behaves identically across harnesses. The harness-specific wrappers own +project setup, task splitting, and the spawn/wait mechanics; this file owns everything from +"a single task's implementation is committed" onward. + +## Vocabulary + +- **task-runner** — the per-task agent (one spawn per task). Owns the loop below. Its context + is disposable: only its compact final summary propagates up to the orchestrator. +- **impl agent / impl-fix agent** — sub-agents the task-runner spawns to write or fix the + implementation. They never write test code. +- **test-author agent** — sub-agent that writes the ad-hoc test overlay and builds. +- **overlay** — the throwaway `#ifdef _DEBUG` test code for the current task. Never part of an + implementation commit. Lives as a patch under the task folder between rounds. +- **golden tdata** — a read-only backup of the authed test account. Tests only ever copy FROM + it; they never write to it. + +## Inputs the wrapper passes in + +- `TASK_DIR` — `.ai///` for this task. +- `TASK_ID` — stable id used in commit trailers (e.g. the project + letter). +- **TASK SPEC** — the task's full description block (from `implementing.md`) and its referenced + images (`images/` design mockups / screenshots / graphic resources for this isolated task). + This is half of what the tests are designed against (the diff is the other half); the design READS + the images — they show what the result should look like. +- Config: `BUILD` (build command), `EXE` (built binary path), `MAX_ATTEMPTS` (default 4). The test + account lives in `out/Debug/` as the portable-data folders described under "Test account" below; + the wrapper has already confirmed the golden one exists (launch gate). All paths are relative to + the current checkout — no worktrees are created; the run happens in whatever repository slot it + was launched from. + +## State machine (run by the task-runner) + +Precondition: the implementation for this task is committed in the current checkout (impl agents +commit; they do not stash). Record that commit's SHA as **IMPL_SHA** — the reset after each test run +returns the checkout to exactly it. The runner tracks the attempt number as its own state (`attempt` +starts at 1); the commit message carries no attempt marker. Commits follow "Commit message" below. + +``` +TEST_AUTHOR -> RUN -> ASSESS (adversarial — see "Assessing"): + APPROVED -> reset to the impl commit (drop overlay); return DONE up. Task complete. + TEST_FLAW -> fix the overlay only; back to RUN. Does NOT cost an impl attempt. + IMPL_BUG -> spawn impl-fix agent (input = test.md, latest attempt's Root cause / Fix hint); + it commits a NEW attempt; re-apply overlay (--3way, else re-author); RUN. attempt++ + UNRECOVERABLE -> return BLOCKED up with the reason. Stop. + attempt > MAX -> return BLOCKED up with test.md + "improve" notes. Stop. +``` + +Early-escalation rule: if two consecutive ASSESS rounds produce the **same failure signature** +(same step fails the same way after a fix), stop and return BLOCKED — do not burn the rest of +the attempt budget chasing it. + +UNRECOVERABLE conditions: the app reaches a login screen / `AUTH_KEY_DUPLICATED` and re-copying the +test account does not recover it; a file-lock build error (`LNK1104`, `C1041`) that persists after a +`taskkill`; `test_TelegramForcePortable` missing when SETUP runs; or a crash with no usable +diagnostic after one retry. + +## Handoff tokens + +- **Commit** is the only impl handoff. Impl/impl-fix agents `git add -A && git commit` per "Commit + message" below (and, if submodules changed, commit inside each submodule first, then bump the + superproject pointer in the same logical attempt — real commits, never stash). The runner records + the resulting SHA as that attempt's IMPL_SHA. +- **Result doc** (`result.md`) is the only thing handed back to a fix agent and the only thing + the runner reads to decide. See format below. + +## Commit message + +Impl commits must read like the repository's own history — never marked as autonomous. Match the +style of recent `git log` subjects. +- **Subject:** one concise, plain-language line summarizing the change, ≤ ~50-60 characters. This is + usually the ENTIRE message. +- **Body (rare):** only when the subject can't carry it — a short plain-language note of WHAT was + done (user-facing, not the technical how); a line or two at most. +- **No trailers, ever.** No `Autotask:`/attempt marker; no `Co-Authored-By:` or any tool/assistant + attribution line. This explicitly OVERRIDES any harness default that would append one — a freshly + spawned committing sub-agent may add `Co-Authored-By` unless told not to, so pass this rule to it. + The attempt number is the runner's own state, never part of the message. + +## Test account (portable data) — hard rules + +The debug build runs in portable mode out of `out/Debug/`. Three sibling folders matter: + +- `test_TelegramForcePortable` — the golden test account, prepared by the user. Read-only SOURCE, + never modified by tests. (Its presence is the launch gate; the wrapper aborts if it is missing.) +- `TelegramForcePortable` — the LIVE folder the app actually uses (its presence is what puts the + build in portable mode). Disposable; recreated fresh each run. +- `real_TelegramForcePortable` — the user's real data, preserved once so manual use survives. + +**SETUP — run at the START of every test run, with NO app instance alive. Idempotent: it +guarantees a clean test account no matter how the previous run ended.** +1. If `TelegramForcePortable` exists AND `real_TelegramForcePortable` does NOT, rename + `TelegramForcePortable` -> `real_TelegramForcePortable`. (Captures the user's real data exactly + once; guarded so it is never overwritten afterward.) +2. If `TelegramForcePortable` still exists, delete it. (Safe: `real_...` now holds the real data, so + this only discards a leftover live/test copy.) +3. Copy `test_TelegramForcePortable` -> `TelegramForcePortable`. The live folder is now a fresh copy + of the golden test account — ready to launch. + +**CLEANUP — optional, after a run.** The SETUP steps already self-heal, so cleanup exists only to +leave the user's real data live for manual use: +1. Delete `TelegramForcePortable`. +2. Copy `real_TelegramForcePortable` -> `TelegramForcePortable`. + +Why this is safe: `real_...` is written exactly once (step 1 is guarded by "real does not exist") +and `test_...` is only ever a copy source, so both the user's real data and the golden test account +are structurally protected — only `TelegramForcePortable` is ever destroyed. Use `robocopy /MIR` +(or `Copy-Item -Recurse` / `Remove-Item -Recurse -Force`) for the folder ops. + +**Serialize app runs.** Never have two `Telegram.exe` instances alive against this account at once — +concurrent reuse of one auth key can trigger a server-side session reset. Always `tasklist` and +`taskkill /IM Telegram.exe /F` any stragglers before SETUP, launching, or rebuilding. + +**Avoid destructive calls.** The overlay must never trigger logout / session-termination / +account-deletion. Tests that genuinely need those use a separate burner account, not this one. (If a +permanent destructive-call fuse is later added to the debug build, this is enforced in code; until +then it is the test-author's responsibility.) + +## Design the tests from THIS task (the crux) + +The single most important rule: **tests are derived from what THIS task changed — not from generic +project navigation, and not reused from a previous task.** Different change → different checks. If +two tasks produce the same screenshots and the same assertions, the second test is a no-op. Before +writing any overlay: + +1. **Read both sides of the task.** (a) The TASK SPEC — the task's full description block from + `implementing.md` and its referenced design-mockup images (`images/`); READ the images, + they are the source of truth for what the result should look like. (b) The change under test — + `git show ` (the actual diff) and `/plan.md`. List every concrete thing the + diff changed and every surface the task (description + "Observable result") says it affects. +2. **Turn each into a falsifiable check with an ORACLE** — something that can come out FAIL. A check + with no way to fail is not a test. By change type: + - **String / text** → assert the EXACT expected text is present at runtime (dump the label/widget + text to the log and compare) AND the old text is gone. Not "the screen opened". + - **Visual / asset (icon, image, color, layout)** → the rendered target must MATCH the intended + new artwork and DIFFER from the old. Both are available as files (intended art under + `.ai//...`; the committed new file is `git show :`; the old is + `git show ^:`). Render those references to PNG and compare the tight crop + against both. **If the rendered target matches the OLD art — or you cannot tell them apart — + that is a FAIL, not a pass.** (This is the check that catches a change that never took effect, + e.g. an asset that wasn't rebuilt into the binary.) + - **Behavior** → drive the specific action and observe the concrete state/log/screenshot the + change should produce, and confirm the pre-change behavior no longer happens. +3. **Cover every surface the task names.** If the Observable result lists a settings row, a balance + header, a gift field, and a suggestion bar, each must be observed (or explicitly marked N/A with + a reason). Do not stop at one or two. +4. **Write the checks into `/test.md` BEFORE running** (format under "Test report"), so the + design is explicit and Actual/Result can be filled in per check afterward. + +## Overlay mechanics + +The overlay is ad-hoc, authored fresh against the CURRENT implementation, injected at the +highest level that still exercises the change (often a direct data-layer call like +`item->applyEdition(...)` rather than a faked MTP response). It must: + +- Live entirely inside `#ifdef _DEBUG` blocks. +- Pick a **test strategy** and record it in the spec: + `live-data` (use real account data) · `live-mutate` (really create an entity — prefer a + throwaway target, clean up after) · `inject` (build fake local state without the network) · + `mock-api` (intercept specific requests, return canned responses — for payments/destructive). + Prefer `inject` over `live-mutate` to avoid account/server accumulation and flake. +- Drive the scenario on the Qt event loop, preferring **condition-waits over fixed timers** + (wait until the target widget/data actually exists, with a timeout fallback). Fixed sleeps are + the main source of screenshot flake. +- Write a flushed log to `/test_log.txt` (open Append|Text, flush after each write) and + save screenshots to `/screenshots/`. Delete the old log at the first step. +- **Capture the target tightly.** Grab the specific widget / row / glyph (or crop the saved PNG to + it) so the target is unambiguously in frame at usable resolution. A full-window grab that leaves + the target clipped, off-screen, or thumbnail-sized is NOT acceptable evidence — if the target + isn't clearly captured, that is a TEST_FLAW (re-frame), never a pass. +- **Lay down the oracle's reference.** For an asset/visual check, also save the OLD and intended-NEW + art as PNGs beside the crop (`screenshots/_old.png`, `_new.png`) so the assessment is + a direct three-way comparison, not a memory test. +- Emit these markers, one per line: + `TEST_STEP: ` · `TEST_RESULT: PASS: ` / `TEST_RESULT: FAIL: -
` · + `SCREENSHOT: ` · `TEST_COMPLETE` (immediately before quit). +- Prefer asserting on **logged state** (log the actual value, assert on text — deterministic); + reserve screenshots for genuinely visual checks where an eye is the right judge. +- **Watchdog:** install a `QTimer` at scenario start that force-quits (`Core::Quit()`, and if + needed `std::abort` after a flush) at a hard wall-clock cap (default 120s). This guarantees the + app never hangs holding a lock on the exe — independent of the runner's own timeout. +- End every path (success or assertion failure) by logging `TEST_COMPLETE` then `Core::Quit()`. + +### Git mechanics for the overlay (no stash) + +- After building, save the overlay as a patch: `git diff > /test-overlay.patch`. + Then **reset the checkout back to the implementation commit** so it stays impl-only: + `git reset --hard ` (and `git submodule update --init --recursive` if the overlay + touched submodules). The overlay never enters an impl commit. +- Next round, re-apply on top of the new implementation: `git apply --3way + /test-overlay.patch`. This succeeds ~90% of the time when the tail change was small. +- On conflict, **re-author the conflicting hunk from the spec** in `test.md` (which records + intent: injection point, fake values, assertions) rather than fighting the conflict markers. + Scenario steps that only call public APIs should live in their own block so they never conflict; + only true in-situ injections land inside impl files. + +## Build & run discipline + +- Build with `BUILD`. A single changed TU compiles fast; only the overlay-touched files + link + rebuild between rounds. On `LNK1104`/`C1041`, `taskkill /IM Telegram.exe /F`, wait, retry once; + if it persists -> UNRECOVERABLE. +- **Codegen does not track resource mtimes.** If the task changed only a resource the style codegen + consumes (an icon `.svg`, etc.) without touching a `.style`, an incremental build will NOT re-pack + it and the binary keeps the OLD asset. Before building such a task force regeneration — touch the + referencing `.style` (or clean the codegen output) — so the change actually ships. A render that + shows no difference from before is the symptom of skipping this. +- Run: run the SETUP steps (Test account) -> launch `EXE` in the background -> poll `test_log.txt` + every ~5s -> on each `SCREENSHOT:` read the image and judge it -> detect `TEST_COMPLETE` (success) + or process death (crash) or no new output for the watchdog cap (hang) -> `taskkill` any straggler + -> optional CLEANUP -> save the overlay (`git diff > /test-overlay.patch`) -> THEN + `git reset --hard ` (back to impl-only — the patch must be saved before this reset). + +## Assessing (adversarial) + +ASSESS decides APPROVED / TEST_FLAW / IMPL_BUG. Default to **not approved**; a check passes only on +positive, specific evidence — in the captured pixels or the log — that the change is present AND +correct. + +- **No pass by inference.** "Same asset so it's fine", "probably", "looks like" are not evidence. + Missing, clipped, or ambiguous evidence for a check → **TEST_FLAW**: re-frame/re-capture and run + again. Never turn missing evidence into a PASS. +- **Judge the actual artifact.** State what is literally visible in the crop / present in the log, + then compare to the oracle reference (`_old.png` vs `_new.png`). Do not narrate expectations. +- **Judge visually, never by hash.** Do NOT pixel-diff or hash images. Desktop renders differ from + the mobile (iOS/Android) design mockups by platform, DPI, theme and antialiasing — the mockups + convey the intended look, they are NOT pixel targets, so never fail a check merely for not matching + a mockup pixel-for-pixel. The falsifiable signal is the on-screen crop against the OLD vs + intended-NEW render (does it match the new and differ from the old?); the mockup informs what + "correct" means. Read the images and decide like a designer reviewing the build. +- **No-difference = IMPL_BUG.** If a check detects no difference from the pre-change state (the glyph + matches the OLD art; the string still shows the old word), the change did not take effect — return + IMPL_BUG; do not approve. +- **A visual check with no baseline/target comparison cannot APPROVE** — with no oracle you have + tested nothing. +- APPROVED requires every derived check to PASS with evidence; else IMPL_BUG (real defect) or + TEST_FLAW (the test was wrong, not the code). + +## Test report (`/test.md`) — human-readable, append per attempt + +The file the human opens to see how testing went. The test-author writes the checks (Expected / +Oracle / Observed via) BEFORE running; ASSESS fills Actual / Result and the verdict. Append a new +`## Attempt` section each round — never overwrite prior attempts. + +``` +# Test report — /: + +## Attempt <n> — commit <sha> — strategy <...> — verdict: <APPROVED|TEST_FLAW|IMPL_BUG|UNRECOVERABLE> + +### Test 1 — <aspect of THIS change> +- Expected: <observable effect the change should produce> +- Oracle: <what would make this check FAIL> +- Observed via: <surface + how captured: tight crop of widget X; refs _old/_new> +- Actual: <what is literally visible / logged> +- Screenshots: screenshots/<after>.png (refs: _old.png, _new.png) +- Result: PASS | FAIL + +### Test 2 — ... + +### Verdict reasoning +<1-3 lines tying the checks to the verdict> +### Root cause / Fix hint (only if IMPL_BUG — the impl-fix agent reads this) +### Failure signature (one line, for early-escalation comparison) +``` + +## Compact summary the task-runner returns up + +``` +TASK: <TASK_ID> +STATUS: <DONE|BLOCKED> +VERDICT: <APPROVED|reason if blocked> +ATTEMPTS: <n> +TOUCHED: <repo paths or none> +DISCOVERED: <new follow-up tasks to append to implementing.md, or none> +NOTES: <one or two lines, or none> +``` + +Detailed reasoning stays in `.ai/` artifacts. The chat reply is only this block. diff --git a/.agents/skills/implement/SKILL.md b/.agents/skills/implement/SKILL.md new file mode 100644 index 0000000000..8c19e964dd --- /dev/null +++ b/.agents/skills/implement/SKILL.md @@ -0,0 +1,218 @@ +--- +name: implement +description: Autonomously implement a request on this repository (Telegram Desktop). Accept an inline description or a task-list file, normalize it into a project with a testability-split task list, then drive each task to test-approval through an isolated per-task runner subagent that does context, planning, implementation, build, a single review pass, and an in-app test loop. Use when the user wants one prompt — ideally under Goal run mode — to carry work all the way to a tested, approved state with persistent .ai/ artifacts and a thin parent thread. Reuses task-think's phase prompts and the shared test-loop protocol; prefers spawn_agent/wait_agent and keeps the main thread lean. +--- + +# Implement Pipeline + +The tested superset of `task-think`. The parent thread stays lean: it holds only the task list and +one compact summary per task. Each task is driven to test-approval by an isolated **task-runner** +subagent that spawns its own phase subagents. + +This skill does not re-specify the implementation phases or the test loop. It reuses: +- `.agents/skills/task-think/PROMPTS.md` — Phase 0-6 prompt templates and the Codex execution-mode + / wait-ladder / progress-heartbeat / compact-reply rules. +- `.agents/shared/test-loop.md` — the harness-neutral impl⇄test loop (state machine, commit + handoff, overlay mechanics, `--3way`/re-author, test-account swap, watchdog, escalation). + +Read both before orchestrating. + +## Inputs + +`$ARGUMENTS` = ONE of: an inline task description; a path to a task-list file (e.g. +`.ai/communities/tasks.txt`); or an existing project name to resume, optionally followed by extra +work. May reference attached screenshots. + +## Config + +Runs in the **current checkout** — wherever the skill is invoked. No worktrees are created; paths +below are relative to that repository root. + +``` +BUILD = cmake --build ./out --config Debug --target Telegram +EXE = ./out/Debug/Telegram.exe (or ./out/Debug/Telegram on WSL/Linux — verify the tree) +TEST_ACCOUNT = ./out/Debug/test_TelegramForcePortable (user-prepared golden; launch gate aborts if absent) +MAX_ATTEMPTS = 4 +``` + +Tasks run **sequentially** in this one checkout. App runs must serialize (one client per account). +To parallelize, run the skill in a different checkout/slot (e.g. `C:\Telegram\tdesktop`, +`D:\Telegram\tdesktop`, `D:\Telegram\twin`); give parallel slots separate test accounts so two test +runs never share one auth key. + +## Artifacts (per project) + +- `.ai/<project>/implementing.md` — the canonical, final, testability-split task list (descriptions + + status); the main thread is its only writer. +- `.ai/<project>/images/` — illustrations referenced by tasks. +- `.ai/<project>/<letter>/` — per-task artifacts (context, plan, review, test, result, overlay, logs). +- `.ai/<project>/about.md` — project blueprint (task-think convention). + +## Done (for Goal run mode) + +Done when every task in `implementing.md` has `Status: approved` or `Status: blocked: <reason>`. +Resumable: re-invoking with the project name reads `implementing.md` and continues from the first +unfinished task. + +## Phase A: Setup & input resolution (main thread) + +1. Record `$START_TIME`. +2. **Test-account gate (hard precondition — before any work).** If + `out/Debug/test_TelegramForcePortable` does not exist, STOP the entire skill immediately and tell + the user to prepare it (a portable-data folder authed to a throwaway test account); autonomous + testing is impossible without it. +3. **Resolve `$ARGUMENTS` into (project, SOURCE, mode) — without reading task files or images** + (the main thread never loads task prose or assets; resolving needs only paths and existence + checks). SOURCE ends as EITHER inline text OR a confirmed file path that the planner will read. + - **File input** — first token is a path: confirm it exists (do NOT read it), SOURCE = that path; + if under `.ai/<name>/`, project = `<name>`, else derive a short kebab name from the filename; + mode = **extend** if that project already has `implementing.md`, else new. + - **Existing project** — else if `.ai/<FIRST_TOKEN>/` exists: project = `FIRST_TOKEN`; empty + remainder AND `implementing.md` present → mode = **resume**; non-empty remainder → mode = + **extend**, SOURCE = the remainder, OR — if the remainder is itself a path to an existing file — + SOURCE = that path (confirm it exists, do NOT read it). + - **New inline** — else SOURCE = all of `$ARGUMENTS`; pick a unique short kebab-case name. + After this step you have a project name and a SOURCE (inline text or a confirmed path), having read + neither the file nor any image. +4. Create `.ai/<project>/` and `.ai/<project>/images/` if new. +5. Images must be on disk: the planner reads them as files and subagents cannot see chat + attachments, so each image a task needs must exist as a file (referenced by the SOURCE file or + under `.ai/<project>/images/`). If the user only pasted an image into the chat, get it onto disk — + ask them to drop it into `.ai/<project>/images/` as a file — or, as a lossy fallback, write a + textual description for the planner. Don't claim to have saved a paste you can't. Images the + SOURCE file *references by path* are the planner's job, not yours. +6. If mode = **resume**, skip Phase B and go to Phase C. + +## Phase B: Planning & testability split (delegate) + +Spawn a worker subagent (`fork_context: false`). Give it the SOURCE — EITHER the inline request OR a +path to a task-list file (the main thread has NOT read it): tell the worker to READ the file itself +if SOURCE is a path, READ every image the source references (resolve relative to the SOURCE file's +dir), and COPY each into `.ai/<project>/images/` with a descriptive kebab-case name (the main thread +did not read or move them). Then read AGENTS.md, gauge scope, and produce the FINAL ordered task list +where EVERY task satisfies both constraints: +- **Implementable in one pass** — a single agent with a ~200k-token budget can implement it fully + without context compaction (a bounded change across a handful of files, not a sweep across dozens); + split anything bigger. +- **Independently testable** — each task yields an observable behavior the test agent can drive from + an in-app debug overlay and verify via log/screenshot; split on testable seams. +Minimal number of tasks subject to both; preserve dependency order. If the SOURCE is already a list +(inline or in the file the worker read), respect its breakdown and refine only as needed (split +too-big/untestable entries; optionally merge +trivially tiny adjacent ones if still one testable unit). Distribute the provided images across the tasks they pertain to: every +task that changes UI / visual / asset behavior MUST cite the specific mockups/resources it must match +via its `Images:` line (caption = what to match), and leave no provided image referenced by no task. +These per-task references are the oracle the test phase verifies against — be specific and per-task, +not one shared dump. + +Write `.ai/<project>/implementing.md`: + +``` +# Implementing: <project> + +## Goal +<one-line overall goal> + +## Tasks + +### a: <imperative title> +Status: todo +<2-4 line self-contained description: what to implement and the observable, testable result> +Images: images/<file> — <caption> (only if the task uses an image) + +### b: <imperative title> +Status: todo +<...> +``` + +For **extend** mode, APPEND new lettered tasks after the existing ones, leaving prior entries and +statuses untouched. The worker replies with ONLY a compact confirmation (`ready — <N> tasks`, or +extend: `ready — appended <letters>`), never echoing the task list or image contents. Then the main +thread reads `implementing.md` back ONCE (its first and only load of the task prose; it never reads +the images). + +## Phase C: Per-task loop (main thread orchestrates) + +For each task whose `Status` is not `approved`/`blocked`, in order: + +1. Set `Status: in-progress`. Spawn ONE **task-runner** worker (`fork_context: false`, request + `model: gpt-5.4`, `reasoning_effort: xhigh` when supported) with the prompt below. Apply + task-think's wait ladder (5-min waits while in progress, 1-2 min near completion; inspect the + task's progress/result artifacts on timeout; one follow-up then one fresh retry before escalating). +2. Read only its compact reply block. Detail is in `.ai/`. +3. Update the task's `Status:` — `approved` (STATUS DONE) or `blocked: <reason>`. +4. Append any `DISCOVERED` tasks as new lettered `### <letter>:` blocks (`Status: todo`) after the + remaining ones. The main thread is the only writer of `implementing.md`. +5. On BLOCKED, stop and report — do not start the next task. + +### task-runner prompt + +``` +You are a task-runner for ONE task in an autonomous implement-and-test workflow on Telegram +Desktop (C++ / Qt). You own this task end to end and MUST use subagents (spawn_agent/wait_agent) +for each phase, keeping the parent thread lean. + +PROJECT: <project> TASK: <letter> — <title> +TASK DESCRIPTION: +<the task's full description block from implementing.md> +IMAGES: <referenced .ai/<project>/images/* paths, or none> +TASK_DIR: .ai/<project>/<letter>/ TASK_ID: <project>-<letter> +Config (paths relative to this checkout): BUILD/EXE/MAX_ATTEMPTS = <values>. Test account = the +out/Debug/ portable-data folders (see test-loop.md "Test account"). + +Read first: AGENTS.md; REVIEW.md; `.agents/skills/task-think/PROMPTS.md` (Phase 1-6 templates + +execution rules); `.agents/shared/test-loop.md` (testing). Read any IMAGES listed above. For a +follow-up letter, also read `about.md` and the previous letter's `context.md`. +Create `<TASK_DIR>/` and `<TASK_DIR>/logs/`. + +Pipeline for THIS task only, one fresh subagent per phase, writing prompt/progress/result logs per +task-think: +1. CONTEXT — task-think Phase 1 (or 1F) -> context.md (+ about.md). +2. PLAN — Phase 2 -> plan.md. +3. ASSESS — Phase 3. +4. IMPLEMENT— Phase 4, one unit per plan phase. Do not commit in these units. +5. BUILD — Phase 5 (prefer same-thread build; fix errors; file-lock -> stop). +6. REVIEW — Phase 6 but a SINGLE pass (one 6a, one 6b if NEEDS_CHANGES, rebuild). +7. COMMIT — git commit with a concise plain-language subject (≤ ~50-60 chars, matching recent + `git log` style; usually the whole message — short plain body only if needed). NO `Autotask:`/ + attempt trailer and NO `Co-Authored-By:`/attribution line (overrides the default; see test-loop.md + "Commit message"). Submodules first, then superproject pointer. Record the commit SHA as IMPL_SHA + (track the attempt number yourself). +8. TEST — run `.agents/shared/test-loop.md` to APPROVED / BLOCKED / attempt cap. Spawn a + test-author subagent and feed it BOTH sides per test-loop.md "Design the tests from THIS task": + (1) the TASK SPEC — this task's full description block above PLUS its referenced IMAGES (have it + READ the mockups; they show the intended result), and (2) the implementation — `git show + <IMPL_SHA>` + touched files. It designs a falsifiable oracle per change and writes the plan into + `<TASK_DIR>/test.md` BEFORE running (visual/asset changes compare the tight crop vs old vs + intended-new art — judged VISUALLY, never by hash/byte; mobile mockups are not pixel targets), + covers every surface the task names, and never reuses another task's navigate+screenshot. Drive + RUN/ASSESS adversarially (no pass-by-inference; missing evidence = TEST_FLAW; + no-difference-from-before = IMPL_BUG) and keep the human-readable `<TASK_DIR>/test.md` report. Spawn an impl-fix subagent on IMPL_BUG (commits the + next attempt → new IMPL_SHA). After each run, save the overlay patch and `git reset --hard + <IMPL_SHA>`. Run the test-account SETUP before each launch; honor every test-account hard rule. + +Skip TEST only for docs/config-only tasks (say so). On Windows, after approval run task-think +Phase 7 (CRLF / no-BOM) on the task's touched source/config files. + +Reply with only the compact summary block from test-loop.md +(TASK/STATUS/VERDICT/ATTEMPTS/TOUCHED/DISCOVERED/NOTES). +``` + +## Completion + +Per-task summary (approved/blocked, attempts, touched files, key evidence), discovered tasks added, +the project name for follow-ups, total elapsed time, and a note that overlays are saved as +`<TASK_DIR>/test-overlay.patch` with the checkout reset to each task's implementation commit. + +## Error handling + +- Follow task-think's retry ladder for stuck phases; a task-runner returning BLOCKED stops the loop. +- Never push past a file-lock build error. +- The launch gate (Phase A) guarantees the test account exists before any work begins. +- Keep `.ai/` artifacts and edited text files LF/no-BOM on WSL; run CRLF normalization only on a + native Windows checkout. + +## User invocation + +`Use local implement skill: <request or path to a task file>` — ideally under Goal run mode so it +loops to a tested state. Resume/extend: `Use local implement skill: <project> [additional change]`. diff --git a/.claude/commands/implement.md b/.claude/commands/implement.md new file mode 100644 index 0000000000..795736f8dc --- /dev/null +++ b/.claude/commands/implement.md @@ -0,0 +1,258 @@ +--- +description: Autonomously implement a task (split into a task list if needed), then implement + test each task to approval via isolated per-task subagents +allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, AskUserQuestion, TodoWrite +--- + +# Implement - Autonomous Implement-and-Test Orchestrator + +You are the **top orchestrator**. You take a request — an inline description OR a task-list file — +normalize it into a project with a testability-split task list, and drive each task to +test-approval through an isolated per-task `task-runner` subagent. Your context must stay lean: you +hold only the task list and one compact summary per task. All heavy work (planning, coding, +building, testing) happens inside subagents whose context is discarded. + +This is the tested superset of `/task`: it reuses `/task`'s phase prompts for implementation and +adds the impl⇄test loop defined in `.agents/shared/test-loop.md`. + +**Arguments:** `$ARGUMENTS` = ONE of: +- an inline task description (e.g. `add a dark-mode toggle to settings`) +- a path to a task-list file (e.g. `.ai/communities/tasks.txt` — a rough list of tasks to refine) +- an existing project name to resume (e.g. `communities`), optionally followed by extra work +May also reference attached images. + +## Config + +Runs in the **current checkout** — wherever `/implement` is invoked. No worktrees are created; all +paths below are relative to that repository root. + +``` +BUILD = cmake --build ./out --config Debug --target Telegram +EXE = ./out/Debug/Telegram.exe +TEST_ACCOUNT = ./out/Debug/test_TelegramForcePortable # user-prepared golden; launch gate aborts if absent +MAX_ATTEMPTS = 4 +``` + +Tasks run **sequentially** in this one checkout (the build cache stays warm; app runs must +serialize against the account anyway). To parallelize, launch `/implement` in a different +checkout/slot (e.g. `C:\Telegram\tdesktop`, `D:\Telegram\tdesktop`, `D:\Telegram\twin`) — each run +is independent and single-tree. Don't run the **test phase** in two slots against the same account +at once (concurrent clients on one auth key can trigger a session reset); give parallel slots +separate test accounts. + +## Artifacts (per project) + +- `.ai/<project>/implementing.md` — the canonical, final, testability-split task list (descriptions + + status). Your single source of truth; you are its only writer. +- `.ai/<project>/images/` — illustrations referenced by tasks (`images/01.png`, ...). +- `.ai/<project>/<letter>/` — per-task artifacts (context, plan, review, test, result, overlay). +- `.ai/<project>/about.md` — project blueprint (the `/task` convention). + +## Done (for `/goal` loop mode) + +The run is **done** when every task in `implementing.md` has `Status: approved` or +`Status: blocked: <reason>`. Under a `/goal` loop this is the stop condition. The run is +**resumable**: re-invoking with the project name reads `implementing.md` and continues from the +first unfinished task. + +## Phase A: Setup & input resolution + +1. Record start time (`Get-Date`). +2. **Test-account gate (hard precondition — before any work).** If + `out/Debug/test_TelegramForcePortable` does NOT exist, STOP the entire command immediately and + tell the user: the test account is not prepared — create `out/Debug/test_TelegramForcePortable` + (a portable-data folder authed to a throwaway test account) before `/implement` can run, because + autonomous testing is impossible without it. Do no implementation work. +3. **Resolve `$ARGUMENTS` into (project, SOURCE, mode) — without reading task files or images.** + The main thread never loads task prose or assets; resolving needs only paths and existence + checks. SOURCE ends up as EITHER inline text OR a confirmed file path (the planner reads it). + - **File input** — if the first token is a path: confirm it exists (`Test-Path`, do NOT read it) + and set SOURCE = that path. If the path is under `.ai/<name>/`, project = `<name>`; else derive + a short kebab name from the filename. Mode = **extend** if that project already has + `implementing.md`, else new. + - **Existing project** — else if `.ai/<FIRST_TOKEN>/` exists: project = `FIRST_TOKEN`. If the + remainder is empty AND `implementing.md` exists → mode = **resume**. If there is a remainder → + mode = **extend**: if the remainder is itself a path to an existing file, SOURCE = that path + (confirm with `Test-Path`, do NOT read it); otherwise SOURCE = the remainder text. + - **New inline** — else SOURCE = the `$ARGUMENTS` text; pick a unique short kebab-case project + name (consult `ls .ai/`). + After this step you always have a project name and a SOURCE (inline text or a confirmed path) — + and you have read neither the file nor any image. +4. Create `.ai/<project>/` and `.ai/<project>/images/` if new. +5. **Images must be on disk.** The planner reads images as files, and subagents cannot see chat + attachments — so every image a task needs must exist as a file (referenced by the SOURCE file, or + under `.ai/<project>/images/`). The main thread **cannot** save a pasted/inline chat image to disk + (`Write` is text-only; there is no save-attachment tool, and on Windows clipboard-paste isn't even + supported). So if the user only pasted an image into the chat, either ask them to drop it into + `.ai/<project>/images/` as a file, or — as a lossy fallback — write a textual description of it for + the planner. Do not claim to have saved it. Images the SOURCE file *references by path* are the + planner's job, not handled here. +6. If mode = **resume**, skip Phase B and go to Phase C. + +## Phase B: Planning & testability split + +Spawn one planner subagent (Task, `general-purpose`): + +``` +You are a planning/splitting agent for a large C++ codebase (Telegram Desktop). + +SOURCE — EITHER an inline request OR a path to a task-list file. If it is a PATH, READ it yourself +(and any task files it points to); the main thread has NOT read it. If it is inline text, use it as +the request: +<the inline description, or the file path> +PROJECT: <project> MODE: <new | extend> + +IMAGES — the SOURCE and/or its task file may reference images by path (resolve them relative to the +SOURCE file's directory, or use absolute paths). READ every referenced image yourself, then COPY +each into `.ai/<project>/images/` with a descriptive kebab-case name, and reference it from the +specific task(s) it pertains to (see "Images per task" below). The main thread did NOT read or move +these — that is your job. If an image exists only as a textual description (because the user pasted +it into chat and it could not be saved to a file), it is provided here — treat that description as +the visual spec: <description(s) or none> + +Read AGENTS.md. Briefly scan the codebase to gauge scope. Produce the FINAL ordered task list that +satisfies BOTH constraints for every task: + +- **Implementable in one pass**: a single agent with a ~200k-token budget must be able to implement + the task fully on its own WITHOUT triggering context compaction — i.e. a bounded change it can + read and edit across a handful of files, not a sweep across dozens. If a unit is too big, split + it. +- **Independently testable**: each task must yield an observable behavior the test agent can drive + from an in-app debug overlay and verify via log/screenshot. Split on testable seams, so each task + ends at a point where something concrete can be exercised and checked. + +Use the minimal number of tasks subject to both constraints; preserve dependency order (a task +comes before any task that depends on it). If the SOURCE is already a list, respect its intended +breakdown and refine only as needed: split entries that are too big or not independently testable; +you may merge trivially tiny adjacent entries if the result is still one testable unit. + +Write `.ai/<project>/implementing.md` in EXACTLY this format: + +# Implementing: <project> + +## Goal +<one-line overall goal> + +## Tasks + +### a: <imperative title> +Status: todo +<2-4 line self-contained description: what to implement and the observable, testable result. Enough +that a fresh agent can act on it.> +Images: images/<file> — <caption> (this line only if the task uses an image) + +### b: <imperative title> +Status: todo +<...> + +**Images per task (required).** Every provided image is a design/resource the work must satisfy. +Attach each to the task(s) it pertains to via the `Images:` line, with a caption stating what that +task must match in it (the exact wording on a mockup, the glyph/shape of a resource, etc.). A task +that changes UI / visual / asset behavior MUST cite the specific mockups/resources it has to match; +do not leave such a task without its images, and do not leave a provided image referenced by no task +(if one genuinely applies to none, note why). These per-task references are the oracle the test +phase verifies against — be specific and per-task, not one shared dump on the first task. + +Use letters a, b, c... as task ids. Do not plan internals or implement. When done, reply with ONLY a +compact confirmation — `ready — <N> tasks` (extend: `ready — appended <letters>`); do NOT echo the +task list or image contents back, the main thread reads `implementing.md` itself. +``` + +For **extend** mode, instead instruct the planner to APPEND new lettered tasks (continuing the +letter sequence) after the existing ones, leaving existing entries and their statuses untouched. + +After the planner replies `ready`, read `implementing.md` back ONCE (your first and only load of the +task prose; you never read the images). Initialize a TodoWrite list mirroring the tasks so progress +is visible. + +## Phase C: Per-task loop + +For each task in `implementing.md` whose `Status` is not `approved`/`blocked`, in order: + +1. Set its `Status: in-progress` (and mark `in_progress` in TodoWrite). +2. Spawn ONE `task-runner` subagent (Task, `general-purpose`) with the prompt below. Wait for it. +3. Read ONLY its compact summary block (the `task-runner` writes all detail to `.ai/`). +4. Update the task's `Status:` line — `approved` if `STATUS: DONE`, else `blocked: <reason>`. +5. If `DISCOVERED` lists new tasks, append them to `implementing.md` as new lettered `### <letter>:` + blocks (`Status: todo`) **after** the current remaining tasks, and add them to TodoWrite. (You + are the only writer of `implementing.md`, so there are no write races.) +6. If `STATUS: BLOCKED`, stop the loop and report to the user — do not start the next task. (Under + `/goal`, surfacing the blocker is the correct stop; the loop should not spin on a blocked task.) + +### task-runner prompt + +``` +You are a task-runner for ONE task in an autonomous implement-and-test workflow on Telegram +Desktop (C++ / Qt). You own this task end to end and isolate its context from the orchestrator. +You MAY and SHOULD spawn your own subagents (the Task tool is available to you). + +PROJECT: <project> TASK: <letter> — <title> +TASK DESCRIPTION: +<the task's full description block from implementing.md> +IMAGES: <referenced .ai/<project>/images/* paths, or none — Read them if present> +TASK_DIR: .ai/<project>/<letter>/ +TASK_ID: <project>-<letter> + +Config (paths relative to this checkout): BUILD=<...> EXE=<...> MAX_ATTEMPTS=<...>. The test account +is the out/Debug/ portable-data folders (see test-loop.md "Test account"). + +Read first: AGENTS.md; REVIEW.md; `.claude/commands/task.md` (for the exact Phase 1-6 prompt +templates); `.agents/shared/test-loop.md` (for the testing phase). For a follow-up letter, also read +`.ai/<project>/about.md` and the previous letter's `context.md`. + +Run this pipeline for THIS task only, spawning a fresh subagent per phase (so each phase's output +stays in YOUR context, not the orchestrator's): + +1. CONTEXT — run task.md's Phase 1 (new) or Phase 1F (follow-up) prompt for this task; produces + `<TASK_DIR>/context.md` (and `about.md` for the project). +2. PLAN — task.md Phase 2 -> `<TASK_DIR>/plan.md`. +3. ASSESS — task.md Phase 3 (refine plan, size phases). +4. IMPLEMENT— task.md Phase 4, one subagent per plan phase. Implementation agents do NOT commit + yet; you commit after build passes. +5. BUILD — task.md Phase 5 (build with BUILD, fix errors). On file-lock errors, taskkill and + retry once, else stop. +6. REVIEW — task.md Phase 6 but a SINGLE pass (not 3): one review agent, then one fix agent if + NEEDS_CHANGES, then rebuild. (Tests catch behavior; review catches dead code / duplication / + placement / style.) +7. COMMIT — `git add -A && git commit` with a concise plain-language subject (≤ ~50-60 chars, + matching recent `git log` style; usually the whole message — add a short plain body only if the + subject can't carry it). NO `Autotask:`/attempt trailer and NO `Co-Authored-By:`/attribution line + (this overrides the default; see test-loop.md "Commit message"). Commit submodules first if dirty, + then bump the pointer. Record the commit SHA as IMPL_SHA (you track the attempt number yourself). +8. TEST — run the loop in `.agents/shared/test-loop.md` to APPROVED, BLOCKED, or attempt cap. + Spawn a test-author subagent and feed it BOTH sides per test-loop.md "Design the tests from THIS + task": (1) the TASK SPEC — this task's full description block above PLUS its referenced IMAGES + (tell it to READ the mockups; they show the intended result), and (2) the implementation — + `git show <IMPL_SHA>` + touched files. It designs a falsifiable oracle per change and writes the + plan into `<TASK_DIR>/test.md` BEFORE running (for visual/asset changes the oracle compares the + tight crop against old vs intended-new art — judged VISUALLY, never by hash/byte; mobile mockups + are not pixel targets), covers every surface the task names, and never reuses another task's + navigate+screenshot. You drive RUN/ASSESS yourself, ADVERSARIALLY (no pass-by-inference; missing + evidence = TEST_FLAW; no-difference-from-before = IMPL_BUG), and keep the human-readable + `<TASK_DIR>/test.md` report. Spawn an impl-fix subagent on IMPL_BUG (it commits the next attempt → + new IMPL_SHA). After each run, save the overlay patch into TASK_DIR and `git reset --hard + <IMPL_SHA>` so the checkout returns to impl-only. Run the test-account SETUP steps before each + launch and honor every test-account hard rule (serialize app runs; avoid destructive calls). + +Skip TEST only if the task changed no runnable behavior (docs/config only) — say so explicitly. + +When done, write nothing new to chat except the compact summary block from test-loop.md +("TASK/STATUS/VERDICT/ATTEMPTS/TOUCHED/DISCOVERED/NOTES"). All reasoning lives in `.ai/`. +``` + +## Completion + +When the loop ends (all tasks approved/blocked, or a blocked task stopped it): +1. Summarize per task: approved vs blocked, attempts, files touched, key test evidence. +2. List any discovered tasks that were added. +3. Note the project name for `/implement <project> <follow-up>`. +4. Show total elapsed time (`Xh Ym Zs`, omit zero components). +5. Remind that test overlays are saved as `.ai/<project>/<letter>/test-overlay.patch` and the + checkout is left at each task's implementation commit (overlays reset away). + +## Error handling + +- A `task-runner` returning BLOCKED stops the loop; report its reason and the `test.md` path. +- If `implementing.md` or any artifact is malformed, re-spawn that step with tighter instructions. +- Never proceed past a file-lock build error — ask the user to close `Telegram.exe`. +- The launch gate (Phase A) guarantees the test account exists before any work begins; if it is + absent the command never starts. diff --git a/.claude/commands/planner.md b/.claude/commands/planner.md deleted file mode 100644 index 0a0e2e7c3b..0000000000 --- a/.claude/commands/planner.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -description: Plan and create a repetitive task automation (prompt.md + tasks.json pair) -allowed-tools: Read, Write, Edit, Glob, Grep, Bash(mkdir:*), Bash(ls:*), AskUserQuestion ---- - -# Task Planner - Create Automated Task Workflows - -You are setting up a new **repetitive task automation** for Claude Code. The goal is to create a folder in `.ai/<featurename>/` containing: -- `prompt.md` - Detailed instructions for the autonomous agent -- `tasks.json` - List of tasks with completion tracking - -This pair can then be executed via `.claude/iterate.ps1 <featurename>`. - -## Your Workflow - -### 1. Understand the Goal - -First, understand what the user wants to automate. Ask clarifying questions using AskUserQuestion if needed: -- What is the overall goal/feature being implemented? -- What are the individual tasks involved? -- Are there dependencies between tasks? -- What files/areas of the codebase are involved? -- Are there any reference examples or patterns to follow? - -### 2. Choose a Feature Name - -The `<featurename>` should be: -- Short (1-2 words, lowercase, hyphen-separated) -- Easy to type on command line -- Descriptive of the work being done -- Not already used in `.ai/` - -Check existing folders: -```bash -ls .ai/ -``` - -Suggest a name to the user or let them specify one directly via $ARGUMENTS. - - -### 3. Create the Folder and Files - -Create `.ai/<featurename>/`: - -**prompt.md** should include: -- Overview of what we're doing -- Architecture/context needed -- Step-by-step instructions for each task type -- Code patterns and examples -- Build/test commands -- Commit message format (see below) - -### Commit Message Guidelines - -All prompts should specify commit message length requirements: -- **Soft limit**: ~50 characters (ideal length for first line) -- **Hard limit**: 76 characters (must not exceed) - -Example instruction for prompt.md: -``` -## Commit Format - -First line: Short summary ending with a dot (aim for ~50 chars, max 76 chars) - -<Optional body with details, also ending with a dot.> - -IMPORTANT: Never try to commit files in .ai/ -``` - -**tasks.json** format: -```json -{ - "tasks": [ - { - "id": "task-id", - "title": "Short task title", - "description": "Detailed description of what to do", - "started": false, - "completed": false, - "dependencies": ["other-task-id"] - } - ] -} -``` - -### 4. Iterate with the User - -After creating initial files, the user may want to: -- Add more tasks to tasks.json -- Refine the prompt with more details -- Add examples or patterns -- Clarify instructions - -Keep refining until the user is satisfied. - -## Arguments - -If `$ARGUMENTS` is provided, it's the feature name to use: -- `$ARGUMENTS` = "$ARGUMENTS" - -If empty, you'll need to determine/suggest a name based on the discussion. - -## Examples - -### Example 1: Settings Migration -``` -/taskplanner settings-upgrade -``` -Creates `.ai/settings-upgrade/` with prompt and tasks for migrating settings sections. - -### Example 2: Open-ended -``` -/taskplanner -``` -Starts a conversation to understand what needs to be automated, then creates the appropriate folder. - -## Starting Point - -Let's begin! Please describe: -1. What repetitive coding task do you want to automate? -2. What is the end goal? -3. Do you have initial tasks in mind, or should we discover them together? diff --git a/.claude/commands/withtest.md b/.claude/commands/withtest.md deleted file mode 100644 index d6f3c72f30..0000000000 --- a/.claude/commands/withtest.md +++ /dev/null @@ -1,656 +0,0 @@ ---- -description: Implement a feature using multi-agent workflow, then iteratively test and fix it in-app -allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, AskUserQuestion, TodoWrite ---- - -# WithTest - Multi-Agent Implementation + Testing Workflow - -You orchestrate a multi-phase implementation workflow followed by an iterative testing/fixing loop. This is an extended version of `/task` that adds in-app programmatic testing after the build succeeds. - -**Arguments:** `$ARGUMENTS` = "$ARGUMENTS" - -If `$ARGUMENTS` is provided, it's the task description. If empty, ask the user what they want implemented. - -## Overview - -The workflow produces `.ai/<feature-name>/` containing: -- `context.md` - Gathered codebase context relevant to the task -- `plan.md` - Detailed implementation plan with phases and status -- `testN.md` - Test plan for iteration N -- `resultN.md` - Test result report for iteration N -- `planN.md` - Fix plan for iteration N (if implementation bugs found) -- `screenshots/` - Screenshots captured during test runs - -Two major stages: -1. **Implementation** (Phases 0-5) - same as `/task` -2. **Testing Loop** (Phase 6) - iterative test-plan → test-do → test-run → test-check cycle - ---- - -## STAGE 1: IMPLEMENTATION (Phases 0-5) - -These phases are identical to the `/task` workflow. - -### Phase 0: Setup - -1. Understand the task from `$ARGUMENTS` or ask the user. -2. **Follow-up detection:** Check if `$ARGUMENTS` starts with a task name (the first word/token before any whitespace or newline). Look for `.ai/<that-name>/` directory: - - If `.ai/<that-name>/` exists AND contains both `context.md` and `plan.md`, this is a **follow-up task**. Read both files. The rest of `$ARGUMENTS` (after the task name) is the follow-up task description describing what additional changes are needed. - - If no matching directory exists, this is a **new task** - proceed normally. -3. For new tasks: check existing folders in `.ai/` to pick a unique short name (1-2 lowercase words, hyphen-separated) and create `.ai/<feature-name>/`. -4. For follow-up tasks: the folder already exists, skip creation. - -### Follow-up Task Flow - -When a follow-up task is detected (existing `.ai/<name>/` with `context.md` and `plan.md`): - -1. Skip Phase 1 (Context Gathering) - context already exists. -2. Skip Phase 2 (Planning) - original plan already exists. -3. Go directly to **Phase 2F (Follow-up Planning)** instead of Phase 3. - -**Phase 2F: Follow-up Planning** - -Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt: - -``` -You are a planning agent for a follow-up task on an existing implementation. - -Read these files: -- .ai/<feature-name>/context.md - Previously gathered codebase context -- .ai/<feature-name>/plan.md - Previous implementation plan (already completed) - -Then read the source files referenced in context.md and plan.md to understand what was already implemented. - -FOLLOW-UP TASK: <paste the follow-up task description here> - -The previous plan was already implemented and tested. Now there are follow-up changes needed. - -YOUR JOB: -1. Understand what was already done from plan.md (look at the completed phases). -2. Read the actual source files to see the current state of the code. -3. If context.md needs updates for the follow-up task (new files relevant, new patterns needed), update it with additional sections marked "## Follow-up Context (iteration 2)" or similar. -4. Create a NEW follow-up plan. Update plan.md by: - - Keep the existing content as history (do NOT delete it) - - Add a new section at the end: - - --- - ## Follow-up Task - <description> - - ## Follow-up Approach - <high-level description> - - ## Follow-up Files to Modify - <list> - - ## Follow-up Implementation Steps - - ### Phase F1: <name> - 1. <specific step> - 2. ... - - ### Phase F2: <name> (if needed) - ... - - ## Follow-up Status - Phases: <N> - - [ ] Phase F1: <name> - - [ ] Phase F2: <name> (if applicable) - - [ ] Build verification - - [ ] Testing - Assessed: yes - -Reason carefully. The follow-up plan should be self-contained enough that an implementation agent can execute it by reading context.md and the updated plan.md. -``` - -After this agent completes, read `plan.md` to verify the follow-up plan was written. Then proceed to Phase 4 (Implementation), using the follow-up phases (F1, F2, etc.) instead of the original phases. After implementation and build verification, proceed to Stage 2 (Testing Loop) as normal. - -### New Task Flow - -When this is a new task (no existing folder), proceed with Phases 1-5 as described below. - -### Phase 1: Context Gathering - -Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure: - -``` -You are a context-gathering agent for a large C++ codebase (Telegram Desktop). - -TASK: <paste the user's task description here> - -YOUR JOB: Read CLAUDE.md, inspect the codebase, find ALL files and code relevant to this task, and write a comprehensive context document. - -Steps: -1. Read CLAUDE.md for project conventions and build instructions. -2. Search the codebase for files, classes, functions, and patterns related to the task. -3. Read all potentially relevant files. Be thorough - read more rather than less. -4. For each relevant file, note: - - File path - - Relevant line ranges - - What the code does and how it relates to the task - - Key data structures, function signatures, patterns used -5. Look for similar existing features that could serve as a reference implementation. -6. Check api.tl if the task involves Telegram API. -7. Check .style files if the task involves UI. -8. Check lang.strings if the task involves user-visible text. - -Write your findings to: .ai/<feature-name>/context.md - -The context.md should contain: -- **Task Description**: The full task restated clearly -- **Relevant Files**: Every file path with line ranges and descriptions of what's there -- **Key Code Patterns**: How similar things are done in the codebase (with code snippets) -- **Data Structures**: Relevant types, structs, classes -- **API Methods**: Any TL schema methods involved (copied from api.tl) -- **UI Styles**: Any relevant style definitions -- **Localization**: Any relevant string keys -- **Build Info**: Build command and any special notes -- **Reference Implementations**: Similar features that can serve as templates - -Be extremely thorough. Another agent with NO prior context will read this file and must be able to understand everything needed to implement the task. -``` - -After this agent completes, read `context.md` to verify it was written properly. - -### Phase 2: Planning - -Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure: - -``` -You are a planning agent. You must create a detailed implementation plan. - -Read these files: -- .ai/<feature-name>/context.md - Contains all gathered context -- Then read the specific source files referenced in context.md to understand the code deeply. - -Think carefully about the implementation approach. - -Create a detailed plan in: .ai/<feature-name>/plan.md - -The plan.md should contain: - -## Task -<one-line summary> - -## Approach -<high-level description of the implementation approach> - -## Files to Modify -<list of files that will be created or modified> - -## Files to Create -<list of new files, if any> - -## Implementation Steps - -Each step must be specific enough that an agent can execute it without ambiguity: -- Exact file paths -- Exact function names -- What code to add/modify/remove -- Where exactly in the file (after which function, in which class, etc.) - -Number every step. Group steps into phases if there are more than ~8 steps. - -### Phase 1: <name> -1. <specific step> -2. <specific step> -... - -### Phase 2: <name> (if needed) -... - -## Build Verification -- Build command to run -- Expected outcome - -## Status -- [ ] Phase 1: <name> -- [ ] Phase 2: <name> (if applicable) -- [ ] Build verification -- [ ] Testing -``` - -After this agent completes, read `plan.md` to verify it was written properly. - -### Phase 3: Plan Assessment - -Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure: - -``` -You are a plan assessment agent. Review and refine an implementation plan. - -Read these files: -- .ai/<feature-name>/context.md -- .ai/<feature-name>/plan.md -- Then read the actual source files referenced to verify the plan makes sense. - -Carefully assess the plan: - -1. **Correctness**: Are the file paths and line references accurate? Does the plan reference real functions and types? -2. **Completeness**: Are there missing steps? Edge cases not handled? -3. **Code quality**: Will the plan minimize code duplication? Does it follow existing codebase patterns from CLAUDE.md? -4. **Design**: Could the approach be improved? Are there better patterns already used in the codebase? -5. **Phase sizing**: Each phase should be implementable by a single agent in one session. If a phase has more than ~8-10 substantive code changes, split it further. - -Update plan.md with your refinements. Keep the same structure but: -- Fix any inaccuracies -- Add missing steps -- Improve the approach if you found better patterns -- Ensure phases are properly sized for single-agent execution -- Add a line at the top of the Status section: `Phases: <N>` indicating how many implementation phases there are -- Add `Assessed: yes` at the bottom of the file - -If the plan is small enough for a single agent (roughly <=8 steps), mark it as a single phase. -``` - -After this agent completes, read `plan.md` to verify it was assessed. - -### Phase 4: Implementation - -Now read `plan.md` yourself to understand the phases. - -For each phase in the plan that is not yet marked as done, spawn an implementation agent (Task tool, subagent_type=`general-purpose`): - -``` -You are an implementation agent working on phase <N> of an implementation plan. - -Read these files first: -- .ai/<feature-name>/context.md - Full codebase context -- .ai/<feature-name>/plan.md - Implementation plan - -Then read the source files you'll be modifying. - -YOUR TASK: Implement ONLY Phase <N> from the plan: -<paste the specific phase steps here> - -Rules: -- Follow the plan precisely -- Follow CLAUDE.md coding conventions (no comments except complex algorithms, use auto, empty line before closing brace, etc.) -- Do NOT modify .ai/ files except to update the Status section in plan.md -- When done, update plan.md Status section: change `- [ ] Phase <N>: ...` to `- [x] Phase <N>: ...` -- Do NOT work on other phases - -When finished, report what you did and any issues encountered. -``` - -After each implementation agent returns: -1. Read `plan.md` to check the status was updated. -2. If more phases remain, spawn the next implementation agent. -3. If all phases are done, proceed to build verification. - -### Phase 5: Build Verification - -Spawn a build verification agent (Task tool, subagent_type=`general-purpose`): - -``` -You are a build verification agent. - -Read these files: -- .ai/<feature-name>/context.md -- .ai/<feature-name>/plan.md - -The implementation is complete. Your job is to build the project and fix any build errors. - -Steps: -1. Run: cmake --build "c:\Telegram\tdesktop\out" --config Debug --target Telegram -2. If the build succeeds, update plan.md: change `- [ ] Build verification` to `- [x] Build verification` -3. If the build fails: - a. Read the error messages carefully - b. Read the relevant source files - c. Fix the errors in accordance with the plan and CLAUDE.md conventions - d. Rebuild and repeat until the build passes - e. Update plan.md status when done - -Rules: -- Only fix build errors, do not refactor or improve code -- Follow CLAUDE.md conventions -- If build fails with file-locked errors (C1041, LNK1104), STOP and report - do not retry - -When finished, report the build result. -``` - -After the build agent returns, read `plan.md` to confirm build verification passed. If it did, proceed to Stage 2. - ---- - -## STAGE 2: TESTING LOOP (Phase 6) - -This stage iteratively tests the implementation in-app and fixes issues. It maintains an iteration counter `N` starting at 1. - -**Key concept:** Since the project has tight coupling and no unit test infrastructure, we test by injecting `#ifdef _DEBUG` blocks into the app code that perform actions, write to `log.txt`, save screenshots, and call `Core::Quit()` when done. An agent then runs the app and observes the output. - -### Git Submodule Awareness - -Before ANY git operation (commit, stash, stash pop), the agent must: -1. Run `git submodule status` to check for modified submodules. -2. If submodules have changes, commit/stash those submodules FIRST, individually: - ``` - cd <submodule-path> && git add -A && git commit -m "[wip-N] test changes" && cd <repo-root> - ``` - or for stash: - ``` - cd <submodule-path> && git stash && cd <repo-root> - ``` -3. Then operate on the main repo. - -### Step 6a: Test Plan (test-plan agent) - -Spawn an agent (Task tool, subagent_type=`general-purpose`): - -``` -You are a test-planning agent for Telegram Desktop (C++ / Qt). - -Read these files: -- .ai/<feature-name>/context.md -- .ai/<feature-name>/plan.md -<if N > 1, also include:> -- .ai/<feature-name>/result<N-1>.md - Previous test result -<if a planN.md triggered this iteration:> -- .ai/<feature-name>/plan<trigger>.md - Fix plan that was just implemented - -CURRENT ITERATION: <N> - -YOUR TASKS: - -1. **Commit current implementation changes.** - - Run `git submodule status` to check for modified submodules. - - If any submodules are dirty, go into each one and commit: - `cd <submodule> && git add -A && git commit -m "[wip-<N>]" && cd <repo-root>` - - Then in main repo: `git add -A && git commit -m "[wip-<N>]"` - - Do NOT add files in .ai/ to the commit. - -2. <If N > 1> **Restore previous test code.** - - Run `git submodule status` and `git stash list` in any dirty submodules to check for stashed test code. - - Pop submodule stashes first: `cd <submodule> && git stash pop && cd <repo-root>` - - Then pop main repo stash: `git stash pop` - - Read the previous test<N-1>.md to understand what was tested before. - - Decide: reuse/modify existing test code or start fresh. - -3. **Plan the test code.** - Carefully design test code that will verify the implementation works correctly. - - The test code must: - - Be wrapped in `#ifdef _DEBUG` blocks so it only runs in Debug builds - - Be injected at appropriate points in the app lifecycle (e.g., after main window shows, after chats load, etc.) - - Write progress and results to a log file. Use a dedicated path like: - `QFile logFile("c:/Telegram/tdesktop/.ai/<feature-name>/test_log.txt");` - Open with `QIODevice::Append | QIODevice::Text`, write with QTextStream, and flush after every write. - - Save screenshots where visual verification is needed: - `widget->grab().save("c:/Telegram/tdesktop/.ai/<feature-name>/screenshots/<name>.png");` - Log each screenshot save: `"SCREENSHOT: <full-path>"` - - Use `QTimer::singleShot(...)` or deferred calls to schedule test steps after UI events settle - - Call `Core::Quit()` when all test steps complete, so the app exits cleanly - - Log `"TEST_COMPLETE"` right before `Core::Quit()` so the test-run agent knows testing finished - - Log `"TEST_STEP: <description>"` before each major step for progress tracking - - Log `"TEST_RESULT: PASS: <what>"` or `"TEST_RESULT: FAIL: <what> - <details>"` for each check - - Consider what needs testing: - - Does the new UI appear correctly? - - Do interactions work (clicks, navigation)? - - Does data flow correctly? - - Are there edge cases to verify? - -4. **Write the test plan** to `.ai/<feature-name>/test<N>.md` containing: - - ## Test Iteration <N> - ## What We're Testing - <description of what this test verifies> - - ## Test Steps - 1. <step>: what we do, what we expect, how we verify - 2. ... - - ## Code Injection Points - - File: <path>, Location: <where in file>, Purpose: <what this block does> - - ... - - ## Expected Log Output - <example of what test_log.txt should contain if everything works> - - ## Expected Screenshots - - <name>.png: should show <description> - - ... - - ## Success Criteria - - <criterion 1> - - <criterion 2> - - ... - -When finished, report what test plan was created. -``` - -### Step 6b: Test Implementation (test-do agent) - -Spawn an agent (Task tool, subagent_type=`general-purpose`): - -``` -You are a test implementation agent for Telegram Desktop (C++ / Qt). - -Read these files: -- .ai/<feature-name>/context.md -- .ai/<feature-name>/plan.md -- .ai/<feature-name>/test<N>.md - The test plan to implement - -YOUR TASK: Implement the test code described in test<N>.md. - -Rules: -- ALL test code MUST be inside `#ifdef _DEBUG` blocks -- Place test code at the injection points specified in the test plan -- Make sure the screenshots folder exists: create `.ai/<feature-name>/screenshots/` directory -- Delete any old test_log.txt before the test starts (in code, at the first test step) -- Use QTimer::singleShot for delayed operations to let the UI settle -- Flush log writes immediately (don't buffer) -- End with logging "TEST_COMPLETE" and calling Core::Quit() -- Follow CLAUDE.md coding conventions -- Make sure the code compiles: run `cmake --build "c:\Telegram\tdesktop\out" --config Debug --target Telegram` -- If build fails, fix errors and rebuild until it passes -- If build fails with file-locked errors (C1041, LNK1104), STOP and report - -When finished, report what test code was added and where. -``` - -### Step 6c: Test Run (test-run agent) - -Spawn an agent (Task tool, subagent_type=`general-purpose`): - -``` -You are a test execution agent. You run the Telegram Desktop app and observe test output. - -Read these files: -- .ai/<feature-name>/test<N>.md - The test plan (so you know what to expect) - -YOUR TASK: Run the built app and monitor test execution. - -Steps: - -1. **Prepare.** - - Delete old test_log.txt if it exists: `del "c:\Telegram\tdesktop\docs\ai\work\<feature-name>\test_log.txt" 2>nul` - - Ensure screenshots folder exists: `mkdir "c:\Telegram\tdesktop\docs\ai\work\<feature-name>\screenshots" 2>nul` - -2. **Launch the app.** - - Run in background: `start "" "c:\Telegram\tdesktop\out\Debug\Telegram.exe"` - - Note the time of launch. - -3. **Monitor test_log.txt in a polling loop.** - - Every 5 seconds, read the log file to check for new output. - - When you see `"SCREENSHOT: <path>"`, read the screenshot image file to visually verify it. - - Track which TEST_STEP entries appear. - - Track TEST_RESULT entries (PASS/FAIL). - -4. **Detect completion or failure.** - - **Success**: Log contains `"TEST_COMPLETE"` - the app should exit on its own shortly after. - - **Crash**: The process disappears before `"TEST_COMPLETE"`. Check for crash dumps or error dialogs. - - **Hang/Timeout**: If no new log output for 120 seconds and no `"TEST_COMPLETE"`, kill the process: - `taskkill /IM Telegram.exe /F` - - **No log at all**: If no test_log.txt appears within 60 seconds of launch, kill the process. - -5. **After the process exits (or is killed), wait 5 seconds, then:** - - Read the full final test_log.txt - - Read all screenshot files saved during the test - - Check for any leftover Telegram.exe processes: `tasklist /FI "IMAGENAME eq Telegram.exe"` and kill if needed - -6. **Write the result report** to `.ai/<feature-name>/result<N>.md`: - - ## Test Result - Iteration <N> - ## Outcome: <PASS / FAIL / CRASH / TIMEOUT> - - ## Log Output - <full contents of test_log.txt, or note that it was empty/missing> - - ## Screenshot Analysis - - <name>.png: <description of what you see, whether it matches expectations from test<N>.md> - - ... - - ## Test Results Summary - - PASS: <list> - - FAIL: <list> - - ## Issues Found - <any problems observed, unexpected behavior, etc.> - - ## Raw Details - <process exit code if available, timing information, any stderr output> - -When finished, report the test outcome. -``` - -After the test-run agent returns, read `result<N>.md`. - -### Step 6d: Test Assessment (test-check agent) - -Spawn an agent (Task tool, subagent_type=`general-purpose`): - -``` -You are a test assessment agent. You analyze test results and decide next steps. - -Read these files: -- .ai/<feature-name>/context.md -- .ai/<feature-name>/plan.md -- .ai/<feature-name>/test<N>.md -- .ai/<feature-name>/result<N>.md -<if N > 1, also read previous test/result pairs for history> - -Carefully analyze the test results. - -DECIDE one of three outcomes: - -### Outcome A: ALL TESTS PASS -If all test results are PASS and screenshots look correct: -1. Write to result<N>.md (append): `\n## Verdict: PASS` -2. Report "ALL_TESTS_PASS" so the orchestrator knows to finish. - -### Outcome B: TEST CODE NEEDS CHANGES -If the test itself was flawed (wrong assertions, bad timing, insufficient waits, screenshot taken too early, wrong injection point, etc.) but the implementation seems correct: -1. Describe what's wrong with the test and what to change. -2. Make the changes directly to the test code in the source files. -3. Rebuild: `cmake --build "c:\Telegram\tdesktop\out" --config Debug --target Telegram` -4. If build fails with file-locked errors (C1041, LNK1104), STOP and report. -5. Write the updated test description to `.ai/<feature-name>/test<N+1>.md` explaining what changed and why. -6. Report "TEST_NEEDS_RERUN" so the orchestrator goes back to step 6c. - -### Outcome C: IMPLEMENTATION HAS BUGS -If the test results indicate actual bugs in the implementation (not test issues): -1. Analyze what's wrong with the implementation. -2. Write a fix plan to `.ai/<feature-name>/plan<N>.md`: - - ## Fix Plan - Iteration <N> - ## Problem - <what the test revealed> - - ## Root Cause - <analysis of why the implementation is wrong> - - ## Fix Steps - 1. <specific fix with file path, location, what to change> - 2. ... - -3. Stash the test code (it will be restored later): - - Run `git submodule status` and stash dirty submodules first: - `cd <submodule> && git stash && cd <repo-root>` - - Then: `git stash` -4. Report "IMPLEMENTATION_NEEDS_FIX" so the orchestrator goes to re-implementation. - -When finished, report your verdict clearly as one of: ALL_TESTS_PASS, TEST_NEEDS_RERUN, IMPLEMENTATION_NEEDS_FIX. -``` - -### Orchestrator Loop Logic - -After Phase 5 (build verification) succeeds, you (the orchestrator) run the testing loop: - -``` -Set N = 1 - -LOOP: - 1. Spawn test-plan agent (Step 6a) with iteration N - 2. Spawn test-do agent (Step 6b) with iteration N - 3. Spawn test-run agent (Step 6c) with iteration N - 4. Spawn test-check agent (Step 6d) with iteration N - 5. Read the verdict: - - "ALL_TESTS_PASS" → go to FINISH - - "TEST_NEEDS_RERUN" → - N = N + 1 - go to step 3 (skip 6a and 6b, test code was already updated by test-check) - - "IMPLEMENTATION_NEEDS_FIX" → - Spawn implementation fix agent (see below) - N = N + 1 - go to step 1 (full restart: new commit, stash pop test code, etc.) - 6. Safety: if N > 5, stop and report to user - too many iterations. - -FINISH: - - Stash or revert all test code (#ifdef _DEBUG blocks): - - git submodule status, stash submodules if dirty - - git stash (to save test code separately, user may want it later) - - Update plan.md: change `- [ ] Testing` to `- [x] Testing` - - Report to user -``` - -### Implementation Fix Agent - -When test-check reports IMPLEMENTATION_NEEDS_FIX, spawn this agent: - -``` -You are an implementation fix agent. - -Read these files: -- .ai/<feature-name>/context.md -- .ai/<feature-name>/plan.md -- .ai/<feature-name>/plan<N>.md - The fix plan from test assessment - -Then read the source files mentioned in the fix plan. - -YOUR TASK: Implement the fixes described in plan<N>.md. - -Steps: -1. Read and understand the fix plan. -2. Make the specified code changes. -3. Build: `cmake --build "c:\Telegram\tdesktop\out" --config Debug --target Telegram` -4. Fix any build errors. -5. If build fails with file-locked errors (C1041, LNK1104), STOP and report. - -Rules: -- Only make changes specified in the fix plan -- Follow CLAUDE.md conventions -- Do NOT touch test code or .ai/ files (except plan.md status if relevant) - -When finished, report what was fixed. -``` - ---- - -## Completion - -When the testing loop finishes (ALL_TESTS_PASS or user stops it): -1. Read the final `plan.md` and report full summary to the user. -2. List all files modified/created by the implementation. -3. Summarize test iterations: how many rounds, what was found and fixed. -4. Note that test code is stashed (available via `git stash pop` if needed). -5. Note any remaining concerns. - -## Error Handling - -- If any agent fails or gets stuck, report the issue to the user and ask how to proceed. -- If context.md or plan.md is not written properly by an agent, re-spawn that agent with more specific instructions. -- If build errors persist after agent attempts, report remaining errors to the user. -- If the testing loop exceeds 5 iterations, stop and report - something fundamental may be wrong. -- If the app crashes repeatedly, report to user - may need manual investigation. -- If file-locked build errors occur at ANY point, stop immediately and ask user to close Telegram.exe. diff --git a/.claude/iterate.ps1 b/.claude/iterate.ps1 deleted file mode 100644 index 195c1be6ac..0000000000 --- a/.claude/iterate.ps1 +++ /dev/null @@ -1,343 +0,0 @@ -#!/usr/bin/env pwsh -# Iterative Task Runner -# Runs Claude Code in a loop to complete tasks from a taskplanner-created folder -# -# Usage: .\docs\ai\iterate.ps1 <featurename> [-MaxIterations N] [-Interactive] [-DryRun] [-SingleCommit] [-NoCommit] -# -# Arguments: -# featurename Name of the folder in .ai/ containing prompt.md and tasks.json -# -MaxIterations Maximum iterations before stopping (default: 50) -# -Interactive Pause between iterations for user confirmation (default: auto/no pause) -# -DryRun Show what would be executed without running -# -SingleCommit Don't commit after each task, commit all changes at the end -# -NoCommit Don't commit at all (no per-task commits, no final commit) - -param( - [Parameter(Position=0, Mandatory=$true)] - [string]$FeatureName, - - [int]$MaxIterations = 50, - [switch]$Interactive, - [switch]$DryRun, - [switch]$SingleCommit, - [switch]$NoCommit -) - -$ErrorActionPreference = "Stop" - -$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -$RepoRoot = Resolve-Path (Join-Path $ScriptDir "..\..") -$WorkDir = Join-Path $ScriptDir "work\$FeatureName" -$PromptMd = Join-Path $WorkDir "prompt.md" -$TasksJson = Join-Path $WorkDir "tasks.json" - -$BuildOutputDir = Join-Path $RepoRoot "out\Debug" -$TelegramExe = Join-Path $BuildOutputDir "Telegram.exe" -$TelegramPdb = Join-Path $BuildOutputDir "Telegram.pdb" - -function Format-Duration { - param([int]$Seconds) - - if ($Seconds -lt 60) { - return "${Seconds}s" - } elseif ($Seconds -lt 3600) { - $min = [math]::Floor($Seconds / 60) - $sec = $Seconds % 60 - return "${min}m ${sec}s" - } else { - $hr = [math]::Floor($Seconds / 3600) - $min = [math]::Floor(($Seconds % 3600) / 60) - $sec = $Seconds % 60 - return "${hr}h ${min}m ${sec}s" - } -} - -function Test-BuildFilesUnlocked { - $filesToCheck = @($TelegramExe, $TelegramPdb) - - foreach ($file in $filesToCheck) { - if (Test-Path $file) { - try { - Remove-Item $file -Force -ErrorAction Stop - Write-Host "Removed: $file" -ForegroundColor DarkGray - } - catch { - Write-Host "" - Write-Host "========================================" -ForegroundColor Red - Write-Host " ERROR: Cannot delete build output" -ForegroundColor Red - Write-Host " File is locked: $file" -ForegroundColor Red - Write-Host "" -ForegroundColor Red - Write-Host " Please close Telegram.exe and any" -ForegroundColor Red - Write-Host " debugger, then try again." -ForegroundColor Red - Write-Host "========================================" -ForegroundColor Red - Write-Host "" - return $false - } - } - } - return $true -} - -function Show-ClaudeStream { - param([string]$Line) - - try { - $obj = $Line | ConvertFrom-Json -ErrorAction Stop - - switch ($obj.type) { - "assistant" { - if ($obj.message.content) { - foreach ($block in $obj.message.content) { - if ($block.type -eq "text") { - Write-Host $block.text -ForegroundColor White - } - elseif ($block.type -eq "tool_use") { - $summary = "" - if ($block.input) { - if ($block.input.file_path) { - $summary = $block.input.file_path - } elseif ($block.input.pattern) { - $summary = $block.input.pattern - } elseif ($block.input.command) { - $cmd = $block.input.command - if ($cmd.Length -gt 60) { $cmd = $cmd.Substring(0, 60) + "..." } - $summary = $cmd - } else { - $inputStr = $block.input | ConvertTo-Json -Compress -Depth 1 - if ($inputStr.Length -gt 60) { $inputStr = $inputStr.Substring(0, 60) + "..." } - $summary = $inputStr - } - } - Write-Host "[Tool: $($block.name)] $summary" -ForegroundColor Yellow - } - } - } - } - "user" { - # Tool results - skip verbose output - } - "result" { - Write-Host "`n--- Session Complete ---" -ForegroundColor Cyan - if ($obj.cost_usd) { - Write-Host "Cost: `$$($obj.cost_usd)" -ForegroundColor DarkCyan - } - } - "system" { - # System messages - skip - } - } - } - catch { - # Not valid JSON, skip - } -} - -# Verify feature folder exists -if (-not (Test-Path $WorkDir)) { - Write-Error "Feature folder not found: $WorkDir`nRun '/taskplanner $FeatureName' first to create it." - exit 1 -} - -# Verify required files exist -foreach ($file in @($PromptMd, $TasksJson)) { - if (-not (Test-Path $file)) { - Write-Error "Required file not found: $file" - exit 1 - } -} - -if ($SingleCommit -or $NoCommit) { - $AfterImplementation = @" - - Mark the task completed in tasks.json ("completed": true) - - If new tasks emerged, add them to tasks.json -"@ - $CommitRule = "- Do NOT commit changes after task is done, just mark it as done in tasks.json. Commit will be done when all tasks are complete, separately." -} else { - $AfterImplementation = @" - - Mark the task completed in tasks.json ("completed": true) - - Commit your changes - - If new tasks emerged, add them to tasks.json -"@ - $CommitRule = "" -} - -$Prompt = @" -You are an autonomous coding agent working on: $FeatureName - -Read these files for context: -- .ai/$FeatureName/prompt.md - Detailed instructions and architecture -- .ai/$FeatureName/tasks.json - Task list with completion status - -Do exactly ONE task per iteration. - -## Steps - -1. Read tasks.json and find the most suitable task to implement (it can be first uncompleted task or it can be some task in the middle, if it is better suited to be implemented right now, respecting dependencies) -2. Plan the implementation carefully -3. Implement that ONE task only -4. After successful implementation: -$AfterImplementation - -## Critical Rules - -- Only mark a task complete if you verified the work is done (build passes, etc.) -- If stuck, document the issue in the task's notes field and move on -- Do ONE task per iteration, then stop -- NEVER try to commit files in .ai/ -$CommitRule - -## Completion Signal - -If ALL tasks in tasks.json have "completed": true, output exactly: -===ALL_TASKS_COMPLETE=== -"@ - -$CommitPrompt = @" -You are an autonomous coding agent. All tasks for "$FeatureName" are now complete. - -Your job: Create a single commit with all the changes. - -## Steps - -1. Run git status to see all modified files -2. Run git diff to review the changes -3. Create a commit with a short summary (aim for ~50 chars, max 76 chars) describing what was implemented -4. The commit message should describe the overall feature/fix, not list individual changes - -## Critical Rules - -- NEVER try to commit files in .ai/ -- Use a concise commit message that captures the essence of the work done -"@ - -Write-Host "" -Write-Host "========================================" -ForegroundColor Cyan -Write-Host " Iterative Task Runner" -ForegroundColor Cyan -Write-Host " Feature: $FeatureName" -ForegroundColor Cyan -Write-Host " Max iterations: $MaxIterations" -ForegroundColor Cyan -Write-Host " Mode: $(if ($Interactive) { 'Interactive' } else { 'Auto' })" -ForegroundColor Cyan -Write-Host " Commit: $(if ($NoCommit) { 'None' } elseif ($SingleCommit) { 'Single (at end)' } else { 'Per task' })" -ForegroundColor Cyan -Write-Host " Working directory: $RepoRoot" -ForegroundColor Cyan -Write-Host "========================================" -ForegroundColor Cyan -Write-Host "" - -if ($DryRun) { - Write-Host "[DRY RUN] Would execute with prompt:" -ForegroundColor Yellow - Write-Host $Prompt - Write-Host "" - Write-Host "Feature folder: $WorkDir" -ForegroundColor Yellow - Write-Host "Prompt file: $PromptMd" -ForegroundColor Yellow - Write-Host "Tasks file: $TasksJson" -ForegroundColor Yellow - exit 0 -} - -Push-Location $RepoRoot - -$ScriptStartTime = Get-Date -$IterationTimes = @() - -try { - for ($i = 1; $i -le $MaxIterations; $i++) { - Write-Host "" - Write-Host "========================================" -ForegroundColor Yellow - Write-Host " Iteration $i of $MaxIterations" -ForegroundColor Yellow - Write-Host "========================================" -ForegroundColor Yellow - Write-Host "" - - if (-not (Test-BuildFilesUnlocked)) { - exit 1 - } - - $IterationStartTime = Get-Date - - claude --dangerously-skip-permissions --verbose -p $Prompt --output-format stream-json 2>&1 | ForEach-Object { - Show-ClaudeStream $_ - } - - $IterationEndTime = Get-Date - $IterationDuration = [int]($IterationEndTime - $IterationStartTime).TotalSeconds - $IterationTimes += $IterationDuration - Write-Host "Iteration time: $(Format-Duration $IterationDuration)" -ForegroundColor DarkCyan - - # Check task status after each run - $tasks = Get-Content $TasksJson | ConvertFrom-Json - $incomplete = @($tasks.tasks | Where-Object { -not $_.completed }) - $inProgress = @($tasks.tasks | Where-Object { $_.started -and -not $_.completed }) - - if ($incomplete.Count -eq 0) { - if ($SingleCommit -and -not $NoCommit) { - $i++ - if ($i -le $MaxIterations) { - Write-Host "" - Write-Host "========================================" -ForegroundColor Yellow - Write-Host " Final commit iteration" -ForegroundColor Yellow - Write-Host "========================================" -ForegroundColor Yellow - Write-Host "" - - $CommitStartTime = Get-Date - - claude --dangerously-skip-permissions --verbose -p $CommitPrompt --output-format stream-json 2>&1 | ForEach-Object { - Show-ClaudeStream $_ - } - - $CommitEndTime = Get-Date - $CommitDuration = [int]($CommitEndTime - $CommitStartTime).TotalSeconds - $IterationTimes += $CommitDuration - Write-Host "Commit time: $(Format-Duration $CommitDuration)" -ForegroundColor DarkCyan - } else { - Write-Host "" - Write-Host "========================================" -ForegroundColor Red - Write-Host " Max iterations reached before commit" -ForegroundColor Red - Write-Host " Run manually: git add . && git commit" -ForegroundColor Red - Write-Host "========================================" -ForegroundColor Red - Write-Host "" - exit 1 - } - } - - $TotalTime = [int]((Get-Date) - $ScriptStartTime).TotalSeconds - $AvgTime = if ($IterationTimes.Count -gt 0) { [int](($IterationTimes | Measure-Object -Sum).Sum / $IterationTimes.Count) } else { 0 } - - Write-Host "" - Write-Host "========================================" -ForegroundColor Green - Write-Host " ALL TASKS COMPLETE!" -ForegroundColor Green - Write-Host " Feature: $FeatureName" -ForegroundColor Green - Write-Host " Iterations: $($IterationTimes.Count)" -ForegroundColor Green - Write-Host " Total time: $(Format-Duration $TotalTime)" -ForegroundColor Green - Write-Host " Avg per iteration: $(Format-Duration $AvgTime)" -ForegroundColor Green - Write-Host "========================================" -ForegroundColor Green - Write-Host "" - - exit 0 - } - - Write-Host "" - Write-Host "Remaining tasks: $($incomplete.Count)" -ForegroundColor Cyan - if ($inProgress.Count -gt 0) { - Write-Host "In progress: $($inProgress[0].title)" -ForegroundColor Yellow - } - - if ($Interactive) { - Write-Host "Press Enter to continue, Ctrl+C to stop..." -ForegroundColor Cyan - Read-Host - } else { - Start-Sleep -Seconds 2 - } - } - - $TotalTime = [int]((Get-Date) - $ScriptStartTime).TotalSeconds - $AvgTime = if ($IterationTimes.Count -gt 0) { [int](($IterationTimes | Measure-Object -Sum).Sum / $IterationTimes.Count) } else { 0 } - - Write-Host "" - Write-Host "========================================" -ForegroundColor Red - Write-Host " Max iterations ($MaxIterations) reached" -ForegroundColor Red - Write-Host " Check tasks.json for remaining tasks" -ForegroundColor Red - Write-Host " Total time: $(Format-Duration $TotalTime)" -ForegroundColor Red - Write-Host " Avg per iteration: $(Format-Duration $AvgTime)" -ForegroundColor Red - Write-Host "========================================" -ForegroundColor Red - Write-Host "" - exit 1 -} -finally { - Pop-Location -} diff --git a/AGENTS.md b/AGENTS.md index febf0df740..c87ae5fec8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -129,6 +129,13 @@ Retrying builds wastes time and context. The ONLY fix is for the user to close t - Do not save source, header, build/config, style, or localization files as UTF-8 with BOM. Use UTF-8 without BOM. - When rewriting project text files for normalization, preserve file content otherwise and do not introduce a BOM. +## Commits + +- Subject: one concise, plain-language line summarizing the change, ~50-60 characters, matching the style of recent `git log` subjects. This is usually the entire message. +- Add a short plain-language body only when the subject can't carry it (what was done, not the technical how) — a line or two at most. +- Never add a `Co-Authored-By:` line or any tool/assistant attribution trailer. +- Never add `Autotask:`/attempt or other workflow markers — commits read like normal history. + ## Local Storage Serialization Both app-level (`Core::Settings`) and session-level (`Main::SessionSettings`) use sequential binary serialization via `QDataStream`. Key rules: