diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c9f4405 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,55 @@ +# Changelog + +## 2.1.1 — 2026-07-15 + +Robustness release: script hardening, dynamic branching, and a regression test suite. + +### Fixed + +- **State-directory footgun**: `codex-implement`, `codex-code-review`, and `codex-ask` now ship their own `start`/`resume`/`reset`/`show` wrapper scripts that pin `STATE_DIR` themselves. The old `export STATE_DIR` choreography silently broke when the export and the script call ran in separate shells — worst case, a resume continued the *plan-review* thread with the reviewer model instead of the implementation thread. +- **State-key lookup for free-form labels**: `TRIP-3-release` now derives the code-review state key via the new shared `key.sh` instead of an inline `realpath` pipeline, which mis-keyed unplanned work (free-form labels) and silently downgraded a converged Codex review to the "skipped" fallback. +- **Prompt substitution**: `load_prompt` no longer corrupts prompts containing `&` or backslashes, and no longer loops forever when a substituted value quotes its own placeholder (e.g. implementer notes discussing the templates). +- **Committed session artifacts**: added the missing `state/.gitignore` for `codex-implement` and `codex-ask`, so `git add -A` during release no longer commits thread IDs, reports, and event logs. +- **Stale v1 references**: week-anchor formula location, synthesize template's version-step pointer, tutorial step numbering and its renumber list. + +### Changed + +- **Dynamic base branch**: `TRIP-2-implement`, `TRIP-3-release`, and `TRIP-hotfix` now work from whatever branch is active. The base branch is recorded at branch creation (`branch..trip-base` git config) and the release/hotfix merge returns to it — the `[MAIN_BRANCH]` placeholder is gone. +- **Push is now explicit**: `TRIP-3-release` and `TRIP-hotfix` list the available branches and ask which one to push (base branch recommended), unless the user already specified one. Hotfix previously pushed automatically as part of the merge chain. +- **Hotfix merge policy**: `TRIP-hotfix` now enforces `--ff-only` like the release skill — never a merge commit. +- **Docs**: unified ARCHI.md token thresholds (10–15k target, ~20k ceiling) across README, `TRIP-compact`, and `TRIP-3-release`; added the Bash/Codex CLI/`jq`/GNU-coreutils requirements note; typo fixes. + +### Added + +- `tests/workflow-regression.sh` — regression suite covering prompt substitution (literal `&`/backslash handling, self-referencing placeholder termination), shared key derivation for free-form labels, wrapper state-directory isolation, and state `.gitignore` presence. + +## 2.1.0 — 2026-07-13 + +### Added + +- **`codex-ask` skill**: grounded second opinion from Codex on any matter — architecture calls, debugging hypotheses, research conclusions. Read-only sandbox, threaded per topic, advisory only (no verdict tags, nothing gated). +- **TRIP-research cross-check step**: decision-grade findings are red-teamed via `codex-ask` before being presented; real disagreements are recorded in the memo's Open Questions section. + +## 2.0.0 — 2026-07-13 + +Flow simplified to **Plan → Implement → Release**: review and test moved inside Implement as a testing gate and an automatic Codex review loop. + +### Added + +- **`codex-implement` skill**: implementation delegated to Codex CLI in a workspace-write sandbox, with persistent per-plan threads for phased delegation. +- **`TRIP-3-release` skill**: owns the release ceremony (version, CR promotion, changelogs, docs, commit, tag, ff-merge, push). +- **Testing gate** in `TRIP-2-implement`, with the hard-to-cover policy and coverage-debt ledger. +- **Per-flow Codex model defaults** in `codex-plan-review/scripts/_common.sh` (implementation vs review models), overridable via `CODEX_MODEL` / `CODEX_EFFORT`. + +### Changed + +- `TRIP-review` and `TRIP-test` reborn as on-demand support skills (manual fallback/audit review, deep test-authoring reference). +- `TRIP-upgrade` handles the v1 → v2 rename/migration (`TRIP-3-review` → `TRIP-review`, `TRIP-4-test` → `TRIP-test`). + +## Earlier (1.x) + +- 2026-06-09 — Codex review integration, `TRIP-upgrade` skill, review file separation. +- 2026-02-10 — minor tool call update. +- 2026-02-05 — Mistral Vibe compatibility, contributing section, `AskUserQuestion` emulation skill. +- 2026-02-03 — migration to Skills. +- 2026-01-29 — initial commit. diff --git a/README.md b/README.md index 716ee2c..f526164 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![TRIP Workflow Banner](assets/trip-workflow-banner2.png) -![Version](https://img.shields.io/badge/version-2.1.0-blue) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/PiLastDigit/TRIP-workflow/blob/master/LICENSE) ![Works with](https://img.shields.io/badge/Works_with-grey) [![Claude Code](https://img.shields.io/badge/Claude_Code-E5582B)](https://docs.anthropic.com/en/docs/claude-code) [![Codex CLI](https://img.shields.io/badge/Codex_CLI-10A37F)](https://developers.openai.com/codex/cli/) [![OpenCode](https://img.shields.io/badge/OpenCode-1a3a5c)](https://github.com/sst/opencode) [![Mistral Vibe](https://img.shields.io/badge/Mistral_Vibe-F7D046)](https://github.com/mistralai/mistral-vibe) +![Version](https://img.shields.io/badge/version-2.1.1-blue) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/PiLastDigit/TRIP-workflow/blob/master/LICENSE) ![Works with](https://img.shields.io/badge/Works_with-grey) [![Claude Code](https://img.shields.io/badge/Claude_Code-E5582B)](https://docs.anthropic.com/en/docs/claude-code) [![Codex CLI](https://img.shields.io/badge/Codex_CLI-10A37F)](https://developers.openai.com/codex/cli/) [![OpenCode](https://img.shields.io/badge/OpenCode-1a3a5c)](https://github.com/sst/opencode) [![Mistral Vibe](https://img.shields.io/badge/Mistral_Vibe-F7D046)](https://github.com/mistralai/mistral-vibe) ## What is TRIP? @@ -18,7 +18,7 @@ Even the "simple" ones come with: - 47 different commands & skills to memorize - Sub-agents swarm for God-knows-what -- Mutlti-chapters courses (sometimes paid lol) +- Multi-chapter courses (sometimes paid lol) **TRIP is different.** It's deliberately minimal: @@ -38,6 +38,8 @@ It was kept stupid simple because **the goal is to ship features, not to master ## Getting Started +Requirements for the bundled Codex scripts: Bash, Codex CLI, `jq`, and GNU coreutils (`realpath` and GNU `date`). On Windows, run them from WSL or a shell with those tools installed. + 1. Copy the `skills/` folder contents to your repo's `.claude/skills/` or whatever 2. Run `/TRIP-init [YourProjectName]` 3. Follow the interactive prompts @@ -132,7 +134,7 @@ Exploratory investigation with defined compute level. For feasibility studies an ### `/TRIP-compact` Run this skill to compact ARCHI.md size while preserving relevance, accuracy, and coverage through summarization and restructuring. Token calculator script included. -As a rule of thumb, ARCHI.md should not exceed ~10% of context window. +Aim for 10,000–15,000 tokens; treat ~20,000 as the hard ceiling. ## Multi-Agent: Using Different LLMs at Different Steps @@ -147,7 +149,7 @@ As of mid july 2026, this Fable + GPT5.6 harness combo is absolute peak. ## MCP Servers: Less Is More -Last piece of advise before your new coding quest: Every MCP server you add is extra context, extra latency, and extra confusion. Keep it minimal. The one use case where MCP genuinely shines is **up-to-date documentation**, so your agent stops hallucinating deprecated APIs/whatever. Two servers cover it: [Context7](https://github.com/upstash/context7) for current library & framework docs, and [Exa](https://github.com/exa-labs/exa-mcp-server) for web search when the answer isn't in any doc. No bloat beyond that. +Last piece of advice before your new coding quest: Every MCP server you add is extra context, extra latency, and extra confusion. Keep it minimal. The one use case where MCP genuinely shines is **up-to-date documentation**, so your agent stops hallucinating deprecated APIs/whatever. Two servers cover it: [Context7](https://github.com/upstash/context7) for current library & framework docs, and [Exa](https://github.com/exa-labs/exa-mcp-server) for web search when the answer isn't in any doc. No bloat beyond that. ## Contributing diff --git a/skills/TRIP-2-implement/SKILL.md b/skills/TRIP-2-implement/SKILL.md index e1b72de..253ead4 100644 --- a/skills/TRIP-2-implement/SKILL.md +++ b/skills/TRIP-2-implement/SKILL.md @@ -22,13 +22,15 @@ Implement: $ARGUMENTS ## Step 0: Create a Branch (Pre-Implementation) -**Always** create a dedicated branch before implementing — no need to ask. `TRIP-3-release` merges it back into the main branch with fast-forward, keeping a single clean linear history. +**Always** create a dedicated branch before implementing — no need to ask. The workflow is base-branch agnostic: whatever branch is active right now becomes the **base branch**, and `TRIP-3-release` merges the feature branch back into it with fast-forward, keeping a single clean linear history. ```bash +BASE_BRANCH="$(git branch --show-current)" git checkout -b feat/[short-description] # or fix/[short-description] +git config branch."feat/[short-description]".trip-base "$BASE_BRANCH" ``` -Derive the short description from the plan/feature name. If already on a dedicated branch for this work (e.g., resuming a session), continue on it. +The `trip-base` config entry records where the branch came from so the release step knows where to merge back — no hardcoded main. Derive the short description from the plan/feature name. If already on a dedicated branch for this work (e.g., resuming a session), continue on it. --- @@ -49,8 +51,7 @@ You do NOT write the implementation yourself — delegate it to Codex via the `c Follow-up phases resume the same thread (context retained): ```bash - export STATE_DIR=".claude/skills/codex-implement/state" - bash .claude/skills/codex-plan-review/scripts/resume.sh \ + bash .claude/skills/codex-implement/scripts/resume.sh \ --prompt-file .claude/skills/codex-implement/prompts/continue.tpl \ "Now implement Phase 2" ``` @@ -126,15 +127,9 @@ Always run the Codex code review after the testing gate passes — no confirmati ### Loop -Always export before invoking shared scripts: - -```bash -export STATE_DIR=".claude/skills/codex-code-review/state" -``` - 1. **Start**: ```bash - bash .claude/skills/codex-plan-review/scripts/start.sh \ + bash .claude/skills/codex-code-review/scripts/start.sh \ --prompt-file .claude/skills/codex-code-review/prompts/start.tpl \ "$GATE_SUMMARY" ``` @@ -148,7 +143,7 @@ export STATE_DIR=".claude/skills/codex-code-review/state" 5. **Resume** (re-run the testing gate first — lint, typecheck, affected tests — and build a fresh summary): ```bash - bash .claude/skills/codex-plan-review/scripts/resume.sh \ + bash .claude/skills/codex-code-review/scripts/resume.sh \ --prompt-file .claude/skills/codex-code-review/prompts/resume.tpl \ --notes "Fixed X. Pushed back on Y because Z." \ "$GATE_SUMMARY" @@ -164,7 +159,7 @@ Skip if loop converged on Turn 1 (state file already holds full review). Turn-N state files hold only that turn's delta. After multi-round convergence, produce a consolidated review: ```bash -bash .claude/skills/codex-plan-review/scripts/resume.sh \ +bash .claude/skills/codex-code-review/scripts/resume.sh \ --prompt-file .claude/skills/codex-code-review/prompts/synthesize.tpl \ "Today's date is YYYY-MM-DD" ``` diff --git a/skills/TRIP-3-release/SKILL.md b/skills/TRIP-3-release/SKILL.md index d854af0..50886dd 100644 --- a/skills/TRIP-3-release/SKILL.md +++ b/skills/TRIP-3-release/SKILL.md @@ -60,7 +60,7 @@ Now that week (`a`) and version (`x.y.z`) are known: 1. Compute state file path: ```bash - STATE_KEY="$(realpath | sed 's|^/||; s|/|__|g')" + STATE_KEY="$(bash .claude/skills/codex-plan-review/scripts/key.sh )" STATE_FILE=".claude/skills/codex-code-review/state/${STATE_KEY}.review.txt" ``` @@ -158,25 +158,36 @@ git tag vx.y.z ## Step 11: Merge (fast-forward) -Merge the feature branch back into the main branch, keeping a single clean linear history: +Merge the feature branch back into its **base branch** — the branch it was created from, recorded by `TRIP-2-implement` Step 0 — keeping a single clean linear history. Run as ONE command so the lookup and the merge share a shell: ```bash -git checkout [MAIN_BRANCH] -git merge --ff-only -git branch -d +BASE_BRANCH="$(git config branch..trip-base)" \ + && git checkout "$BASE_BRANCH" \ + && git merge --ff-only \ + && git branch -d ``` -If `--ff-only` fails, the main branch moved during implementation — rebase the feature branch onto it, then retry. **Never create a merge commit.** +If the `trip-base` lookup fails (branch created outside TRIP-2), ask the user which branch to merge into and substitute it for `"$BASE_BRANCH"`. + +If `--ff-only` fails, the base branch moved during implementation — rebase the feature branch onto it, then retry. **Never create a merge commit.** ## Step 12: Push -**Use the `AskUserQuestion` tool** to ask: +**If the user already specified which branch to push** (in the request or earlier in the session), push that branch directly and skip the question. + +Otherwise, list the available branches: + +```bash +git branch --format='%(refname:short)' +``` + +Then **use the `AskUserQuestion` tool** to ask: -- **Question**: "Release vx.y.z is committed, tagged, and merged. Push to remote?" -- **Options**: "Yes, push now" (push branch and tags), "Not yet" (push manually later) +- **Question**: "Release vx.y.z is committed, tagged, and merged. Which branch should I push?" +- **Options**: One option per branch from the list above — the base branch first, marked "(recommended)" — plus "Not yet" (push manually later). If there are too many branches to list, include the most relevant ones; the user can name another via "Other". -**If "Yes"**: +**If a branch is selected**: ```bash -git push && git push --tags +git push origin && git push --tags ``` diff --git a/skills/TRIP-compact/SKILL.md b/skills/TRIP-compact/SKILL.md index 9ee2727..6d59c98 100644 --- a/skills/TRIP-compact/SKILL.md +++ b/skills/TRIP-compact/SKILL.md @@ -10,7 +10,7 @@ You are now in **compaction mode** - intelligently reducing ARCHI.md size while ## Why Compact? -ARCHI.md should not exceed _~20k tokens_. A bloated ARCHI: +Aim for 10,000–15,000 tokens and treat ~20,000 as the hard ceiling. A bloated ARCHI: - Consumes tokens that could be used for actual work - Slows down every command that reads it diff --git a/skills/TRIP-compact/count-tokens.sh b/skills/TRIP-compact/count-tokens.sh old mode 100755 new mode 100644 diff --git a/skills/TRIP-hotfix/SKILL.md b/skills/TRIP-hotfix/SKILL.md index 617086a..36727b8 100644 --- a/skills/TRIP-hotfix/SKILL.md +++ b/skills/TRIP-hotfix/SKILL.md @@ -34,11 +34,17 @@ Before proceeding, confirm this is a genuine hotfix: ## Step 2: Create Hotfix Branch +Branch from the **currently active branch** — the workflow is base-branch agnostic, and Step 8 merges back into whatever branch you started from. Run as ONE command so the steps share a shell: + ```bash -git checkout main && git pull -git checkout -b hotfix/[short-description] +BASE_BRANCH="$(git branch --show-current)" \ + && git pull --ff-only \ + && git checkout -b hotfix/[short-description] \ + && git config branch."hotfix/[short-description]".trip-base "$BASE_BRANCH" ``` +If the active branch has no upstream, drop the `git pull --ff-only` step instead of ignoring its failure. + --- ## Step 3: Minimal Investigation @@ -106,12 +112,37 @@ git add -A && git commit -m "hotfix: [brief description]" ## Step 8: Merge & Tag +Merge back into the base branch recorded in Step 2. Run as ONE command so the lookup and the merge share a shell: + +```bash +BASE_BRANCH="$(git config branch.hotfix/[short-description].trip-base)" \ + && git checkout "$BASE_BRANCH" \ + && git merge --ff-only hotfix/[short-description] \ + && git tag vx.y.z \ + && git branch -d hotfix/[short-description] +``` + +If the `trip-base` lookup fails (branch created outside this skill), ask the user which branch to merge into. If `--ff-only` fails, the base branch moved during the fix — rebase the hotfix branch onto it, then retry. **Never create a merge commit.** + +## Step 8b: Push + +**If the user already specified which branch to push**, push that branch directly and skip the question. + +Otherwise, list the available branches: + +```bash +git branch --format='%(refname:short)' +``` + +Then **use the `AskUserQuestion` tool** to ask: + +- **Question**: "Hotfix vx.y.z is merged and tagged. Which branch should I push?" +- **Options**: One option per branch from the list above — the base branch first, marked "(recommended)" — plus "Not yet" (push manually later). If there are too many branches to list, include the most relevant ones; the user can name another via "Other". + +**If a branch is selected**: + ```bash -git checkout main -git merge hotfix/[short-description] -git tag vx.y.z -git push && git push --tags -git branch -d hotfix/[short-description] +git push origin && git push --tags ``` --- diff --git a/skills/TRIP-init/SKILL.md b/skills/TRIP-init/SKILL.md index a8286aa..0bc9d57 100644 --- a/skills/TRIP-init/SKILL.md +++ b/skills/TRIP-init/SKILL.md @@ -645,7 +645,7 @@ Update Step 2 to reference the actual version file: **B. Week Anchor** -The week Init is run becomes **Week 1** of the project. Capture the anchor date (Monday of the current week) and update the week formula in `TRIP-2-implement`. +The week Init is run becomes **Week 1** of the project. Capture the anchor date (Monday of the current week) and update the week formula in `TRIP-3-release`. Run this to get the anchor date: @@ -687,7 +687,7 @@ Then replace the `[WEEK_ANCHOR_DATE]` placeholder in `TRIP-3-release` Step 1 wit Then update the `[TUTORIAL_STEP]` block in `TRIP-3-release` with the user's context: ```markdown -### Step 7: Tutorial +### Step 8: Tutorial Create `docs/5-tuto/tuto_x.y.z.md` explaining the core principle. @@ -705,6 +705,8 @@ Create `docs/5-tuto/tuto_x.y.z.md` explaining the core principle. - Step 8: README Update → **Step 9**: README Update - Step 9: Commit → **Step 10**: Commit - Step 10: Tag → **Step 11**: Tag +- Step 11: Merge → **Step 12**: Merge +- Step 12: Push → **Step 13**: Push **D. Codex Review Test Commands** @@ -1012,10 +1014,9 @@ Update: Technology Stack, and any affected architectural sections - [ ] `TRIP-1-plan`: `[ADAPT_TO_PROJECT]` markers replaced with actual technical considerations - [ ] `TRIP-1-plan`: Guidance sections replaced with project-specific patterns - [ ] `TRIP-1-plan`: Custom plan sections added (if user requested) - - [ ] `TRIP-2-implement`: Testing gate commands (`[LINT_COMMAND]`, `[TYPECHECK_COMMAND]`, `[TEST_COMMAND]`) replaced with actual commands + - [ ] `TRIP-2-implement` and `TRIP-3-release`: Testing commands (`[LINT_COMMAND]`, `[TYPECHECK_COMMAND]`, `[TEST_COMMAND]`) replaced with actual commands - [ ] `TRIP-3-release`: `[VERSION_FILE]` placeholder replaced - [ ] `TRIP-3-release`: `[WEEK_ANCHOR_DATE]` placeholder replaced - - [ ] `TRIP-3-release`: Standalone-verification commands replaced with actual commands - [ ] `TRIP-3-release`: Tutorial preference configured (if enabled: 5-tuto/ folder created + user context; if disabled: `[TUTORIAL_STEP]` block removed) - [ ] `TRIP-review/checklist.md`: `[ADAPT_TO_PROJECT]` markers replaced with project-specific checklist sections - [ ] `TRIP-review/cr-template.md`: Checklist section names updated to match adapted `checklist.md` diff --git a/skills/TRIP-research/SKILL.md b/skills/TRIP-research/SKILL.md index 874fd83..50174f2 100644 --- a/skills/TRIP-research/SKILL.md +++ b/skills/TRIP-research/SKILL.md @@ -150,8 +150,7 @@ Conduct the research: For **decision-grade findings** — architecture recommendations, technology choices, anything the user will build on (typically compute level `think hard` and above) — red-team the draft conclusion with the `codex-ask` skill before presenting. Skip for quick lookups. ```bash -export STATE_DIR=".claude/skills/codex-ask/state" -bash .claude/skills/codex-plan-review/scripts/start.sh \ +bash .claude/skills/codex-ask/scripts/start.sh \ --prompt-file .claude/skills/codex-ask/prompts/ask.tpl \ "Here is my draft recommendation: . Red-team it: what am I missing, what would you choose instead, and why?" ``` diff --git a/skills/TRIP-upgrade/SKILL.md b/skills/TRIP-upgrade/SKILL.md index 80c2441..da1edfb 100644 --- a/skills/TRIP-upgrade/SKILL.md +++ b/skills/TRIP-upgrade/SKILL.md @@ -243,9 +243,8 @@ For each customized skill, take the **new template** from staging and inject the 2. Replace `[PROJECT_NAME]` with extracted `PROJECT_NAME` 3. Replace `[VERSION_FILE]` with extracted `VERSION_FILE` 4. Replace `[WEEK_ANCHOR_DATE]` with extracted `WEEK_ANCHOR_DATE` -5. Replace `[MAIN_BRANCH]` with the repo's default branch name -6. Replace the standalone-verification commands with the same extracted lint/typecheck/test commands -7. Handle tutorial config: +5. Replace the standalone-verification commands with the same extracted lint/typecheck/test commands +6. Handle tutorial config: - If tutorials were disabled: remove the `[TUTORIAL_STEP]` block - If tutorials were enabled: replace the `[TUTORIAL_STEP]` block with extracted `TUTORIAL_CONFIG` and renumber subsequent steps @@ -281,7 +280,7 @@ After writing all files, run a validation pass. Scan all upgraded skill files for leftover placeholders: ```bash -grep -rn '\[ADAPT_TO_PROJECT\|\[PROJECT_NAME\]\|\[VERSION_FILE\]\|\[WEEK_ANCHOR_DATE\]\|\[TEST_COMMAND\]\|\[LINT_COMMAND\]\|\[TYPECHECK_COMMAND\]\|\[TUTORIAL_STEP\]\|\[MAIN_BRANCH\]' .claude/skills/TRIP-*/ +grep -rn '\[ADAPT_TO_PROJECT\|\[PROJECT_NAME\]\|\[VERSION_FILE\]\|\[WEEK_ANCHOR_DATE\]\|\[TEST_COMMAND\]\|\[LINT_COMMAND\]\|\[TYPECHECK_COMMAND\]\|\[TUTORIAL_STEP\]\|\[MAIN_BRANCH\]' .claude/skills/ ``` If any are found, fill them from context or ask the user. @@ -291,7 +290,7 @@ If any are found, fill them from context or ask the user. - `checklist.md` section names must match `cr-template.md` checklist section names - `codex-code-review/prompts/start.tpl` and `resume.tpl` reference `.claude/skills/TRIP-review/checklist.md` — confirm it exists, and that no template still points at the old `TRIP-3-review/` path - `codex-code-review/prompts/synthesize.tpl` and `codex-code-review/SKILL.md` reference `.claude/skills/TRIP-review/cr-template.md` — confirm it exists -- `TRIP-1-plan` and `TRIP-2-implement` reference `codex-plan-review/scripts/start.sh` and `resume.sh`; `TRIP-2-implement` also references `codex-implement/scripts/start.sh` — confirm they exist +- `TRIP-1-plan` references `codex-plan-review/scripts/`; `TRIP-2-implement` references the local wrappers under `codex-implement/scripts/` and `codex-code-review/scripts/` — confirm they exist ### 5.3 Present Summary diff --git a/skills/codex-ask/SKILL.md b/skills/codex-ask/SKILL.md index 221e52f..f59e5fb 100644 --- a/skills/codex-ask/SKILL.md +++ b/skills/codex-ask/SKILL.md @@ -10,11 +10,7 @@ Free-form second opinion from Codex CLI on **any matter** — architecture decis **Advisory, not authoritative.** Unlike `codex-plan-review` / `codex-code-review`, there are no verdict tags and nothing is gated on the answer: treat the response as one input to your judgment, exactly like a colleague's opinion. Agreement is weak evidence; *disagreement* is a strong signal that something deserves the user's attention. -State persisted per topic label under `.claude/skills/codex-ask/state/` — follow-ups resume the same thread, enabling multi-round discussion. The shared scripts from `codex-plan-review` do all the work; always export first: - -```bash -export STATE_DIR=".claude/skills/codex-ask/state" -``` +State persists per topic label under `.claude/skills/codex-ask/state/`; follow-ups resume the same thread, enabling multi-round discussion. Local wrappers set the discussion state directory before delegating to the shared scripts. ## Arguments @@ -26,19 +22,19 @@ export STATE_DIR=".claude/skills/codex-ask/state" 1. **Start** a discussion: ```bash - bash .claude/skills/codex-plan-review/scripts/start.sh \ + bash .claude/skills/codex-ask/scripts/start.sh \ --prompt-file .claude/skills/codex-ask/prompts/ask.tpl \ "" ``` 2. **Follow up** in the same thread (counterpoints, new evidence): ```bash - bash .claude/skills/codex-plan-review/scripts/resume.sh \ + bash .claude/skills/codex-ask/scripts/resume.sh \ --prompt-file .claude/skills/codex-ask/prompts/followup.tpl \ "" ``` -3. **Reset**: `bash .claude/skills/codex-plan-review/scripts/reset.sh ` — **Show**: `show.sh ` +3. **Reset**: `bash .claude/skills/codex-ask/scripts/reset.sh ` — **Show**: `bash .claude/skills/codex-ask/scripts/show.sh ` ## When to use diff --git a/skills/codex-ask/scripts/reset.sh b/skills/codex-ask/scripts/reset.sh new file mode 100644 index 0000000..bdf0d0d --- /dev/null +++ b/skills/codex-ask/scripts/reset.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/reset.sh" "$@" diff --git a/skills/codex-ask/scripts/resume.sh b/skills/codex-ask/scripts/resume.sh new file mode 100644 index 0000000..67d3250 --- /dev/null +++ b/skills/codex-ask/scripts/resume.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/resume.sh" "$@" diff --git a/skills/codex-ask/scripts/show.sh b/skills/codex-ask/scripts/show.sh new file mode 100644 index 0000000..a06e375 --- /dev/null +++ b/skills/codex-ask/scripts/show.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/show.sh" "$@" diff --git a/skills/codex-ask/scripts/start.sh b/skills/codex-ask/scripts/start.sh new file mode 100644 index 0000000..25def94 --- /dev/null +++ b/skills/codex-ask/scripts/start.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/start.sh" "$@" diff --git a/skills/codex-ask/state/.gitignore b/skills/codex-ask/state/.gitignore new file mode 100644 index 0000000..23e2664 --- /dev/null +++ b/skills/codex-ask/state/.gitignore @@ -0,0 +1,4 @@ +# Per-topic Codex discussion state (thread ids, answers, and event logs). +# Local-only; these are session artifacts, not source files. +* +!.gitignore diff --git a/skills/codex-code-review/SKILL.md b/skills/codex-code-review/SKILL.md index 5dd6373..285132d 100644 --- a/skills/codex-code-review/SKILL.md +++ b/skills/codex-code-review/SKILL.md @@ -10,11 +10,7 @@ Iterative code review via Codex CLI on uncommitted changes. Codex reads the plan Review output stays in `state/.review.txt` — not `docs/3-code-review/`. Promotion to `docs/3-code-review/CR_wa_vx.y.z.md` happens after convergence, not per-turn. -State persisted under `.claude/skills/codex-code-review/state/.{thread,review.txt,events.ndjson}`. Shared scripts live under `.claude/skills/codex-plan-review/scripts/`; always export before invoking: - -```bash -export STATE_DIR=".claude/skills/codex-code-review/state" -``` +State persists under `.claude/skills/codex-code-review/state/.{thread,review.txt,events.ndjson}`. Local wrappers set the code-review state directory before delegating to the shared scripts. ## Arguments @@ -27,12 +23,12 @@ export STATE_DIR=".claude/skills/codex-code-review/state" 1. **Parse `$ARGUMENTS`**: extract action (`reset`/`show`/auto) and target. 2. **Auto** — try `start.sh` first (exit code 2 = thread exists -> use `resume.sh`): - - **Start**: `bash .claude/skills/codex-plan-review/scripts/start.sh --prompt-file .claude/skills/codex-code-review/prompts/start.tpl [extra]` - - **Resume**: `bash .claude/skills/codex-plan-review/scripts/resume.sh --prompt-file .claude/skills/codex-code-review/prompts/resume.tpl [extra]` + - **Start**: `bash .claude/skills/codex-code-review/scripts/start.sh --prompt-file .claude/skills/codex-code-review/prompts/start.tpl [extra]` + - **Resume**: `bash .claude/skills/codex-code-review/scripts/resume.sh --prompt-file .claude/skills/codex-code-review/prompts/resume.tpl [extra]` -3. **Reset**: `bash .claude/skills/codex-plan-review/scripts/reset.sh ` +3. **Reset**: `bash .claude/skills/codex-code-review/scripts/reset.sh ` -4. **Show**: `bash .claude/skills/codex-plan-review/scripts/show.sh ` +4. **Show**: `bash .claude/skills/codex-code-review/scripts/show.sh ` 5. **Parse trailing tag**: - `APPROVED` — propose post-convergence steps. diff --git a/skills/codex-code-review/prompts/synthesize.tpl b/skills/codex-code-review/prompts/synthesize.tpl index ef20e73..aedd805 100644 --- a/skills/codex-code-review/prompts/synthesize.tpl +++ b/skills/codex-code-review/prompts/synthesize.tpl @@ -9,7 +9,7 @@ Read `.claude/skills/TRIP-review/cr-template.md` and produce output conforming t Fill-in guide: - **Title**: feature/change name from `{{TARGET}}` - **Review Date**: today's date (YYYY-MM-DD) -- **Version**: leave as `` (requester fills from TRIP-2 Step 2) +- **Version**: leave as `` (requester fills from TRIP-3 Step 2) - **Files Reviewed**: from `git diff --name-only HEAD` - **Plan**: `{{TARGET}}` if path under `docs/1-plans/`, else "no plan — unplanned change" - **Findings**: every finding from all rounds with `file:line` and disposition diff --git a/skills/codex-code-review/scripts/reset.sh b/skills/codex-code-review/scripts/reset.sh new file mode 100644 index 0000000..bdf0d0d --- /dev/null +++ b/skills/codex-code-review/scripts/reset.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/reset.sh" "$@" diff --git a/skills/codex-code-review/scripts/resume.sh b/skills/codex-code-review/scripts/resume.sh new file mode 100644 index 0000000..67d3250 --- /dev/null +++ b/skills/codex-code-review/scripts/resume.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/resume.sh" "$@" diff --git a/skills/codex-code-review/scripts/show.sh b/skills/codex-code-review/scripts/show.sh new file mode 100644 index 0000000..a06e375 --- /dev/null +++ b/skills/codex-code-review/scripts/show.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/show.sh" "$@" diff --git a/skills/codex-code-review/scripts/start.sh b/skills/codex-code-review/scripts/start.sh new file mode 100644 index 0000000..25def94 --- /dev/null +++ b/skills/codex-code-review/scripts/start.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/start.sh" "$@" diff --git a/skills/codex-implement/SKILL.md b/skills/codex-implement/SKILL.md index 2795161..7c1ab31 100644 --- a/skills/codex-implement/SKILL.md +++ b/skills/codex-implement/SKILL.md @@ -8,11 +8,7 @@ argument-hint: " [instructions] | reset | show .{thread,review.txt,events.ndjson}` (the `.review.txt` file holds Codex's implementation **report** — the naming comes from the shared helpers). `resume`/`reset`/`show` reuse the shared scripts from `codex-plan-review`; always export before invoking them: - -```bash -export STATE_DIR=".claude/skills/codex-implement/state" -``` +State persists under `.claude/skills/codex-implement/state/.{thread,review.txt,events.ndjson}` (the `.review.txt` file holds Codex's implementation **report**; the naming comes from the shared helpers). The local wrappers set the correct state directory before delegating to the shared scripts. ## Arguments @@ -27,11 +23,11 @@ export STATE_DIR=".claude/skills/codex-implement/state" 2. **Auto** — try `start.sh` first (exit code 2 = thread exists → use `resume.sh`): - **Start**: `bash .claude/skills/codex-implement/scripts/start.sh --prompt-file .claude/skills/codex-implement/prompts/implement.tpl [instructions]` - - **Resume** (next phase / additional scope): `bash .claude/skills/codex-plan-review/scripts/resume.sh --prompt-file .claude/skills/codex-implement/prompts/continue.tpl [instructions]` + - **Resume** (next phase / additional scope): `bash .claude/skills/codex-implement/scripts/resume.sh --prompt-file .claude/skills/codex-implement/prompts/continue.tpl [instructions]` -3. **Reset**: `bash .claude/skills/codex-plan-review/scripts/reset.sh ` +3. **Reset**: `bash .claude/skills/codex-implement/scripts/reset.sh ` -4. **Show**: `bash .claude/skills/codex-plan-review/scripts/show.sh ` +4. **Show**: `bash .claude/skills/codex-implement/scripts/show.sh ` 5. **Parse trailing tag** of the report: - `IMPLEMENTATION_COMPLETE` — hand control back to the requester's self-review (TRIP-2). diff --git a/skills/codex-implement/scripts/reset.sh b/skills/codex-implement/scripts/reset.sh new file mode 100644 index 0000000..bdf0d0d --- /dev/null +++ b/skills/codex-implement/scripts/reset.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/reset.sh" "$@" diff --git a/skills/codex-implement/scripts/resume.sh b/skills/codex-implement/scripts/resume.sh new file mode 100644 index 0000000..67d3250 --- /dev/null +++ b/skills/codex-implement/scripts/resume.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/resume.sh" "$@" diff --git a/skills/codex-implement/scripts/show.sh b/skills/codex-implement/scripts/show.sh new file mode 100644 index 0000000..a06e375 --- /dev/null +++ b/skills/codex-implement/scripts/show.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" +exec bash "$SCRIPT_DIR/../../codex-plan-review/scripts/show.sh" "$@" diff --git a/skills/codex-implement/scripts/start.sh b/skills/codex-implement/scripts/start.sh old mode 100755 new mode 100644 index 4e6f1af..202b4dc --- a/skills/codex-implement/scripts/start.sh +++ b/skills/codex-implement/scripts/start.sh @@ -14,9 +14,10 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# Default state to THIS skill's directory (shared _common.sh would -# otherwise default to codex-plan-review's state). -STATE_DIR="${STATE_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)/state}" +# Pin state to THIS skill's directory, like the wrapper scripts do — +# unconditionally, so a stale STATE_DIR in the environment can't +# redirect implementation state (or flip the model selection). +STATE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/state" export STATE_DIR # shellcheck source=../../codex-plan-review/scripts/_common.sh source "$SCRIPT_DIR/../../codex-plan-review/scripts/_common.sh" diff --git a/skills/codex-implement/state/.gitignore b/skills/codex-implement/state/.gitignore new file mode 100644 index 0000000..8865ae4 --- /dev/null +++ b/skills/codex-implement/state/.gitignore @@ -0,0 +1,4 @@ +# Per-target Codex implementation state (thread ids, reports, and event logs). +# Local-only; these are session artifacts, not source files. +* +!.gitignore diff --git a/skills/codex-plan-review/scripts/_common.sh b/skills/codex-plan-review/scripts/_common.sh old mode 100755 new mode 100644 index d59807e..998ded5 --- a/skills/codex-plan-review/scripts/_common.sh +++ b/skills/codex-plan-review/scripts/_common.sh @@ -5,8 +5,7 @@ set -euo pipefail SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -# STATE_DIR can be overridden by the caller (e.g., codex-code-review -# exports its own state path before invoking the shared scripts). +# STATE_DIR can be overridden by a consumer wrapper (e.g., codex-code-review). # Default falls back to the script's own skill directory. : "${STATE_DIR:=$SKILL_DIR/state}" export STATE_DIR @@ -68,12 +67,20 @@ load_prompt() { echo "error: prompt template not found: $tpl" >&2 return 1 fi - awk -v target="${TARGET-}" -v extra="${EXTRA_PROMPT-}" -v notes="${IMPLEMENTER_NOTES-}" ' + awk ' + function replace_literal(text, needle, replacement, out, position) { + out = "" + while ((position = index(text, needle)) != 0) { + out = out substr(text, 1, position - 1) replacement + text = substr(text, position + length(needle)) + } + return out text + } { - gsub(/\{\{TARGET\}\}/, target) - gsub(/\{\{EXTRA_PROMPT\}\}/, extra) - gsub(/\{\{IMPLEMENTER_NOTES\}\}/, notes) - print + line = replace_literal($0, "{{TARGET}}", ENVIRON["TARGET"]) + line = replace_literal(line, "{{EXTRA_PROMPT}}", ENVIRON["EXTRA_PROMPT"]) + line = replace_literal(line, "{{IMPLEMENTER_NOTES}}", ENVIRON["IMPLEMENTER_NOTES"]) + print line } ' "$tpl" } diff --git a/skills/codex-plan-review/scripts/key.sh b/skills/codex-plan-review/scripts/key.sh new file mode 100644 index 0000000..b5bd977 --- /dev/null +++ b/skills/codex-plan-review/scripts/key.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=_common.sh +source "$SCRIPT_DIR/_common.sh" + +if [ $# -ne 1 ]; then + echo "usage: key.sh " >&2 + exit 64 +fi + +target_key "$1" diff --git a/skills/codex-plan-review/scripts/reset.sh b/skills/codex-plan-review/scripts/reset.sh old mode 100755 new mode 100644 diff --git a/skills/codex-plan-review/scripts/resume.sh b/skills/codex-plan-review/scripts/resume.sh old mode 100755 new mode 100644 diff --git a/skills/codex-plan-review/scripts/show.sh b/skills/codex-plan-review/scripts/show.sh old mode 100755 new mode 100644 diff --git a/skills/codex-plan-review/scripts/start.sh b/skills/codex-plan-review/scripts/start.sh old mode 100755 new mode 100644 diff --git a/tests/workflow-regression.sh b/tests/workflow-regression.sh new file mode 100644 index 0000000..b761c3d --- /dev/null +++ b/tests/workflow-regression.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TMP_DIR"' EXIT + +fail() { + echo "FAIL: $*" >&2 + exit 1 +} + +assert_eq() { + local expected="$1" + local actual="$2" + local label="$3" + if [ "$actual" != "$expected" ]; then + printf 'FAIL: %s\nexpected: %s\nactual: %s\n' "$label" "$expected" "$actual" >&2 + exit 1 + fi +} + +test_prompt_substitution_preserves_literal_text() { + local template="$TMP_DIR/prompt.tpl" + printf 'Target={{TARGET}}\nExtra={{EXTRA_PROMPT}}\nNotes={{IMPLEMENTER_NOTES}}\n' > "$template" + + export STATE_DIR="$TMP_DIR/state" + export TARGET='docs/C:\work\plan & notes.md' + export EXTRA_PROMPT='Keep A & B; preserve C:\temp\file.' + export IMPLEMENTER_NOTES='Fixed X & Y; left D:\repo\case unchanged.' + + # shellcheck source=../skills/codex-plan-review/scripts/_common.sh + source "$ROOT/skills/codex-plan-review/scripts/_common.sh" + local actual + actual="$(load_prompt "$template")" + local expected + expected=$'Target=docs/C:\\work\\plan & notes.md\nExtra=Keep A & B; preserve C:\\temp\\file.\nNotes=Fixed X & Y; left D:\\repo\\case unchanged.' + assert_eq "$expected" "$actual" "prompt substitution must preserve ampersands and backslashes" +} + +test_prompt_substitution_terminates_when_value_quotes_placeholder() { + local template="$TMP_DIR/self-ref.tpl" + local runner="$TMP_DIR/self-ref-runner.sh" + printf 'Notes={{IMPLEMENTER_NOTES}}\n' > "$template" + + # Run in a subprocess under timeout: a regression here is an infinite + # loop, which must fail the test rather than hang the suite. + cat > "$runner" < "$state/$label.thread" + printf 'report-%s\n' "$skill" > "$state/$label.review.txt" + output="$(bash "$sandbox/skills/$skill/scripts/show.sh" "$label")" + grep -q "report-$skill" <<< "$output" || fail "$skill show wrapper read the wrong state directory" + done +} + +test_runtime_state_is_ignored() { + local skill + for skill in codex-implement codex-ask; do + [ -f "$ROOT/skills/$skill/state/.gitignore" ] || fail "$skill state directory needs a .gitignore" + grep -qx '\*' "$ROOT/skills/$skill/state/.gitignore" || fail "$skill state .gitignore must ignore runtime files" + grep -qx '!\.gitignore' "$ROOT/skills/$skill/state/.gitignore" || fail "$skill state .gitignore must keep itself" + done +} + +test_prompt_substitution_preserves_literal_text +test_prompt_substitution_terminates_when_value_quotes_placeholder +test_key_helper_matches_shared_target_key +test_consumer_show_wrappers_use_their_own_state +test_runtime_state_is_ignored + +echo "workflow regression tests passed"