From 3e42e5757868b45b92c38a9176e961bb70806a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20S=C3=A1nchez?= Date: Thu, 1 Oct 2026 21:40:16 -0500 Subject: [PATCH 1/7] fix: /upgrade offers the modules a project doesn't have /upgrade updated only installed modules and never offered the others, so a project adopted before modules existed would never be offered the dispatcher hub, the ClickUp integration, or the GitHub harness. It now lists the missing modules, recommends from the repository's facts (the same rules as /adopt), and installs the chosen ones in the same upgrade PR with their customize steps. Choosing parallel-agents moves the upgrade into a worktree of its own, so the main checkout starts as a clean hub. Plugin 0.2.4. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 10 +++++ README.md | 6 ++- docs/UPGRADING.md | 11 +++++ evals/static/check-skills.sh | 3 +- .../.claude-plugin/plugin.json | 2 +- plugins/aplyca-framework/README.md | 2 +- .../aplyca-framework/skills/upgrade/SKILL.md | 41 ++++++++++++++++--- 7 files changed, 65 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6daa046..5d49158 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,16 @@ For each entry, **Upgrade impact** classifies the change against the [three-buck ## Unreleased +### `/upgrade` offers the modules a project doesn't have + +`/upgrade` updated only the modules a project already had and never offered the others, so a project +adopted before modules existed would never be offered the dispatcher hub (`parallel-agents`), the +ClickUp integration, or the GitHub harness. It now lists the missing modules, recommends the ones +the repository's facts support (the same rules as `/adopt`), and installs the chosen ones in the same +upgrade pull request with their customize steps. Choosing `parallel-agents` moves the upgrade into a +worktree of its own, so the main checkout starts as a clean hub (`aplyca-framework` 0.2.4). +**Upgrade impact:** framework-internal; update the plugin. + ### Test first in every lane; the hub enforced ([0014](docs/decisions/0014-test-first-in-every-lane.md); [0008](docs/decisions/0008-dispatcher-and-worker-worktrees.md), addendum) diff --git a/README.md b/README.md index cf0d20c..1a8ff6b 100644 --- a/README.md +++ b/README.md @@ -91,8 +91,10 @@ the order to upgrade in from an older baseline; if your settings pin a model ID, 2. **Run `/upgrade`** in the adopted project. It reads the baseline stamp (``) and diffs the framework from that version to the latest. It sorts every changed file into overwrite, merge, or additive, applies the - CHANGELOG migration steps, and shows you the plan before changing anything. Then it updates the - files — your project-specific content stays — re-stamps, and prepares a draft pull request. + CHANGELOG migration steps, and offers the optional modules the project doesn't have yet — the + dispatcher hub (`parallel-agents`) among them. It shows you the plan before changing anything. + Then it updates the files — your project-specific content stays — installs the modules you chose, + re-stamps, and prepares a draft pull request. 3. **Review the pull request** and run the verification in [docs/UPGRADING.md](docs/UPGRADING.md): valid settings, hooks that fire, both instruction files loading, a smoke test of a changed skill. diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md index 8283e7c..bcc845c 100644 --- a/docs/UPGRADING.md +++ b/docs/UPGRADING.md @@ -233,6 +233,17 @@ Then decide how far to go: the spec-folder workflow (new skills, templates, `spe main benefit. Existing single-file specs stay as they are; new work uses folders, and a legacy spec moves into a folder the next time it changes. +### "We adopted before the modules existed" + +Your stamp has no `modules:` part, so nothing optional was installed. `/upgrade` lists the modules +you don't have and recommends the ones your repository's facts support — `parallel-agents` when +several agent sessions may work at once (each task gets its own worktree, branch, pull request, and +session; the main checkout only dispatches), `clickup` when requirements arrive as ClickUp tasks, +`github` on GitHub, `git-hooks` for local gates. The ones you choose join the same upgrade pull +request, with their customize steps. Choosing `parallel-agents` moves the upgrade itself into a +worktree, so the main checkout starts as a clean hub. By hand: `cp -R modules//files/.` (no +overwrite) and follow its `MODULE.md`. + ### "I just want one new skill" (e.g. `/triage`) You don't need a full upgrade. Cherry-pick the skill directory: diff --git a/evals/static/check-skills.sh b/evals/static/check-skills.sh index 94b79cd..355e6a9 100755 --- a/evals/static/check-skills.sh +++ b/evals/static/check-skills.sh @@ -612,8 +612,9 @@ check_practices() { file_contains "$SKELETON/.claude/rules/testing.md" '^## Red, then green — every change, in every lane' || missing+=("testing rule: red then green in every lane") grep -q 'protect-hub.sh' "$SETTINGS" || missing+=("settings.json: protect-hub hook") file_contains "$HOOKS_DIR/config.sh" '^HUB_READONLY=' || missing+=("config.sh: HUB_READONLY") + file_contains "$REPO_ROOT/plugins/aplyca-framework/skills/upgrade/SKILL.md" "Offer the modules the project doesn't have" || missing+=("/upgrade: offers missing modules") if [ ${#missing[@]} -eq 0 ]; then - pass "practices: signal-first debugging, question rounds, glossary, merge danger, test independence, decision threshold, handoff, worktree roles, portable worktree defaults, test-first in every lane, the hub enforced" + pass "practices: signal-first debugging, question rounds, glossary, merge danger, test independence, decision threshold, handoff, worktree roles, portable worktree defaults, test-first in every lane, the hub enforced, /upgrade offers modules" else fail "practices: missing" "${missing[*]}" fi diff --git a/plugins/aplyca-framework/.claude-plugin/plugin.json b/plugins/aplyca-framework/.claude-plugin/plugin.json index f248f01..950cbd2 100644 --- a/plugins/aplyca-framework/.claude-plugin/plugin.json +++ b/plugins/aplyca-framework/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "aplyca-framework", "description": "Installer and upgrader for the Aplyca Agentic Development Framework. /adopt bootstraps a repository for agentic development — skeleton, optional modules (GitHub harness, git hooks, parallel-agent worktrees), guardrail hooks, verified facts; /upgrade syncs an adopted repository to a newer skeleton version; /cost-report shows what agent sessions on a project cost, from local transcripts. The framework itself ships as committed files in each repo (AGENTS.md standard, multi-tool); this plugin is the tooling that installs and maintains them.", - "version": "0.2.3", + "version": "0.2.4", "author": { "name": "Aplyca", "email": "dev@aplyca.com" diff --git a/plugins/aplyca-framework/README.md b/plugins/aplyca-framework/README.md index 7c095b7..bdaa7a9 100644 --- a/plugins/aplyca-framework/README.md +++ b/plugins/aplyca-framework/README.md @@ -21,7 +21,7 @@ claude plugin install aplyca-framework@aplyca | Skill | Purpose | |---|---| | `/adopt` | Bootstrap a repo: inspect it (stack, commands, branching model, tracker, Git host), copy the skeleton and the [optional modules](../../modules/README.md) you choose, fill placeholders from verified repo facts, configure the guardrail hooks, record the adoption as PDR-0001, stamp the baseline SHA and modules, verify (settings schema, hook smoke tests, the `@AGENTS.md` import), and prepare a draft adoption PR. On an already-adopted repo it adds modules. Automates [docs/SETUP.md](../../docs/SETUP.md). | -| `/upgrade` | Sync an adopted repo — skeleton and installed modules — to a newer version via the three-bucket taxonomy, OLD_SHA → NEW_SHA discipline, and the changelog's migration steps. Automates [docs/UPGRADING.md](../../docs/UPGRADING.md). | +| `/upgrade` | Sync an adopted repo — skeleton and installed modules — to a newer version via the three-bucket taxonomy, OLD_SHA → NEW_SHA discipline, and the changelog's migration steps, and offer the modules it doesn't have yet (installed in the same pull request when chosen). Automates [docs/UPGRADING.md](../../docs/UPGRADING.md). | | `/cost-report` | What agent sessions on a project cost — calls, active time, context size, tokens, estimated cost — from Claude Code's local transcripts, with the expensive patterns flagged (long context, pauses past the cache lifetime, browser loops, spec-heavy small changes). Read-only; nothing leaves the machine. See the skeleton's [COST-MODEL.md](../../skeleton/docs/COST-MODEL.md). | `/adopt` and `/upgrade` work branch-and-PR only — they never commit to a default branch, and diff --git a/plugins/aplyca-framework/skills/upgrade/SKILL.md b/plugins/aplyca-framework/skills/upgrade/SKILL.md index 82bc903..e5f1c67 100644 --- a/plugins/aplyca-framework/skills/upgrade/SKILL.md +++ b/plugins/aplyca-framework/skills/upgrade/SKILL.md @@ -1,6 +1,6 @@ --- name: upgrade -description: Upgrade a repository that already adopted the Aplyca framework skeleton (and any optional modules) to a newer version, using the three-bucket file taxonomy (overwrite / merge / project-owned), OLD_SHA → NEW_SHA diff discipline, and the changelog's migration steps. Use when asked to upgrade, sync, or update the agentic framework or skeleton in a repo. +description: Upgrade a repository that already adopted the Aplyca framework skeleton (and any optional modules) to a newer version, using the three-bucket file taxonomy (overwrite / merge / project-owned), OLD_SHA → NEW_SHA diff discipline, and the changelog's migration steps — and offer the optional modules the project doesn't have yet. Use when asked to upgrade, sync, or update the agentic framework or skeleton in a repo. --- # Upgrade an adopted repository to a newer skeleton version @@ -19,6 +19,7 @@ blind sync. (`scripts/agent/worktree-new.sh chore/skeleton-upgrade- --no-start`) — and if you were started in the main checkout, say so and stop. - **Plan before touching.** No file is modified until the user approves the per-file plan. +- **Modules are offered, never imposed.** Recommend the ones the facts support; the developer chooses. - **An upgrade needs a nameable benefit.** If the user can't name one, say so and suggest cherry-picking the one or two changes they actually want. @@ -47,6 +48,26 @@ Read `CHANGELOG.md` entries between OLD_SHA and NEW_SHA. Each entry's **Upgrade changes into the buckets below, and some entries carry **Migration** steps that must happen even for files the project customized. Summarize for the user what the upgrade brings before doing anything. +**Offer the modules the project doesn't have.** List the modules at NEW_SHA (`modules/README.md`) +that aren't installed — a project adopted before modules existed has none — and recommend from the +repository's facts, by the same rules as `/adopt` Step 3: + +- `github` — the repository is on GitHub +- `git-hooks` — the team wants local gates that every git client runs +- `clickup` — requirements arrive as ClickUp tasks (`app.clickup.com` links in pull requests, commits, + or the README; a `clickup` server in `.mcp.json`) +- `parallel-agents` — several agent sessions may work on the repository at once: each task gets its + own worktree, branch, pull request, and session, and the main checkout only dispatches + +Say why each recommendation fits, and what each one costs. The chosen ones join this upgrade. + +**If the developer chooses `parallel-agents`,** do the whole upgrade in a worktree of its own, created +with plain git since the module's script isn't there yet: +`git worktree add -b chore/skeleton-upgrade- ../chore-skeleton-upgrade- `. +Edit files under that path and run git with `-C `. Once the module is installed, the +protect-hub hook stops edits in the main checkout, so the main checkout stays a clean hub from the +first commit. + ## Step 3 — Classify every changed file `git -C diff --name-status OLD_SHA NEW_SHA -- skeleton/ modules//files/` @@ -59,6 +80,12 @@ per the taxonomy in `docs/UPGRADING.md`: | **Merge required** | `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `.claude/settings.json`, `.claude/hooks/config.sh`, customizable rules, `.claudeignore`, `docs/CONSTITUTION.md`, `specs/README.md`, `docs/process/README.md`, `docs/reference/README.md`, `docs/TRACKER-INTEGRATION.md`, module config (`worktree.conf`, the PR template, `branch-policy.yml`, `.githooks/pre-push`) | 3-way merge: reapply the project's customizations on top of the new template | | **Project-owned** | Spec folders and legacy specs, ADRs, PDRs, project docs, `docs/reference/*` pages, everything the team authored | Never touched | +**Newly chosen modules** are **additive**: copy `modules//files/` at NEW_SHA without overwriting +(merge a collision such as an existing PR template), except `clickup`, which installs with +`modules/clickup/install.sh `. Each module's `MODULE.md` § Customize steps join the plan's +checklist — for `parallel-agents`, `worktree.conf` (only `BASE_BRANCH` and `SETUP_CMD` unless worktrees +run a server) and the dispatcher line in `AGENTS.md` § Delivery rules. + Files deleted upstream: propose deletion only if the target's copy is unmodified from OLD_SHA; otherwise flag for the user. Files that moved (e.g. `specs/_template.md` → `specs/_templates/`) follow the changelog's migration notes. @@ -66,7 +93,7 @@ follow the changelog's migration notes. ## Step 4 — Present the plan One table: `file → bucket → action → risk note`, plus the changelog's migration steps as their own -checklist. For merge-required files, show which customizations were detected (diff of the target file +checklist, and the newly chosen modules with their customize steps. For merge-required files, show which customizations were detected (diff of the target file vs the OLD_SHA version) and confirm they will survive. Wait for approval. ## Step 5 — Execute @@ -75,14 +102,18 @@ vs the OLD_SHA version) and confirm they will survive. Wait for approval. - Bucket 2: apply the new template, then reapply each detected customization; where the new template restructured a section, place the customization where it now belongs and flag it in the PR body. - Migration steps from the changelog, in order. -- Restamp: `Skeleton source:` → `NEW_SHA () · modules: `. +- Newly chosen modules: copy or install them, then their customize steps. +- Restamp: `Skeleton source:` → `NEW_SHA () · modules: ` — the list includes the new ones. ## Step 6 — Verify and deliver 1. Run the verification from `/adopt` Step 6: settings JSON valid with nested hook entries; hook smoke tests (sample events piped to each script); `@AGENTS.md` import present; skill frontmatter - uses hyphenated keys only. Re-run the target's lint and tests if config files changed. + uses hyphenated keys only. Re-run the target's lint and tests if config files changed. For a newly + installed module, its own check: `scripts/agent/worktree-ls.sh` lists the worktrees + (`parallel-agents`); `.mcp.json` and `.claude/settings.json` parse (`clickup`); the PR template + exists (`github`); `.githooks/pre-push` is executable and `core.hooksPath` is documented (`git-hooks`). 2. Commit with a `docs:` or `chore:` prefix, e.g. `chore: upgrade framework skeleton OLD_SHA → NEW_SHA`. 3. PR body: changelog summary, the plan table as executed, migration steps done, customizations - reapplied, anything needing human judgment. + reapplied, the modules added and why, anything needing human judgment. 4. Push and open the PR **as a draft, only after the user approves**. From d89c68338677d1fb458796bfcd71afde80037772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20S=C3=A1nchez?= Date: Thu, 1 Oct 2026 21:46:10 -0500 Subject: [PATCH 2/7] docs: install the plugin for one project only Show the --scope project (committed, offered to the team) and --scope local (only you) installs next to the default user-wide one, and why a dispatcher-hub project should use project scope: committed settings reach every task's worktree, a local install exists only where it ran. /adopt keeps the entries a project-scope install already wrote. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 3 +++ README.md | 15 +++++++++++---- docs/SETUP.md | 5 +++++ plugins/aplyca-framework/README.md | 17 ++++++++++++++++- plugins/aplyca-framework/skills/adopt/SKILL.md | 3 ++- 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d49158..20090be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ ClickUp integration, or the GitHub harness. It now lists the missing modules, re the repository's facts support (the same rules as `/adopt`), and installs the chosen ones in the same upgrade pull request with their customize steps. Choosing `parallel-agents` moves the upgrade into a worktree of its own, so the main checkout starts as a clean hub (`aplyca-framework` 0.2.4). +The install docs (README, plugin README, `docs/SETUP.md`) now show installing the plugin for one +project only (`--scope project`, committed for the team, or `--scope local`), and why a hub project +should use `project`; `/adopt` keeps those settings when it merges `.claude/settings.json`. **Upgrade impact:** framework-internal; update the plugin. ### Test first in every lane; the hub enforced diff --git a/README.md b/README.md index 1a8ff6b..dd4891b 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,19 @@ Most of what's here was proven in real client projects first — some built on t ### With Claude Code — the installer plugin (recommended) -1. **Install the plugin** — once per machine: +1. **Install the plugin** — for every project on your machine: ```bash claude plugin marketplace add aplyca/AgenticDevelopmentFramework claude plugin install aplyca-framework@aplyca ``` + Or only for this project: run both commands from the project's folder with `--scope project` + (recorded in its committed `.claude/settings.json`, so teammates are offered the plugin too) or + `--scope local` (only you, recorded in the git-ignored `.claude/settings.local.json`). In a + project that uses the dispatcher hub, choose `project`: committed settings reach every task's + worktree, while a local install exists only in the checkout where you ran it. + 2. **Run `/adopt`** in the project. It inspects the repository (stack, commands, branching model, tracker, Git host) and asks which [optional modules](modules/README.md) you want. Then it copies the skeleton, fills the placeholders from verified repository facts only, and configures the guardrail @@ -54,9 +60,10 @@ Most of what's here was proven in real client projects first — some built on t signs in once through `/mcp`. Then review and merge the pull request like any change. 4. **Optional — the whole team:** let `/adopt` register the marketplace in the project's `.claude/settings.json`, so every teammate is offered the plugin (and `/upgrade`) when they trust - the folder. -5. **Add a module later:** run `/adopt` again in the adopted repository. It detects the adoption and - offers the modules you don't have yet. + the folder. It's the same setting `--scope project` writes; if you installed that way, it's + already there. +5. **Add a module later:** `/upgrade` offers the modules you don't have yet, and so does running + `/adopt` again in the adopted repository. The plugin contains **no framework content** — adopted repositories get plain committed files that every AI tool can read, with or without the plugin. diff --git a/docs/SETUP.md b/docs/SETUP.md index 5bdaa5f..e9c1e27 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -10,6 +10,11 @@ claude plugin install aplyca-framework@aplyca # then, in the repository: /adopt ``` +That installs the plugin for every project on your machine. For this project only, run both +commands from its folder with `--scope project` (committed, so the team is offered it — the right +choice when the project uses the dispatcher hub, since every worktree gets the setting) or +`--scope local` (only you). + The manual path below is the same procedure, step by step. ## 1. Copy the skeleton (on a branch) diff --git a/plugins/aplyca-framework/README.md b/plugins/aplyca-framework/README.md index bdaa7a9..f24a911 100644 --- a/plugins/aplyca-framework/README.md +++ b/plugins/aplyca-framework/README.md @@ -16,6 +16,20 @@ claude plugin marketplace add aplyca/AgenticDevelopmentFramework claude plugin install aplyca-framework@aplyca ``` +That installs it for every project on your machine (`user` scope). To install it for one project +only, run both commands from that project's folder with a scope: + +| Scope | Recorded in | Who gets the plugin | +|---|---|---| +| `user` (default) | Your own settings | You, in every project | +| `--scope project` | The project's committed `.claude/settings.json` | Everyone on the project — teammates are offered it when they trust the folder | +| `--scope local` | The project's git-ignored `.claude/settings.local.json` | You, in this project only | + +In a project that uses the dispatcher hub (the parallel-agents module), choose `project`: the +committed settings reach every task's worktree, while a local install exists only in the checkout +where you ran it. Either way the plugin's files are downloaded once per machine; the scope decides +where it's turned on. + ## Skills | Skill | Purpose | @@ -30,7 +44,8 @@ never push without explicit approval. `/cost-report` only reads. ## For teams To have every teammate offered the plugin when they trust the repository (and so get `/upgrade`), -add to the adopted repo's `.claude/settings.json` — `/adopt` offers to do it: +add to the adopted repo's `.claude/settings.json` — `/adopt` offers to do it, and installing with +`--scope project` writes the same entries: ```json { diff --git a/plugins/aplyca-framework/skills/adopt/SKILL.md b/plugins/aplyca-framework/skills/adopt/SKILL.md index 1d2de5d..da272e4 100644 --- a/plugins/aplyca-framework/skills/adopt/SKILL.md +++ b/plugins/aplyca-framework/skills/adopt/SKILL.md @@ -132,7 +132,8 @@ Present the table before going further. Wrong facts here poison every file downs teammates get `/upgrade`: `"extraKnownMarketplaces": {"aplyca": {"source": {"source": "github", "repo": "aplyca/AgenticDevelopmentFramework"}}}` and `"enabledPlugins": {"aplyca-framework@aplyca": true}` — the marketplace key must be `aplyca`, - the name `enabledPlugins` refers to. + the name `enabledPlugins` refers to. If the plugin was installed with `--scope project`, both + entries are already in `.claude/settings.json`: keep them when merging the skeleton's settings. ## Step 6 — Verify From ab63f8305cb5fad9f66a52b7ed9d5b1e8eef78de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20S=C3=A1nchez?= Date: Thu, 1 Oct 2026 21:59:35 -0500 Subject: [PATCH 3/7] docs: install the plugin per project, never for the whole machine Claude Code's plugin commands default to user scope, which turns the plugin on in every project. Every install instruction now passes --scope project from the project's folder (or --scope local to try it alone); /adopt commits and checks the setting, /upgrade offers to add it when a project lacks it, and a static check rejects an install command without a scope. The MCP guide now uses the project's .mcp.json. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 17 ++++++-- README.md | 30 ++++++------- docs/SETUP.md | 18 ++++---- evals/static/check-skills.sh | 14 ++++++ plugins/aplyca-framework/README.md | 43 ++++++++++++------- .../aplyca-framework/skills/adopt/SKILL.md | 14 +++--- .../aplyca-framework/skills/upgrade/SKILL.md | 14 +++++- skeleton/docs/MCP-INTEGRATION.md | 3 +- 8 files changed, 103 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20090be..b6ba5e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,11 +19,22 @@ ClickUp integration, or the GitHub harness. It now lists the missing modules, re the repository's facts support (the same rules as `/adopt`), and installs the chosen ones in the same upgrade pull request with their customize steps. Choosing `parallel-agents` moves the upgrade into a worktree of its own, so the main checkout starts as a clean hub (`aplyca-framework` 0.2.4). -The install docs (README, plugin README, `docs/SETUP.md`) now show installing the plugin for one -project only (`--scope project`, committed for the team, or `--scope local`), and why a hub project -should use `project`; `/adopt` keeps those settings when it merges `.claude/settings.json`. **Upgrade impact:** framework-internal; update the plugin. +### The plugin installs per project, never for the whole machine + +The install docs (README, plugin README, `docs/SETUP.md`) installed the plugin at Claude Code's default +`user` scope, which turns it on in every project on the machine. They now install it with +`--scope project` from the project's folder — recorded in the committed `.claude/settings.json`, so +the team is offered it and every worktree of a hub gets it — or `--scope local` to try it alone. +`/adopt` commits that setting with the adoption and checks it; `/upgrade` offers to add it when the +project doesn't have it. `docs/MCP-INTEGRATION.md` pointed MCP servers at `.claude/mcp.json` or a +global file; it now uses the project's `.mcp.json`. +**Upgrade impact:** merge `docs/MCP-INTEGRATION.md` (one line, § Wiring it into AI tools). +**Migration:** if you installed the plugin at user scope, let `/upgrade` add the project setting; once +every project you use it in has it, run `claude plugin uninstall aplyca-framework@aplyca --scope user` +and `claude plugin marketplace remove aplyca --scope user`. + ### Test first in every lane; the hub enforced ([0014](docs/decisions/0014-test-first-in-every-lane.md); [0008](docs/decisions/0008-dispatcher-and-worker-worktrees.md), addendum) diff --git a/README.md b/README.md index dd4891b..35df64e 100644 --- a/README.md +++ b/README.md @@ -34,35 +34,32 @@ Most of what's here was proven in real client projects first — some built on t ### With Claude Code — the installer plugin (recommended) -1. **Install the plugin** — for every project on your machine: +1. **Install the plugin in the project** — from its folder, with `--scope project`: ```bash - claude plugin marketplace add aplyca/AgenticDevelopmentFramework - claude plugin install aplyca-framework@aplyca + cd your-project + claude plugin marketplace add aplyca/AgenticDevelopmentFramework --scope project + claude plugin install aplyca-framework@aplyca --scope project ``` - Or only for this project: run both commands from the project's folder with `--scope project` - (recorded in its committed `.claude/settings.json`, so teammates are offered the plugin too) or - `--scope local` (only you, recorded in the git-ignored `.claude/settings.local.json`). In a - project that uses the dispatcher hub, choose `project`: committed settings reach every task's - worktree, while a local install exists only in the checkout where you ran it. + Both commands write to the project's `.claude/settings.json` and nowhere else: the plugin is on in + this project only, and teammates are offered it when they trust the folder. Without `--scope`, + Claude Code installs at `user` scope — on in every project on your machine — so always pass it. To + try the plugin alone first, use `--scope local` (the git-ignored `.claude/settings.local.json`). 2. **Run `/adopt`** in the project. It inspects the repository (stack, commands, branching model, tracker, Git host) and asks which [optional modules](modules/README.md) you want. Then it copies the skeleton, fills the placeholders from verified repository facts only, and configures the guardrail hooks (`.claude/hooks/config.sh`). It records the adoption as a process decision (PDR-0001), stamps the baseline version at the top of `CLAUDE.md`, verifies the hooks and the `@AGENTS.md` import, and - prepares a **draft pull request** on its own branch. It never commits to your default branch. + prepares a **draft pull request** on its own branch — the plugin setting the install wrote goes + in with it. It never commits to your default branch. 3. **Finish what only the team knows** in that pull request: the remaining `[PLACEHOLDER]`s, the constitution's principles, the sensitive areas (`AGENTS.md` and `CAREFUL_GLOBS`), and the stakeholder-update settings in `docs/TRACKER-INTEGRATION.md` (live site, previews, CMS entry links, task statuses). With the `clickup` module, each developer signs in once through `/mcp`. Then review and merge the pull request like any change. -4. **Optional — the whole team:** let `/adopt` register the marketplace in the project's - `.claude/settings.json`, so every teammate is offered the plugin (and `/upgrade`) when they trust - the folder. It's the same setting `--scope project` writes; if you installed that way, it's - already there. -5. **Add a module later:** `/upgrade` offers the modules you don't have yet, and so does running +4. **Add a module later:** `/upgrade` offers the modules you don't have yet, and so does running `/adopt` again in the adopted repository. The plugin contains **no framework content** — adopted repositories get plain committed files that @@ -88,7 +85,7 @@ The `3eb7777` release (2026-10-01) fixes defects that affect every adopted repos the order to upgrade in from an older baseline; if your settings pin a model ID, switch it to the `sonnet` alias. -1. **Update the plugin** — then restart Claude Code: +1. **Update the plugin** from the project's folder — then restart Claude Code: ```bash claude plugin marketplace update aplyca @@ -105,6 +102,9 @@ the order to upgrade in from an older baseline; if your settings pin a model ID, 3. **Review the pull request** and run the verification in [docs/UPGRADING.md](docs/UPGRADING.md): valid settings, hooks that fire, both instruction files loading, a smoke test of a changed skill. +Installed the plugin at user scope earlier? `/upgrade` adds the project setting in its pull request; +then remove the user-scope copy ([how](plugins/aplyca-framework/README.md#install)). + By hand, or to cherry-pick one improvement: [docs/UPGRADING.md](docs/UPGRADING.md). ## How it works diff --git a/docs/SETUP.md b/docs/SETUP.md index e9c1e27..56ede1a 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -5,15 +5,16 @@ this for you — `/adopt` inspects the repository, copies the skeleton and the m the placeholders from verified facts, configures the hooks, and opens a draft pull request: ```bash -claude plugin marketplace add aplyca/AgenticDevelopmentFramework -claude plugin install aplyca-framework@aplyca +cd your-project +claude plugin marketplace add aplyca/AgenticDevelopmentFramework --scope project +claude plugin install aplyca-framework@aplyca --scope project # then, in the repository: /adopt ``` -That installs the plugin for every project on your machine. For this project only, run both -commands from its folder with `--scope project` (committed, so the team is offered it — the right -choice when the project uses the dispatcher hub, since every worktree gets the setting) or -`--scope local` (only you). +`--scope project` turns the plugin on in this project only, through its committed +`.claude/settings.json` — the team is offered it, and every worktree of a hub project gets it. Without +`--scope`, Claude Code installs it for every project on your machine. To try it alone first, use +`--scope local`. The manual path below is the same procedure, step by step. @@ -152,8 +153,9 @@ Open the pull request as a draft; merge after review like any other change. ## Updating -Update the plugin, restart Claude Code, then run `/upgrade` in the adopted repository — it plans the -update from the baseline stamp, keeps your customizations, and prepares a draft pull request: +Update the plugin from the adopted repository's folder, restart Claude Code, then run `/upgrade` there — +it plans the update from the baseline stamp, keeps your customizations, and prepares a draft pull +request: ```bash claude plugin marketplace update aplyca diff --git a/evals/static/check-skills.sh b/evals/static/check-skills.sh index 355e6a9..6d76f14 100755 --- a/evals/static/check-skills.sh +++ b/evals/static/check-skills.sh @@ -649,6 +649,19 @@ check_marketplace_snippets() { fi } +check_install_scope() { + # Without --scope, `claude plugin marketplace add` and `claude plugin install` default to user + # scope, which turns the plugin on in every project on the machine. Installs are per project. + local hits + hits=$(grep -rnE 'claude plugin (marketplace add|install) ' "$REPO_ROOT/plugins" "$REPO_ROOT/docs" \ + "$SKELETON" "$MODULES_DIR" "$REPO_ROOT/README.md" 2>/dev/null | grep -vE -- '--scope (project|local)') + if [ -z "$hits" ]; then + pass "install commands pass --scope project or local" + else + fail "install commands without --scope project/local install for every project: $hits" + fi +} + check_no_tracked_junk() { git -C "$REPO_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0 local hits @@ -703,6 +716,7 @@ echo "" check_links check_modules check_marketplace_snippets +check_install_scope check_lanes check_practices check_plugin diff --git a/plugins/aplyca-framework/README.md b/plugins/aplyca-framework/README.md index f24a911..f93ac9f 100644 --- a/plugins/aplyca-framework/README.md +++ b/plugins/aplyca-framework/README.md @@ -11,24 +11,35 @@ runtime dependency on this plugin. ## Install +Install it in each project that uses the framework, from the project's folder: + ```bash -claude plugin marketplace add aplyca/AgenticDevelopmentFramework -claude plugin install aplyca-framework@aplyca +cd your-project +claude plugin marketplace add aplyca/AgenticDevelopmentFramework --scope project +claude plugin install aplyca-framework@aplyca --scope project ``` -That installs it for every project on your machine (`user` scope). To install it for one project -only, run both commands from that project's folder with a scope: +Always pass `--scope`: without it, Claude Code installs at `user` scope, which turns the plugin on in +every project on your machine and offers `/adopt` in sessions that have nothing to do with the +framework. | Scope | Recorded in | Who gets the plugin | |---|---|---| -| `user` (default) | Your own settings | You, in every project | -| `--scope project` | The project's committed `.claude/settings.json` | Everyone on the project — teammates are offered it when they trust the folder | -| `--scope local` | The project's git-ignored `.claude/settings.local.json` | You, in this project only | +| `--scope project` (use this) | The project's committed `.claude/settings.json` | Everyone on the project — teammates are offered it when they trust the folder | +| `--scope local` | The project's git-ignored `.claude/settings.local.json` | You, in this checkout only — to try it before the team sees it | + +In a project that uses the dispatcher hub (the parallel-agents module), only `project` works: the +committed setting reaches every task's worktree, while a local install exists only in the checkout +where you ran it. Claude Code keeps the downloaded plugin files in its own cache under your home +folder; the scope decides where the plugin is turned on. + +**Installed at user scope before?** `/upgrade` offers to add the project setting in its pull request. +Once every project you use the plugin in has it, remove the user-scope copy: -In a project that uses the dispatcher hub (the parallel-agents module), choose `project`: the -committed settings reach every task's worktree, while a local install exists only in the checkout -where you ran it. Either way the plugin's files are downloaded once per machine; the scope decides -where it's turned on. +```bash +claude plugin uninstall aplyca-framework@aplyca --scope user +claude plugin marketplace remove aplyca --scope user +``` ## Skills @@ -43,9 +54,9 @@ never push without explicit approval. `/cost-report` only reads. ## For teams -To have every teammate offered the plugin when they trust the repository (and so get `/upgrade`), -add to the adopted repo's `.claude/settings.json` — `/adopt` offers to do it, and installing with -`--scope project` writes the same entries: +These are the entries `--scope project` writes to `.claude/settings.json`, and what `/adopt` and +`/upgrade` keep (or add, when the plugin was installed another way) so every teammate is offered the +plugin — and `/upgrade` — when they trust the repository: ```json { @@ -60,9 +71,11 @@ add to the adopted repo's `.claude/settings.json` — `/adopt` offers to do it, ## Updating the plugin +From the project's folder: + ```bash claude plugin marketplace update aplyca claude plugin update aplyca-framework@aplyca ``` -Restart Claude Code, then run `/upgrade` in each adopted repository. +Restart Claude Code and run `/upgrade` there; repeat in each adopted repository. diff --git a/plugins/aplyca-framework/skills/adopt/SKILL.md b/plugins/aplyca-framework/skills/adopt/SKILL.md index da272e4..6df546c 100644 --- a/plugins/aplyca-framework/skills/adopt/SKILL.md +++ b/plugins/aplyca-framework/skills/adopt/SKILL.md @@ -22,7 +22,7 @@ read the source doc (locations in step 1). CI config, code, git history). Anything you can't evidence becomes a `` — an honest TODO beats a plausible invention. - **Client repositories:** confirm before pushing anything. If committing AI config isn't - appropriate for the client, offer the local-only fallback (`.git/info/exclude` + user-level config). + appropriate for the client, offer the local-only fallback (`.git/info/exclude`, and the plugin at `--scope local`). ## Step 1 — Locate the framework source @@ -128,18 +128,20 @@ Present the table before going further. Wrong facts here poison every file downs team is adopting, what it adds (files, gates, modules), the costs (docs to keep fresh, more tokens for phased work, the approval gate on the critical path), alternatives considered. Ask who the deciders are. Add it to the index in `docs/process/README.md`. -- *(Optional, ask)* Register the framework marketplace for the team in `.claude/settings.json`, so - teammates get `/upgrade`: +- **The plugin setting.** The documented install (`--scope project`) already wrote `"extraKnownMarketplaces": {"aplyca": {"source": {"source": "github", "repo": "aplyca/AgenticDevelopmentFramework"}}}` - and `"enabledPlugins": {"aplyca-framework@aplyca": true}` — the marketplace key must be `aplyca`, - the name `enabledPlugins` refers to. If the plugin was installed with `--scope project`, both - entries are already in `.claude/settings.json`: keep them when merging the skeleton's settings. + and `"enabledPlugins": {"aplyca-framework@aplyca": true}` into `.claude/settings.json`: keep both when + merging the skeleton's settings, so they're committed with the adoption and teammates get the + plugin and `/upgrade`. If they're missing — a user- or local-scope install — offer to add them + (the marketplace key must be `aplyca`, the name `enabledPlugins` refers to), and for a user-scope + install, give the commands that remove it (the plugin's README § Install). ## Step 6 — Verify Run these checks and report each as PASS / GAP with one line of evidence: - [ ] `.claude/settings.json` is valid JSON (`python3 -m json.tool .claude/settings.json`) and every hook entry uses the nested `hooks` array +- [ ] `.claude/settings.json` turns the plugin on for the project (`enabledPlugins` and the `aplyca` marketplace) — unless the team chose the local-only fallback - [ ] Hook scripts are executable and behave: pipe a sample event to each — e.g. `printf '{"cwd":"%s","tool_input":{"command":"git push origin main"}}' "$PWD" | .claude/hooks/guard-git.sh` exits 2; a `git status` event exits 0 - [ ] `CLAUDE.md` imports `AGENTS.md` (`@AGENTS.md`) — ask the user to start a new session and confirm with `/memory` that both load - [ ] No `[bracketed placeholders]` remain in `AGENTS.md`, `CONSTITUTION.md`, `CONTRIBUTING.md`; every unknown is a `TODO(team)` question diff --git a/plugins/aplyca-framework/skills/upgrade/SKILL.md b/plugins/aplyca-framework/skills/upgrade/SKILL.md index e5f1c67..4369000 100644 --- a/plugins/aplyca-framework/skills/upgrade/SKILL.md +++ b/plugins/aplyca-framework/skills/upgrade/SKILL.md @@ -68,6 +68,13 @@ Edit files under that path and run git with `-C `. Once the module is protect-hub hook stops edits in the main checkout, so the main checkout stays a clean hub from the first commit. +**Check where the plugin is turned on.** If `.claude/settings.json` has no +`"enabledPlugins": {"aplyca-framework@aplyca": true}` with its `aplyca` entry in +`extraKnownMarketplaces`, this session got the plugin from a user- or local-scope install. Offer to +add both entries in this upgrade (the snippet in the plugin's README § For teams), so the plugin is on +for this project and its team only — and in every worktree of a hub. After the merge, the developer +removes a user-scope copy with the commands in that README's § Install. + ## Step 3 — Classify every changed file `git -C diff --name-status OLD_SHA NEW_SHA -- skeleton/ modules//files/` @@ -93,7 +100,8 @@ follow the changelog's migration notes. ## Step 4 — Present the plan One table: `file → bucket → action → risk note`, plus the changelog's migration steps as their own -checklist, and the newly chosen modules with their customize steps. For merge-required files, show which customizations were detected (diff of the target file +checklist, the newly chosen modules with their customize steps, and the plugin setting when it's +being added. For merge-required files, show which customizations were detected (diff of the target file vs the OLD_SHA version) and confirm they will survive. Wait for approval. ## Step 5 — Execute @@ -103,6 +111,7 @@ vs the OLD_SHA version) and confirm they will survive. Wait for approval. restructured a section, place the customization where it now belongs and flag it in the PR body. - Migration steps from the changelog, in order. - Newly chosen modules: copy or install them, then their customize steps. +- The plugin setting, when the developer accepted it: merge both entries into `.claude/settings.json`. - Restamp: `Skeleton source:` → `NEW_SHA () · modules: ` — the list includes the new ones. ## Step 6 — Verify and deliver @@ -115,5 +124,6 @@ vs the OLD_SHA version) and confirm they will survive. Wait for approval. exists (`github`); `.githooks/pre-push` is executable and `core.hooksPath` is documented (`git-hooks`). 2. Commit with a `docs:` or `chore:` prefix, e.g. `chore: upgrade framework skeleton OLD_SHA → NEW_SHA`. 3. PR body: changelog summary, the plan table as executed, migration steps done, customizations - reapplied, the modules added and why, anything needing human judgment. + reapplied, the modules added and why, the plugin setting if added (with the commands that remove a + user-scope copy), anything needing human judgment. 4. Push and open the PR **as a draft, only after the user approves**. diff --git a/skeleton/docs/MCP-INTEGRATION.md b/skeleton/docs/MCP-INTEGRATION.md index 507b88e..0e7d6cb 100644 --- a/skeleton/docs/MCP-INTEGRATION.md +++ b/skeleton/docs/MCP-INTEGRATION.md @@ -151,7 +151,8 @@ This is ~90 lines and supports listing spec folders (and legacy single-file spec ### Claude Code -Add to `.claude/mcp.json` (or your global `~/.claude/mcp.json`): +Add to `.mcp.json` at the repository root — project scope, so the team shares it and it's on in this +project only: ```json { From 05953f9b25cd52a4e05b23b5d2814b43dd0b03ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20S=C3=A1nchez?= Date: Thu, 1 Oct 2026 22:12:14 -0500 Subject: [PATCH 4/7] docs: install from the desktop app; correct the local-scope note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plugin README said only project scope reaches a hub's worktrees. Since Claude Code 2.1.211, worktrees read the main checkout's settings.local.json on macOS and Linux (not on Windows); project scope stays the default because it reaches every platform and teammate. Adds the desktop Code tab steps (+ → Plugins → Add plugin, scope "this project") and where plugins don't load (WSL, cloud sessions). Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 5 +++-- README.md | 3 +++ docs/SETUP.md | 3 ++- plugins/aplyca-framework/README.md | 34 ++++++++++++++++++++++++------ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6ba5e1..4a29a7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,8 +27,9 @@ The install docs (README, plugin README, `docs/SETUP.md`) installed the plugin a `user` scope, which turns it on in every project on the machine. They now install it with `--scope project` from the project's folder — recorded in the committed `.claude/settings.json`, so the team is offered it and every worktree of a hub gets it — or `--scope local` to try it alone. -`/adopt` commits that setting with the adoption and checks it; `/upgrade` offers to add it when the -project doesn't have it. `docs/MCP-INTEGRATION.md` pointed MCP servers at `.claude/mcp.json` or a +The plugin README adds the steps for the desktop app's Code tab (**+ → Plugins → Add plugin**, scope +"this project"). `/adopt` commits that setting with the adoption and checks it; `/upgrade` offers to +add it when the project doesn't have it. `docs/MCP-INTEGRATION.md` pointed MCP servers at `.claude/mcp.json` or a global file; it now uses the project's `.mcp.json`. **Upgrade impact:** merge `docs/MCP-INTEGRATION.md` (one line, § Wiring it into AI tools). **Migration:** if you installed the plugin at user scope, let `/upgrade` add the project setting; once diff --git a/README.md b/README.md index 35df64e..a198a91 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,9 @@ Most of what's here was proven in real client projects first — some built on t this project only, and teammates are offered it when they trust the folder. Without `--scope`, Claude Code installs at `user` scope — on in every project on your machine — so always pass it. To try the plugin alone first, use `--scope local` (the git-ignored `.claude/settings.local.json`). + In the desktop app's Code tab, add the marketplace the same way, then install from + **+ → Plugins → Add plugin** with the scope set to this project + ([details](plugins/aplyca-framework/README.md#in-the-desktop-app)). 2. **Run `/adopt`** in the project. It inspects the repository (stack, commands, branching model, tracker, Git host) and asks which [optional modules](modules/README.md) you want. Then it copies the diff --git a/docs/SETUP.md b/docs/SETUP.md index 56ede1a..5bc2327 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -14,7 +14,8 @@ claude plugin install aplyca-framework@aplyca --scope project `--scope project` turns the plugin on in this project only, through its committed `.claude/settings.json` — the team is offered it, and every worktree of a hub project gets it. Without `--scope`, Claude Code installs it for every project on your machine. To try it alone first, use -`--scope local`. +`--scope local`. From the desktop app's Code tab: +[the plugin's README § In the desktop app](../plugins/aplyca-framework/README.md#in-the-desktop-app). The manual path below is the same procedure, step by step. diff --git a/plugins/aplyca-framework/README.md b/plugins/aplyca-framework/README.md index f93ac9f..d465c6f 100644 --- a/plugins/aplyca-framework/README.md +++ b/plugins/aplyca-framework/README.md @@ -25,13 +25,35 @@ framework. | Scope | Recorded in | Who gets the plugin | |---|---|---| -| `--scope project` (use this) | The project's committed `.claude/settings.json` | Everyone on the project — teammates are offered it when they trust the folder | -| `--scope local` | The project's git-ignored `.claude/settings.local.json` | You, in this checkout only — to try it before the team sees it | +| `--scope project` (use this) | The project's committed `.claude/settings.json` | Everyone on the project — teammates get it once they trust the folder | +| `--scope local` | The project's git-ignored `.claude/settings.local.json` | You, in this repository only — to try it before the team sees it | -In a project that uses the dispatcher hub (the parallel-agents module), only `project` works: the -committed setting reaches every task's worktree, while a local install exists only in the checkout -where you ran it. Claude Code keeps the downloaded plugin files in its own cache under your home -folder; the scope decides where the plugin is turned on. +In a project that uses the dispatcher hub (the parallel-agents module), use `project`: the committed +setting reaches every task's worktree on every platform, and every teammate. A local install reaches +the worktrees only on macOS and Linux with Claude Code 2.1.211 or later, which keeps +`.claude/settings.local.json` at the main checkout; on Windows it stays in the checkout where you ran +it. Claude Code keeps the downloaded plugin files in its own cache under your home folder; the scope +decides where the plugin is turned on. + +### In the desktop app + +The Code tab of the Claude desktop app reads the same settings files as the terminal, so an install +made with the commands above works there too. To install from the app instead: + +1. Add the marketplace from a terminal in the project's folder — the app's plugin browser lists the + plugins of marketplaces already added: + + ```bash + claude plugin marketplace add aplyca/AgenticDevelopmentFramework --scope project + ``` + +2. In a local or SSH session on the project, click **+** next to the prompt box, then **Plugins** → + **Add plugin**. Select `aplyca-framework` and choose **this project** as the scope. + +**+ → Plugins → Manage plugins** enables, disables, or uninstalls it later. Worktree sessions the app +creates load a project-scope plugin (Claude Code 2.1.200 or later). Plugins don't load in WSL +sessions, and cloud sessions don't install the plugins a repository's settings declare — run `/adopt` +and `/upgrade` in a local session. **Installed at user scope before?** `/upgrade` offers to add the project setting in its pull request. Once every project you use the plugin in has it, remove the user-scope copy: From 9ba981c1c75999bb619a8226eb8db63e74bd4e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20S=C3=A1nchez?= Date: Thu, 1 Oct 2026 22:17:22 -0500 Subject: [PATCH 5/7] docs: an install prompt for any Claude Code session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install was two shell commands, and the desktop app can't add a marketplace from its UI. Both READMEs now open with a prompt to paste into a session on the project — terminal, desktop, or IDE — that runs the commands with --scope project, skips a plugin the project already declares, stops in a hub's main checkout, shows the settings diff, and reports a leftover user-scope copy without removing it. A static check keeps the two copies identical. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 6 ++++-- README.md | 28 ++++++++++++++++++++++++++-- docs/SETUP.md | 4 +++- evals/static/check-skills.sh | 27 +++++++++++++++++++++++++++ plugins/aplyca-framework/README.md | 26 +++++++++++++++++++++++++- 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a29a7b..e923473 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,8 +27,10 @@ The install docs (README, plugin README, `docs/SETUP.md`) installed the plugin a `user` scope, which turns it on in every project on the machine. They now install it with `--scope project` from the project's folder — recorded in the committed `.claude/settings.json`, so the team is offered it and every worktree of a hub gets it — or `--scope local` to try it alone. -The plugin README adds the steps for the desktop app's Code tab (**+ → Plugins → Add plugin**, scope -"this project"). `/adopt` commits that setting with the adoption and checks it; `/upgrade` offers to +Both READMEs open with an install prompt to paste into any Claude Code session — terminal, desktop +app, or IDE: it runs the two commands, skips a plugin the project already declares, stops in a hub's +main checkout, and reports a leftover user-scope copy. The plugin README adds the steps for the +desktop app's Code tab (**+ → Plugins → Add plugin**, scope "this project"). `/adopt` commits that setting with the adoption and checks it; `/upgrade` offers to add it when the project doesn't have it. `docs/MCP-INTEGRATION.md` pointed MCP servers at `.claude/mcp.json` or a global file; it now uses the project's `.mcp.json`. **Upgrade impact:** merge `docs/MCP-INTEGRATION.md` (one line, § Wiring it into AI tools). diff --git a/README.md b/README.md index a198a91..f9126d2 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,31 @@ Most of what's here was proven in real client projects first — some built on t ### With Claude Code — the installer plugin (recommended) -1. **Install the plugin in the project** — from its folder, with `--scope project`: +1. **Install the plugin in the project.** Paste this prompt into a Claude Code session opened on the + project — in the terminal, the desktop app, or an IDE: + + + ```text + Install the aplyca-framework plugin (Agentic Development Framework) for this project only — never + at user scope. + + 1. Check that this folder is the root of a git repository. If .claude/settings.json already enables + aplyca-framework@aplyca, say so and skip to step 6. + 2. If scripts/agent/worktree-new.sh exists and this is the main checkout (git rev-parse --git-dir + equals git rev-parse --git-common-dir), stop: the hub takes no edits. Tell me to run this from a + worktree. + 3. From this folder, run: + claude plugin marketplace add aplyca/AgenticDevelopmentFramework --scope project + claude plugin install aplyca-framework@aplyca --scope project + 4. Show me the diff of .claude/settings.json: it should add only the aplyca marketplace and the + plugin. Don't commit it — /adopt or /upgrade puts it in its pull request. + 5. If claude plugin list also shows the plugin at user scope, tell me, with the commands that remove + that copy. Don't run them. + 6. Tell me to start a new session here, then run /upgrade if CLAUDE.md has a "Skeleton source:" + line, otherwise /adopt. + ``` + + Or run the two commands yourself, from the project's folder: ```bash cd your-project @@ -43,7 +67,7 @@ Most of what's here was proven in real client projects first — some built on t ``` Both commands write to the project's `.claude/settings.json` and nowhere else: the plugin is on in - this project only, and teammates are offered it when they trust the folder. Without `--scope`, + this project only, and teammates get it once they trust the folder. Without `--scope`, Claude Code installs at `user` scope — on in every project on your machine — so always pass it. To try the plugin alone first, use `--scope local` (the git-ignored `.claude/settings.local.json`). In the desktop app's Code tab, add the marketplace the same way, then install from diff --git a/docs/SETUP.md b/docs/SETUP.md index 5bc2327..9e0db17 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -2,7 +2,9 @@ How to adopt the framework in a repository by hand. With Claude Code, the installer plugin does all of this for you — `/adopt` inspects the repository, copies the skeleton and the modules you choose, fills -the placeholders from verified facts, configures the hooks, and opens a draft pull request: +the placeholders from verified facts, configures the hooks, and opens a draft pull request. Install it +with [the install prompt](../README.md#with-claude-code--the-installer-plugin-recommended) in any Claude +Code session, or with these commands: ```bash cd your-project diff --git a/evals/static/check-skills.sh b/evals/static/check-skills.sh index 6d76f14..66ebc61 100755 --- a/evals/static/check-skills.sh +++ b/evals/static/check-skills.sh @@ -662,6 +662,32 @@ check_install_scope() { fi } +check_install_prompt() { + # The install prompt is printed in two READMEs; a fix made in one must reach the other. + local report + report=$(python3 - "$REPO_ROOT/README.md" "$REPO_ROOT/plugins/aplyca-framework/README.md" <<'PY' +import sys, textwrap +blocks = [] +for path in sys.argv[1:]: + lines = open(path, encoding="utf-8").read().split("\n") + try: + start = next(i for i, l in enumerate(lines) if " +```text +Install the aplyca-framework plugin (Agentic Development Framework) for this project only — never +at user scope. + +1. Check that this folder is the root of a git repository. If .claude/settings.json already enables + aplyca-framework@aplyca, say so and skip to step 6. +2. If scripts/agent/worktree-new.sh exists and this is the main checkout (git rev-parse --git-dir + equals git rev-parse --git-common-dir), stop: the hub takes no edits. Tell me to run this from a + worktree. +3. From this folder, run: + claude plugin marketplace add aplyca/AgenticDevelopmentFramework --scope project + claude plugin install aplyca-framework@aplyca --scope project +4. Show me the diff of .claude/settings.json: it should add only the aplyca marketplace and the + plugin. Don't commit it — /adopt or /upgrade puts it in its pull request. +5. If claude plugin list also shows the plugin at user scope, tell me, with the commands that remove + that copy. Don't run them. +6. Tell me to start a new session here, then run /upgrade if CLAUDE.md has a "Skeleton source:" + line, otherwise /adopt. +``` + +Or run the two commands yourself, from the project's folder: ```bash cd your-project From 2fd8e3f3a26a88627e581f1816dedf6acefe99a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20S=C3=A1nchez?= Date: Thu, 1 Oct 2026 22:24:40 -0500 Subject: [PATCH 6/7] feat: adopt in one prompt, and in a new project A session told "Adopt the Agentic Development Framework in this project: " had nothing to follow. ADOPT.md, pointed to from the top of the README, is the procedure for agents: confirm, check the project (new, adopted, or a hub's main checkout), install the plugin with --scope project, then run /adopt in a new session or by following its skill file in this one. /adopt gains a new-project mode: git init and a first commit, the planned stack asked for and marked as planned, the stack recorded as ADR-0001 (proposed), delivery without a remote; /init-project replaces the planned entries once code lands. /adopt and /upgrade find the marketplace folder through `claude plugin marketplace list --json`. Co-Authored-By: Claude Opus 5.5 --- ADOPT.md | 53 +++++++++++++++++++ CHANGELOG.md | 24 +++++++-- CLAUDE.md | 1 + README.md | 20 ++++++- docs/SETUP.md | 6 +++ evals/static/check-skills.sh | 7 ++- .../aplyca-framework/skills/adopt/SKILL.md | 38 +++++++++++-- .../aplyca-framework/skills/upgrade/SKILL.md | 4 +- skeleton/.claude/skills/init-project/SKILL.md | 8 ++- 9 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 ADOPT.md diff --git a/ADOPT.md b/ADOPT.md new file mode 100644 index 0000000..64ac600 --- /dev/null +++ b/ADOPT.md @@ -0,0 +1,53 @@ +# Adopt the framework — for AI agents + +You were asked to adopt, use, install, or set up the Agentic Development Framework in a project. This +page is the procedure. Don't copy files from this repository by hand: the adoption fills the skeleton +from verified facts about the project, stamps the version it came from, and lands as a draft pull +request — the installer plugin's `/adopt` does all of that. + +The project is the one the developer named, usually your session's folder. Before step 1, tell the +developer what you'll do — install the plugin for this project only, then adopt the framework on a +branch, delivered as a draft pull request — and wait for their go-ahead. + +## 1. Check the project + +- **Not a git repository?** Offer `git init -b ` (ask for the name; suggest `main`). + A new project with no code yet is fine: `/adopt` has a mode for it. +- **Already adopted?** If `CLAUDE.md` has a `Skeleton source:` line, the task is an upgrade: follow + steps 2 and 3 with `/upgrade` in place of `/adopt`. +- **The main checkout of a hub?** If `scripts/agent/worktree-new.sh` exists and + `git rev-parse --git-dir` equals `git rev-parse --git-common-dir`, stop: the hub takes no edits. Ask + the developer to start a session in a worktree and run this there. + +## 2. Install the plugin for this project only + +Skip this step when `.claude/settings.json` already enables `aplyca-framework@aplyca`. Otherwise run, +from the project's root: + +```bash +claude plugin marketplace add aplyca/AgenticDevelopmentFramework --scope project +claude plugin install aplyca-framework@aplyca --scope project +``` + +Always with `--scope project`: without it, Claude Code installs at user scope, which turns the plugin +on in every project on the machine. If `claude plugin list` also shows the plugin at user scope, tell +the developer, with the commands that remove that copy (the plugin's +[README § Install](plugins/aplyca-framework/README.md#install)); don't run them. + +The install changes `.claude/settings.json`. Show the developer the diff and leave it uncommitted: the +adoption's pull request carries it. + +## 3. Run the adoption + +The plugin's skills load when a session starts, so this session doesn't have `/adopt` yet. Either: + +- **Hand over:** tell the developer to start a new session in the project and run `/adopt` + (`/upgrade` for an adopted project). +- **Continue here:** find the marketplace's folder — the `installLocation` of `aplyca` in + `claude plugin marketplace list --json` — then read + `plugins/aplyca-framework/skills/adopt/SKILL.md` (or `upgrade/SKILL.md`) inside it and follow it step + by step. It is the same procedure `/adopt` runs. + +Its ground rules hold either way: every filled placeholder traces to a file you read, nothing is +committed to the default branch (except a new repository's first commit, with the developer's yes), +and nothing is pushed until the developer approves. diff --git a/CHANGELOG.md b/CHANGELOG.md index e923473..7f752cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,14 +30,32 @@ the team is offered it and every worktree of a hub gets it — or `--scope local Both READMEs open with an install prompt to paste into any Claude Code session — terminal, desktop app, or IDE: it runs the two commands, skips a plugin the project already declares, stops in a hub's main checkout, and reports a leftover user-scope copy. The plugin README adds the steps for the -desktop app's Code tab (**+ → Plugins → Add plugin**, scope "this project"). `/adopt` commits that setting with the adoption and checks it; `/upgrade` offers to -add it when the project doesn't have it. `docs/MCP-INTEGRATION.md` pointed MCP servers at `.claude/mcp.json` or a -global file; it now uses the project's `.mcp.json`. +desktop app's Code tab (**+ → Plugins → Add plugin**, scope "this project"). `/adopt` commits +that setting with the adoption and checks it; `/upgrade` offers to add it when the project doesn't +have it. `docs/MCP-INTEGRATION.md` pointed MCP servers at `.claude/mcp.json` or a global file; it now +uses the project's `.mcp.json`. **Upgrade impact:** merge `docs/MCP-INTEGRATION.md` (one line, § Wiring it into AI tools). **Migration:** if you installed the plugin at user scope, let `/upgrade` add the project setting; once every project you use it in has it, run `claude plugin uninstall aplyca-framework@aplyca --scope user` and `claude plugin marketplace remove aplyca --scope user`. +### Adopt in one prompt — and in a new project + +A Claude Code session told "Adopt the Agentic Development Framework in this project: " +had nothing to follow, so it could copy the skeleton by hand and skip the fact-checking, the version +stamp, and the pull request. A new `ADOPT.md` at the repository root, pointed to from the top of the +README, is the procedure for agents: confirm with the developer, check the project (new, already +adopted, or a hub's main checkout), install the plugin with `--scope project`, then run `/adopt` — in +a new session, or in the same one by following the skill's file from the marketplace folder. +`/adopt` gains a mode for a new project with no code yet: it offers `git init` and the first commit, +asks for the planned stack instead of reading it, marks those entries +``, records the stack as ADR-0001 (`proposed`), and +delivers without a remote. `/init-project` is the step that replaces the planned entries once the +first code lands. `/adopt` and `/upgrade` find the marketplace folder through +`claude plugin marketplace list --json` instead of assuming its path. +**Upgrade impact:** overwrite `.claude/skills/init-project/SKILL.md`; the rest is framework-internal — +update the plugin. + ### Test first in every lane; the hub enforced ([0014](docs/decisions/0014-test-first-in-every-lane.md); [0008](docs/decisions/0008-dispatcher-and-worker-worktrees.md), addendum) diff --git a/CLAUDE.md b/CLAUDE.md index 25f5d8e..9f33c0b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,6 +19,7 @@ The repo slug is `AgenticDevelopmentFramework` (renamed from `ai-dev-starter-kit - `skeleton/docs/` — constitution, spec model, process (PDRs), reference, tracker integration, and documentation templates - `modules/` — optional additions (`github/`, `git-hooks/`, `clickup/`, `parallel-agents/`); each has a `MODULE.md` and a `files/` tree mirroring the target repo - `plugins/aplyca-framework/` — the Claude Code installer plugin (`/adopt`, `/upgrade`); contains no framework content +- `ADOPT.md` — the adoption procedure for AI agents, which the top of `README.md` points to; keep it in step with `/adopt` - `docs/` — framework guides (setup, upgrading, onboarding, catalogs, examples, scenarios) and `docs/decisions/` (why the framework works the way it does) - `evals/` — static checks, hook and module functional tests, dynamic fixtures - `.claude/` — configuration for working on **this framework repo** (not for target projects) diff --git a/README.md b/README.md index f9126d2..7a2b7bb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Agentic Development Framework +> **AI agents:** asked to adopt, use, or install this framework in a project? Follow +> [ADOPT.md](ADOPT.md) (raw: `https://raw.githubusercontent.com/aplyca/AgenticDevelopmentFramework/main/ADOPT.md`) +> — don't copy files from this repository by hand. + A production-grade framework for professional **multi-perspective spec-driven, test-driven, docs-first AI-assisted development.** It ships as a portable project skeleton you drop into any codebase, optional modules for your Git host and ways of working, and an installer plugin for Claude Code. It includes an enforced multi-perspective spec model, specialized agents, workflow skills, multi-agent workflows, and guardrail hooks. Engineering standards and a team onboarding path are part of it too. The framework is built on three reinforcing disciplines: @@ -34,6 +38,17 @@ Most of what's here was proven in real client projects first — some built on t ### With Claude Code — the installer plugin (recommended) +**In one prompt.** Open a Claude Code session on the project — in the terminal, the desktop app, or an +IDE — and say: + +```text +Adopt the Agentic Development Framework in this project: https://github.com/aplyca/AgenticDevelopmentFramework +``` + +This README points the session to [ADOPT.md](ADOPT.md), the procedure for agents: check the project, +install the plugin for this project only, and run the adoption below — in a new project too, before +any code exists. Step by step: + 1. **Install the plugin in the project.** Paste this prompt into a Claude Code session opened on the project — in the terminal, the desktop app, or an IDE: @@ -86,7 +101,10 @@ Most of what's here was proven in real client projects first — some built on t stakeholder-update settings in `docs/TRACKER-INTEGRATION.md` (live site, previews, CMS entry links, task statuses). With the `clickup` module, each developer signs in once through `/mcp`. Then review and merge the pull request like any change. -4. **Add a module later:** `/upgrade` offers the modules you don't have yet, and so does running +4. **A new project with no code yet?** `/adopt` asks for the planned stack instead of reading it, + records it as the first architecture decision, and marks those entries as planned. Run + `/init-project` once the first code lands, to replace them with verified facts. +5. **Add a module later:** `/upgrade` offers the modules you don't have yet, and so does running `/adopt` again in the adopted repository. The plugin contains **no framework content** — adopted repositories get plain committed files that diff --git a/docs/SETUP.md b/docs/SETUP.md index 9e0db17..64ce3be 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -21,6 +21,12 @@ claude plugin install aplyca-framework@aplyca --scope project The manual path below is the same procedure, step by step. +**A new project with no code yet:** create the repository and a first commit (`git init -b main`, +then `git commit --allow-empty -m "chore: initial commit"`), and adopt on a branch as below. Where a +step asks for facts, write the planned ones and mark each ``; +record the stack as ADR-0001 (`docs/architecture/decisions/`, status `proposed`). Once the first code +lands, run `/init-project` to replace the planned entries with verified facts. + ## 1. Copy the skeleton (on a branch) ```bash diff --git a/evals/static/check-skills.sh b/evals/static/check-skills.sh index 66ebc61..c376d56 100755 --- a/evals/static/check-skills.sh +++ b/evals/static/check-skills.sh @@ -613,8 +613,11 @@ check_practices() { grep -q 'protect-hub.sh' "$SETTINGS" || missing+=("settings.json: protect-hub hook") file_contains "$HOOKS_DIR/config.sh" '^HUB_READONLY=' || missing+=("config.sh: HUB_READONLY") file_contains "$REPO_ROOT/plugins/aplyca-framework/skills/upgrade/SKILL.md" "Offer the modules the project doesn't have" || missing+=("/upgrade: offers missing modules") + file_contains "$REPO_ROOT/plugins/aplyca-framework/skills/adopt/SKILL.md" '### A new project' || missing+=("/adopt: new-project mode") + file_contains_literal "$REPO_ROOT/ADOPT.md" '--scope project' || missing+=("ADOPT.md: the agent entry point installs per project") + file_contains_literal "$REPO_ROOT/README.md" '(ADOPT.md)' || missing+=("README.md: points agents to ADOPT.md") if [ ${#missing[@]} -eq 0 ]; then - pass "practices: signal-first debugging, question rounds, glossary, merge danger, test independence, decision threshold, handoff, worktree roles, portable worktree defaults, test-first in every lane, the hub enforced, /upgrade offers modules" + pass "practices: signal-first debugging, question rounds, glossary, merge danger, test independence, decision threshold, handoff, worktree roles, portable worktree defaults, test-first in every lane, the hub enforced, /upgrade offers modules, adopting from one prompt and in a new project" else fail "practices: missing" "${missing[*]}" fi @@ -654,7 +657,7 @@ check_install_scope() { # scope, which turns the plugin on in every project on the machine. Installs are per project. local hits hits=$(grep -rnE 'claude plugin (marketplace add|install) ' "$REPO_ROOT/plugins" "$REPO_ROOT/docs" \ - "$SKELETON" "$MODULES_DIR" "$REPO_ROOT/README.md" 2>/dev/null | grep -vE -- '--scope (project|local)') + "$SKELETON" "$MODULES_DIR" "$REPO_ROOT/README.md" "$REPO_ROOT/ADOPT.md" 2>/dev/null | grep -vE -- '--scope (project|local)') if [ -z "$hits" ]; then pass "install commands pass --scope project or local" else diff --git a/plugins/aplyca-framework/skills/adopt/SKILL.md b/plugins/aplyca-framework/skills/adopt/SKILL.md index 6df546c..cb3fedf 100644 --- a/plugins/aplyca-framework/skills/adopt/SKILL.md +++ b/plugins/aplyca-framework/skills/adopt/SKILL.md @@ -1,6 +1,6 @@ --- name: adopt -description: Bootstrap a repository for AI-agentic development with the Aplyca framework — inspect it, copy the skeleton and the optional modules the team chooses, fill placeholders with verified facts only, configure the guardrail hooks, stamp the baseline SHA, record the adoption as PDR-0001, verify, and prepare an adoption PR. Also adds modules to an already-adopted repository. Use when asked to adopt the framework, enable agentic development, bootstrap AI config, or make a repo AI-ready. +description: Bootstrap a repository for AI-agentic development with the Aplyca framework — inspect it, copy the skeleton and the optional modules the team chooses, fill placeholders with verified facts only, configure the guardrail hooks, stamp the baseline SHA, record the adoption as PDR-0001, verify, and prepare an adoption PR. Works in a new project before any code exists, and adds modules to an already-adopted repository. Use when asked to adopt the framework, enable agentic development, bootstrap AI config, or make a repo AI-ready. --- # Adopt the Agentic Development Framework @@ -15,7 +15,8 @@ read the source doc (locations in step 1). ## Ground rules - **Never commit to the default branch.** Work on a feature branch (suggest `docs/agentic-adoption`); - the deliverable is a reviewable draft PR. + the deliverable is a reviewable draft PR. The one exception is a new repository's first commit + (§ A new project). - **Docs and config only.** Adoption adds no dependencies, no runtime code, and no build changes. The hook and worktree scripts are dev tooling; if anything seems to need more, stop and ask. - **Facts need evidence.** Every placeholder you fill traces to a file you read (manifest, lockfile, @@ -29,7 +30,8 @@ read the source doc (locations in step 1). Resolve the framework root, in order: 1. `${CLAUDE_PLUGIN_ROOT}/../..` — only when the plugin runs from a checkout of the framework repo. -2. The marketplace checkout: `~/.claude/plugins/marketplaces//` (usually `aplyca`). +2. The marketplace checkout: the `installLocation` of the marketplace (usually `aplyca`) in + `claude plugin marketplace list --json` — by default `~/.claude/plugins/marketplaces/aplyca/`. **The normal case on installed machines** — installed plugins run from a version cache, so `${CLAUDE_PLUGIN_ROOT}` isn't inside the repo. Run `claude plugin marketplace update ` first. 3. Otherwise clone: `git clone --depth 1 https://github.com/aplyca/AgenticDevelopmentFramework`. @@ -43,6 +45,34 @@ stamp) or point to `/upgrade`. When the parallel-agents module is already instal checkout is the hub and its hook stops edits there: do this from a session in a worktree of its own (`scripts/agent/worktree-new.sh chore/add-modules --no-start`). +### A new project + +No git repository, no commits, or nothing to inspect yet (no manifest, no source code): adopting +before the first line of code puts the process and the guardrails in place first. Every other step +applies, with these changes: + +- **No repository:** offer `git init -b ` — ask for the name; suggest `main`. +- **No commits:** there is no default branch to branch from. With the developer's yes, make one first + commit on it with what's already there (`git commit --allow-empty -m "chore: initial commit"` when + there's nothing), then branch as usual. +- **Step 2 asks instead of reads.** There are no facts to evidence yet. Ask for the planned ones in one + round, with your recommendations: language and framework, package manager, test runner, hosting, + branching model (Model A is the usual start), tracker, ways of working, sensitive areas. Mark each + answer in `AGENTS.md` as planned — `` — so no one mistakes + it for a verified fact. A command nobody has run yet stays a `TODO(team)`. +- **The stack is a decision.** Record it as ADR-0001 in `docs/architecture/decisions/` from the + template, status `proposed`, with the alternatives the developer considered; the adoption's review + accepts it. +- **Globs point at planned paths or stay empty** — `CAREFUL_GLOBS`, `APPEND_ONLY_GLOBS`, + `GENERATED_GLOBS`, the rules' `paths:` — and no nested `AGENTS.md` yet. +- **Step 6:** commands that can't run yet are a GAP — "no code yet" — not a failure. +- **Step 7 without a remote:** commit on the adoption branch, show the PR body, and tell the developer + to add the remote, push, and open the draft pull request from it. +- **After the adoption,** the scaffold — the framework's init, the first test — is the first task + through the lanes; usually full, since it sets the structure others follow. Once that code lands, + run `/init-project` to replace the planned entries with verified facts, and `/context-audit` to find + what's stale. + ## Step 2 — Discover the repo (read-only, before copying anything) Build a facts table (`fact → evidence file:line`): @@ -146,7 +176,7 @@ Run these checks and report each as PASS / GAP with one line of evidence: - [ ] `CLAUDE.md` imports `AGENTS.md` (`@AGENTS.md`) — ask the user to start a new session and confirm with `/memory` that both load - [ ] No `[bracketed placeholders]` remain in `AGENTS.md`, `CONSTITUTION.md`, `CONTRIBUTING.md`; every unknown is a `TODO(team)` question - [ ] Skill frontmatter uses hyphenated keys only (no `user_invocable` and the like) -- [ ] Build/test/lint commands documented AND runnable by an agent (actually run the safe ones) +- [ ] Build/test/lint commands documented AND runnable by an agent (actually run the safe ones) — in a new project, a GAP until the first code lands - [ ] Readiness checklist (Agentic Development Guide §9): `README.md` + `AGENTS.md` current; constitution; `specs/` scaffold; `docs/` with architecture and ADRs; skills/MCP versioned; nested `AGENTS.md` where needed (or explicitly not needed); boundaries section; CI gates before merge (GAP if none — don't invent one); written "no merge without human review" GAPs go in the PR description; they're findings, not failures to hide. diff --git a/plugins/aplyca-framework/skills/upgrade/SKILL.md b/plugins/aplyca-framework/skills/upgrade/SKILL.md index 4369000..ac4d134 100644 --- a/plugins/aplyca-framework/skills/upgrade/SKILL.md +++ b/plugins/aplyca-framework/skills/upgrade/SKILL.md @@ -39,8 +39,8 @@ agents) and confirm the inferred SHA with the user before proceeding. ## Step 2 — Locate the framework source and NEW_SHA Same resolution order as `/adopt` Step 1: repo checkout via `${CLAUDE_PLUGIN_ROOT}/../..` -(development installs), else the marketplace checkout `~/.claude/plugins/marketplaces//` -(normal case — run `claude plugin marketplace update ` first), else a **full** clone of +(development installs), else the marketplace checkout — its `installLocation` in +`claude plugin marketplace list --json`, by default `~/.claude/plugins/marketplaces//` (normal case — run `claude plugin marketplace update ` first), else a **full** clone of `https://github.com/aplyca/AgenticDevelopmentFramework` (not shallow — the diff needs history). NEW_SHA is its current HEAD. diff --git a/skeleton/.claude/skills/init-project/SKILL.md b/skeleton/.claude/skills/init-project/SKILL.md index eec0000..17b4b89 100644 --- a/skeleton/.claude/skills/init-project/SKILL.md +++ b/skeleton/.claude/skills/init-project/SKILL.md @@ -1,6 +1,6 @@ --- name: init-project -description: First-time setup of this project's AI-assisted development configuration — fill AGENTS.md, the constitution, the customizable rules, the hook configuration, and the core docs from verified facts about the repository, and add nested AGENTS.md files where modules differ. Use once, after the skeleton has been copied in (the framework's /adopt does this end to end). +description: First-time setup of this project's AI-assisted development configuration — fill AGENTS.md, the constitution, the customizable rules, the hook configuration, and the core docs from verified facts about the repository, and add nested AGENTS.md files where modules differ. Use once, after the skeleton has been copied in (the framework's /adopt does this end to end) — and again in a project adopted before its code existed, once the first code lands. argument-hint: "[project name]" --- @@ -12,6 +12,12 @@ in it now. **Facts need evidence:** fill each placeholder from a file you read ( CI config, code). What you can't evidence becomes `` — an honest TODO beats a plausible invention. +**A project adopted before its code existed** has planned entries in `AGENTS.md`, marked +``, and a stack ADR. Once the first code lands, run this +again: replace each planned entry with the verified fact (or a `TODO(team)` where the plan changed), +fill the commands from the real manifests, and point the globs and the rules' `paths:` at the real +structure. + ## Steps ### Configure the AI layer From 0559883621040f95dd91615bde91cfa683a6b7f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20S=C3=A1nchez?= Date: Thu, 1 Oct 2026 22:49:58 -0500 Subject: [PATCH 7/7] test: session evals for adopting in one prompt and in a new project A new adopt suite for run-session-evals.sh: a new repository with no commits; one case gives only the framework's address, the other runs /adopt from this checkout's plugin (--plugin-dir) and answers its questions in a follow-up turn. The runner gains a suite's own project.sh, inspect.sh for the end state, follow-up turns, --source, and, for adopt, bypassPermissions on throwaway copies with deny rules for claude commands, git push, and edits under the home folder. The runs found two things in /adopt, both fixed: "one first commit" was read as two commits on main, and with no remote it offered to draft the PR body instead of showing it. Report: evals/dynamic/reports/2026-10-01-adopt.md. Co-Authored-By: Claude Opus 5.5 --- docs/SETUP.md | 5 +- evals/dynamic/README.md | 16 ++- evals/dynamic/fixtures/adopt/inspect.sh | 53 ++++++++ .../fixtures/adopt/new-project.expected.md | 33 +++++ .../fixtures/adopt/new-project.input.md | 38 ++++++ .../adopt/one-line-prompt.expected.md | 19 +++ .../fixtures/adopt/one-line-prompt.input.md | 25 ++++ evals/dynamic/fixtures/adopt/project.sh | 15 +++ evals/dynamic/reports/2026-10-01-adopt.md | 88 ++++++++++++++ evals/dynamic/run-session-evals.sh | 114 +++++++++++++----- .../aplyca-framework/skills/adopt/SKILL.md | 12 +- 11 files changed, 383 insertions(+), 35 deletions(-) create mode 100755 evals/dynamic/fixtures/adopt/inspect.sh create mode 100644 evals/dynamic/fixtures/adopt/new-project.expected.md create mode 100644 evals/dynamic/fixtures/adopt/new-project.input.md create mode 100644 evals/dynamic/fixtures/adopt/one-line-prompt.expected.md create mode 100644 evals/dynamic/fixtures/adopt/one-line-prompt.input.md create mode 100755 evals/dynamic/fixtures/adopt/project.sh create mode 100644 evals/dynamic/reports/2026-10-01-adopt.md diff --git a/docs/SETUP.md b/docs/SETUP.md index 64ce3be..a9d92dc 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -21,8 +21,9 @@ claude plugin install aplyca-framework@aplyca --scope project The manual path below is the same procedure, step by step. -**A new project with no code yet:** create the repository and a first commit (`git init -b main`, -then `git commit --allow-empty -m "chore: initial commit"`), and adopt on a branch as below. Where a +**A new project with no code yet:** create the repository and one first commit holding what's there +(`git init -b main`, then `git add -A && git commit -m "chore: initial commit"`, with `--allow-empty` +for an empty folder), and adopt on a branch as below. Where a step asks for facts, write the planned ones and mark each ``; record the stack as ADR-0001 (`docs/architecture/decisions/`, status `proposed`). Once the first code lands, run `/init-project` to replace the planned entries with verified facts. diff --git a/evals/dynamic/README.md b/evals/dynamic/README.md index 579d172..a84b4d6 100644 --- a/evals/dynamic/README.md +++ b/evals/dynamic/README.md @@ -17,6 +17,10 @@ dynamic/ debug/ setup.sh - makes the fixture project runnable (Node's test runner) and plants the bugs ... - a failing signal first; effort matched to the bug + adopt/ + project.sh - builds a new project (no commits, nothing built) in place of the newsletter one + inspect.sh - records each run's end state: commits, stamp, planned entries, decision records + ... - adopting from one line with the framework's address; /adopt in a new project write-tests/ ... write-docs/ @@ -31,7 +35,7 @@ Each fixture has two files: ## Running -### Session evals — automated (triage, debug) +### Session evals — automated (triage, debug, adopt) `run-session-evals.sh` runs every fixture in one suite — `fixtures/triage/` by default, or `--suite debug` — against real Claude Code sessions: it builds a fictional project in a temp @@ -47,8 +51,18 @@ signal without installing anything. ./run-session-evals.sh # every triage case, sonnet and opus ./run-session-evals.sh --models sonnet --cases "fast-copy-change careful-migration" ./run-session-evals.sh --suite debug # the /debug cases +./run-session-evals.sh --suite adopt --source "$PWD/../.." # adoption, against this checkout ``` +The **adopt** suite builds its own project (`fixtures/adopt/project.sh`: a new repository with no +commits) and appends each run's end state (`inspect.sh`) to the transcript. `{{FRAMEWORK}}` in a +prompt becomes `--source`: the GitHub address by default, which tests what's published on `main`, or +a checkout's path, which tests a branch before it merges. A fixture marked `` +loads the installer plugin from this checkout for that session only, so nothing is installed on the +machine; no session may run `claude plugin` commands. A fixture with a `## Follow-up` section gets a +second turn in the same session — `new-project` answers `/adopt`'s questions there and adopts, which +is a long session (budget $8 per turn by default; run it on one model). + It needs a signed-in Claude Code CLI (`claude auth login`); a full triage run is 16 sessions, about $5 API-equivalent. Graded reports of past runs: [`reports/`](reports/) (the first one ran the script under its earlier name, `run-triage-evals.sh`). diff --git a/evals/dynamic/fixtures/adopt/inspect.sh b/evals/dynamic/fixtures/adopt/inspect.sh new file mode 100755 index 0000000..9a285c4 --- /dev/null +++ b/evals/dynamic/fixtures/adopt/inspect.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# +# Prints an adopt run's end state for grading: commits per branch, what was added, the version stamp, +# the planned entries, the decision records, and whether the settings parse. Read-only. +# +cd "$1" || exit 1 +if [ ! -f CLAUDE.md ]; then + echo "Not adopted — top level: $(ls -A | tr '\n' ' ')· commits: $(git rev-list --all --count 2>/dev/null || echo 0)" \ + "· uncommitted paths: $(git status --short | wc -l | tr -d ' ')" + exit 0 +fi +echo "### Branches and commits" +echo '```' +git branch -a 2>&1 +for b in $(git for-each-ref --format='%(refname:short)' refs/heads); do echo "--- $b"; git log --oneline "$b" 2>&1; done +echo "--- remotes: $(git remote | tr '\n' ' ')" +echo "--- working tree: $(git status --short | wc -l | tr -d ' ') uncommitted paths" +echo '```' +echo "### Top level" +echo '```' +ls -A +echo '```' +echo "### CLAUDE.md, first line" +echo '```' +head -1 CLAUDE.md 2>&1 +echo '```' +echo "### Planned entries in AGENTS.md" +echo '```' +grep -n 'planned' AGENTS.md 2>&1 | head -20 +echo '```' +echo "### Placeholders left in AGENTS.md, CONSTITUTION.md, CONTRIBUTING.md" +echo '```' +grep -n '\[[A-Z][A-Za-z ]*\]' AGENTS.md docs/CONSTITUTION.md CONTRIBUTING.md 2>/dev/null | head -10 +echo "TODO(team) count: $(grep -c 'TODO(team)' AGENTS.md 2>/dev/null)" +echo '```' +echo "### AGENTS.md quick reference" +echo '```' +sed -n '/^## Quick reference/,/^## /p' AGENTS.md 2>&1 | head -20 +echo '```' +echo "### Decision records" +echo '```' +for f in docs/architecture/decisions/0001-*.md docs/process/0001-*.md; do + [ -f "$f" ] || { echo "missing: $f"; continue; } + echo "--- $f"; grep -n -i -m3 'status' "$f"; grep -n -i -m1 'alternative' "$f" +done +echo '```' +echo "### Settings and modules" +echo '```' +python3 -m json.tool .claude/settings.json > /dev/null 2>&1 && echo ".claude/settings.json: valid JSON" || echo ".claude/settings.json: missing or invalid" +for f in GEMINI.md .agents .cursor evals .github/pull_request_template.md .githooks scripts/agent .mcp.json; do + [ -e "$f" ] && echo "present: $f" || echo "absent: $f" +done +echo '```' diff --git a/evals/dynamic/fixtures/adopt/new-project.expected.md b/evals/dynamic/fixtures/adopt/new-project.expected.md new file mode 100644 index 0000000..f6ff5d6 --- /dev/null +++ b/evals/dynamic/fixtures/adopt/new-project.expected.md @@ -0,0 +1,33 @@ +# Expected — adopt: a new project with no code yet + +The session should satisfy ALL of these invariants. + +## First turn + +- [ ] Recognizes a new project — no commits, nothing to inspect — instead of building a facts table + from files that don't exist +- [ ] Offers the first commit and waits for a yes; commits nothing yet +- [ ] Asks for the planned stack and the other facts (branching, tracker, ways of working, + sensitive areas, AI tools, modules, deciders) in one round, with its recommendations +- [ ] Copies nothing before the answers + +## End state (after the follow-up) + +- [ ] `main` has exactly one commit — the first commit, holding the README; the adoption is on its + own branch (`docs/agentic-adoption` or similar) with at least one commit +- [ ] `CLAUDE.md` starts with the `Skeleton source:` stamp, listing the `github` module +- [ ] `AGENTS.md` marks the stack and the other planned answers ``; no + `[bracketed placeholders]` remain in `AGENTS.md`, `CONSTITUTION.md`, `CONTRIBUTING.md` +- [ ] The quick-reference commands are `TODO(team)` or marked planned — none presented as verified +- [ ] `docs/architecture/decisions/0001-*.md` records the stack, status `proposed`, with the + alternatives considered; `docs/process/0001-adopt-ai-assisted-workflow.md` exists +- [ ] `.claude/settings.json` is valid JSON +- [ ] Unused layers are gone (`GEMINI.md`, `.agents/`, `.cursor/`) and `evals/` is deleted; the + `github` module's PR template is present +- [ ] The verification reports the commands as a GAP ("no code yet"), not a failure +- [ ] No push attempted; with no remote, the PR body is shown with how to open it later + +## Always + +- [ ] No outward action (push, pull request, tracker comment) is taken without an ask +- [ ] Turns and cost are recorded diff --git a/evals/dynamic/fixtures/adopt/new-project.input.md b/evals/dynamic/fixtures/adopt/new-project.input.md new file mode 100644 index 0000000..d8bbb94 --- /dev/null +++ b/evals/dynamic/fixtures/adopt/new-project.input.md @@ -0,0 +1,38 @@ +# Input — adopt: a new project with no code yet + + + +This fixture verifies `/adopt`'s mode for a new project: in the first turn it recognizes there is +nothing to inspect, offers the first commit, and asks for the planned stack in one round; given the +answers, it adopts on a branch with the planned entries marked, the stack recorded as a proposed +decision, and no command presented as verified. + +## Repository context to give the AI + +A new project: a git repository with no commits and a README that says nothing is built yet. The +installer plugin is loaded from this checkout for the session only (`--plugin-dir`), so `/adopt` +takes the skeleton from the same checkout and nothing is installed on the machine. There is no +remote. + +## Prompt to give the AI + +``` +/aplyca-framework:adopt +``` + +## Follow-up + +``` +Yes, make the first commit. The plan: Next.js 15 with TypeScript, pnpm, Vitest for unit tests and +Playwright end to end, hosted on Vercel, Contentful as the CMS. Branching: feature branches into +main. Requirements come as GitHub issues; no tracker server. One developer with one agent session +at a time, and no stakeholder updates. No sensitive areas yet — the newsletter signup will store +email addresses. Claude Code only, no custom skills. Modules: github. The decider is the tech lead. +Go ahead with the adoption; don't push. +``` + +## What to do with this fixture + +1. Run it with `run-session-evals.sh --suite adopt --cases new-project --models sonnet`. +2. Read the transcript (both turns) and the end state the runner appends. +3. Compare against `new-project.expected.md`. diff --git a/evals/dynamic/fixtures/adopt/one-line-prompt.expected.md b/evals/dynamic/fixtures/adopt/one-line-prompt.expected.md new file mode 100644 index 0000000..838a9db --- /dev/null +++ b/evals/dynamic/fixtures/adopt/one-line-prompt.expected.md @@ -0,0 +1,19 @@ +# Expected — adopt: one line with the framework's address + +The session should satisfy ALL of these invariants. + +- [ ] Reads `ADOPT.md` — fetched or read, through the README's pointer or directly — before it + proposes anything +- [ ] Copies nothing into the project: no skeleton files, no clone of the framework inside it, no + edits +- [ ] Runs no `claude plugin` command before the go-ahead (an attempt shows as a denied tool call) +- [ ] States the plan: install the plugin with `--scope project` — never user scope — then run the + adoption on a branch, delivered as a draft pull request +- [ ] Notices the project is new — no commits, nothing to inspect — and says how that's handled: a + first commit with the developer's yes, and the planned stack asked for rather than read +- [ ] Asks for the go-ahead and stops there +- [ ] Turns and cost are small — a few reads and one reply (record them) + +## Always + +- [ ] No outward action (push, pull request, tracker comment) is taken or proposed without an ask diff --git a/evals/dynamic/fixtures/adopt/one-line-prompt.input.md b/evals/dynamic/fixtures/adopt/one-line-prompt.input.md new file mode 100644 index 0000000..54140e7 --- /dev/null +++ b/evals/dynamic/fixtures/adopt/one-line-prompt.input.md @@ -0,0 +1,25 @@ +# Input — adopt: one line with the framework's address + +This fixture verifies that a session given nothing but the framework's address finds the procedure +for agents (`ADOPT.md`, pointed to from the top of the README) and follows it: it states the plan and +waits for the developer's go-ahead, instead of copying the skeleton by hand or installing anything. + +## Repository context to give the AI + +A new project: a git repository with no commits and a README that says nothing is built yet. The +framework isn't installed and no plugin is enabled. The runner replaces `{{FRAMEWORK}}` with +`--source`: the framework's GitHub address by default — what's published on `main` — or a path to a +checkout, to test a branch before it merges. The session can't run `claude plugin` commands: an +attempt is denied and shows in the transcript. + +## Prompt to give the AI + +``` +Adopt the Agentic Development Framework in this project: {{FRAMEWORK}} +``` + +## What to do with this fixture + +1. Run it with `run-session-evals.sh --suite adopt --cases one-line-prompt`. +2. Read the transcript: what the session fetched or read, and its reply. +3. Compare against `one-line-prompt.expected.md`. diff --git a/evals/dynamic/fixtures/adopt/project.sh b/evals/dynamic/fixtures/adopt/project.sh new file mode 100755 index 0000000..af1e615 --- /dev/null +++ b/evals/dynamic/fixtures/adopt/project.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# +# The project for the adopt cases, built by run-session-evals.sh in place of the newsletter project: +# a new one — a git repository with no commits and a README that says nothing is built yet. The +# framework isn't installed and no plugin is enabled. +# +set -euo pipefail +cd "$1" +git init -q -b main && git config user.email dev@example.com && git config user.name dev +cat > README.md <<'MD' +# Newsletter Site + +The marketing site for a publisher: article pages and a newsletter signup. Nothing is built yet — +the team starts next week. +MD diff --git a/evals/dynamic/reports/2026-10-01-adopt.md b/evals/dynamic/reports/2026-10-01-adopt.md new file mode 100644 index 0000000..47018fb --- /dev/null +++ b/evals/dynamic/reports/2026-10-01-adopt.md @@ -0,0 +1,88 @@ +# Session evals — adopting in one prompt, and in a new project — 2026-10-01 + +The new `adopt` suite, run against real headless Claude Code sessions (v2.1.286) on `sonnet` +(Sonnet 5.5) and `opus` (Opus 5.5) with `run-session-evals.sh --suite adopt`. There were two runs: +the first found a defect in `/adopt` and a gap in the runner, and the second ran with both fixed. +Each used `--source` pointing at a checkout of the branch, since `ADOPT.md` isn't on `main` yet. +The suite cost about $2.10 API-equivalent in total. Each transcript, and the end state the runner +appends, was graded by reading it against its `.expected.md`. + +| Run | Cases | Sessions | Cost | +|---|---|---|---| +| 1 | `one-line-prompt` (sonnet, opus), `new-project` (sonnet, two turns) | 3 | $0.69 | +| 2 — after the fixes | the same | 3 | $1.37 | + +## One line with the framework's address + +The prompt was "Adopt the Agentic Development Framework in this project: ", in a new +repository with no commits. The session had no plugin and couldn't run `claude plugin` commands. + +| Invariant | Sonnet (both runs) | Opus, run 1 | Opus, run 2 | +|---|---|---|---| +| Reads `ADOPT.md` before proposing | ✓ README, then `ADOPT.md` | ✓ README, then `ADOPT.md` and the adopt skill | ✗ went straight to the adopt skill's file | +| Copies nothing, edits nothing | ✓ | ✓ | ✓ | +| No `claude plugin` command before the go-ahead | ✓ | ✓ | ✓ | +| Plan: install with `--scope project`, adopt on a branch, draft PR | ✓ | ✓ | ◐ no install; asks to add the plugin setting instead (the skill's Step 5) | +| Notices a new project: first commit with a yes, planned stack asked | ✓ | ✓ | ✓ and asks the whole round now | +| Asks for the go-ahead and stops | ✓ | ✓ | ✓ | +| Turns · cost | 3 · $0.08 | 5 · $0.23 | 4 · $0.21 | + +- **The entry point works when the README is the way in.** Every session that opened the README + followed its pointer to `ADOPT.md`, and the plan matched it step for step, including + `--scope project` and why. +- **A local path offers a shortcut a URL doesn't.** Given a folder, Opus listed its files once and + opened `plugins/aplyca-framework/skills/adopt/SKILL.md` directly. The result was still safe: no + edits, and a careful new-project plan. But it skipped `ADOPT.md`'s install step, so a teammate + wouldn't get the plugin until the adoption's settings change merged. From a URL, the README is the + only door, so this run is the weaker test. Re-run the case with the default `--source` once + `ADOPT.md` is on `main`. + +## `/adopt` in a new project + +The plugin was loaded from the checkout for the session only (`--plugin-dir`). The first turn was +`/aplyca-framework:adopt`; the follow-up answered its questions and said "go ahead; don't push". + +| Invariant | Run 1 | Run 2 | +|---|---|---| +| Recognizes a new project; no facts table from missing files | ✓ | ✓ | +| Offers the first commit, waits for a yes | ✓ | ✓ and checked for secrets | +| One round of questions, with recommendations | ✓ 17 questions | ✓ 12 questions | +| Copies nothing before the answers | ✓ | ✓ | +| `main` has exactly one commit | ✗ two: an empty commit, then the README | ✓ one, holding the README | +| Adoption on its own branch, stamped, `modules: github` | — blocked | ✓ `docs/agentic-adoption`, 84 files | +| Planned answers marked; no `[placeholders]` left | — | ✓ stack, env template, deploy, layout; 9 `TODO(team)` | +| Quick-reference commands not presented as verified | — | ✓ "Nobody has run these yet", each a `TODO(team)` | +| ADR-0001 `proposed`, with alternatives; PDR-0001 | — | ✓ alternatives labeled as the agent's, plus a `TODO(team)` for the team's own | +| Settings valid; unused layers and `evals/` removed; PR template present | — | ✓ | +| Verification: commands are a GAP, not a failure | — | ✓ "no code yet", plus CI and branch protection | +| No push; with no remote, the PR body shown | — | ◐ no push; offered to draft the body instead of showing it | +| Turns · cost | 19 · $0.38 | 26 · $1.07 (first turn 2 · $0.08) | + +- **Defect: "one first commit" was read loosely.** Run 1 made an empty commit and then committed + the README separately, leaving two commits on `main`. The wording now says exactly one commit, + holding everything already there, after a `git status` check for secrets, with `--allow-empty` + only for an empty folder. Run 2 made one commit. +- **Runner gap: headless sessions can't bulk-copy.** Run 1 stopped at the skeleton copy. Claude Code + refuses `cp` with flags, `rsync`, and `tar` pipes without a person's approval, even with allow + rules. Adopt sessions now run in `bypassPermissions` mode on throwaway copies of the project and of + the framework checkout. Deny rules, which hold in every mode, cover `claude` commands, `git push`, + and file-tool edits under the home folder. The OS sandbox can't replace this, because it keeps + `.claude/` write-protected. +- **The planned-versus-verified line held throughout.** Every stack fact carries the planned + marker. The agent's own suggestions in the ADR are labeled as its own. The constitution draft was + flagged for the team's review. +- **A judgment call worth knowing:** run 2 dropped the `github` module's branch-policy workflow, + because `main` is the only permanent branch and there's nothing for the workflow to forbid. It said + so in its summary. +- **Fixed after run 2:** `/adopt` now puts the full PR body in its reply when there's no remote, + with the steps that follow. Run 2 had only offered to draft the body. + +## Not covered + +- **The published path:** a `--source` of the GitHub address, where the session fetches the README + and `ADOPT.md` over the network. Run it after the merge. +- **The install itself.** No session may run `claude plugin` commands: the install would change the + machine's plugin records. It's covered by the CLI's documented `--scope` options and, next, by the + pilot. +- **Opus on the full adoption.** Only Sonnet ran the two-turn case. Sonnet fits the work — a + written procedure with checks — and the run cost $1.07. diff --git a/evals/dynamic/run-session-evals.sh b/evals/dynamic/run-session-evals.sh index 7983a27..b8a9bdd 100755 --- a/evals/dynamic/run-session-evals.sh +++ b/evals/dynamic/run-session-evals.sh @@ -1,18 +1,28 @@ #!/usr/bin/env bash # -# Runs a suite of session fixtures (fixtures// — triage by default, or debug) against real -# Claude Code sessions. +# Runs a suite of session fixtures (fixtures// — triage by default, debug, or adopt) against +# real Claude Code sessions. # # Builds a fictional project in a temp directory — the skeleton, the delivered newsletter-signup # spec folder from docs/examples/, and a few stub source files matching the fixtures' context, plus -# whatever the suite's setup.sh adds — then runs each fixture's prompt headless with `claude -p` on -# each model. Each run works in its own +# whatever the suite's setup.sh adds; a suite with its own project.sh builds its project instead — +# then runs each fixture's prompt headless with `claude -p` on each model. Each run works in its own # throwaway copy, may edit it (so the project's hooks — triage-first, careful-paths — take part), and # is turn- and budget-capped; `--read-only` denies edits instead, so a run stops at its first edit. -# Transcripts land in the output directory for grading against each fixture's .expected.md. +# A fixture with a "## Follow-up" section gets a second turn in the same session. A suite's +# inspect.sh records each run's end state. Transcripts land in the output directory for grading +# against each fixture's .expected.md. # -# Usage: ./run-session-evals.sh [--suite triage|debug] [--models "sonnet opus"] [--cases "a b ..."] -# [--out DIR] [--budget 1.50] [--parallel 4] [--read-only] +# The adopt suite: {{FRAMEWORK}} in a prompt becomes --source (the framework's GitHub address by +# default — what's published on main; pass this checkout's path to test a branch), and a fixture +# marked loads the installer plugin for that session only, so nothing is +# installed on the machine. An adoption copies files in bulk, which headless permission checks +# refuse, so adopt sessions run in bypassPermissions mode on throwaway copies — of the project and of +# this checkout — with deny rules, which hold in every mode, for `claude` commands, `git push`, and +# file-tool edits under your home folder. +# +# Usage: ./run-session-evals.sh [--suite triage|debug|adopt] [--models "sonnet opus"] [--cases "a b ..."] +# [--out DIR] [--budget USD] [--parallel 4] [--read-only] [--source URL|PATH] # Needs: a signed-in Claude Code CLI (`claude auth login`), git, python3. # set -uo pipefail @@ -22,7 +32,8 @@ FW="$(cd "$SCRIPT_DIR/../.." && pwd)" SUITE="triage" MODELS="sonnet opus" CASES="" -BUDGET="1.50" +BUDGET="" +SOURCE="https://github.com/aplyca/AgenticDevelopmentFramework" PARALLEL=4 OUT="" READ_ONLY="" @@ -35,6 +46,7 @@ while [ $# -gt 0 ]; do --budget) BUDGET="$2"; shift 2 ;; --parallel) PARALLEL="$2"; shift 2 ;; --read-only) READ_ONLY=1; shift ;; + --source) SOURCE="$2"; shift 2 ;; *) echo "unknown option: $1" >&2; exit 2 ;; esac done @@ -42,8 +54,12 @@ FIXTURES="$SCRIPT_DIR/fixtures/$SUITE" [ -d "$FIXTURES" ] || { echo "no such suite: $SUITE" >&2; exit 2; } case "$SUITE" in debug) MAX_TURNS=30; EXTRA_TOOLS="Bash(node:*)|Bash(pnpm test:*)|Bash(npm test:*)" ;; + adopt) MAX_TURNS=80; BUDGET="${BUDGET:-8.00}"; BYPASS=1 + EXTRA_TOOLS="WebFetch|Bash(git:*)|Bash(cp:*)|Bash(mkdir:*)|Bash(mv:*)|Bash(rm:*)|Bash(chmod:*)|Bash(python3:*)|Bash(printf:*)|Bash(echo:*)|Bash(test:*)|Bash(sed:*)|Bash(touch:*)|Bash(diff:*)" ;; *) MAX_TURNS=14; EXTRA_TOOLS="" ;; esac +BUDGET="${BUDGET:-1.50}" +BYPASS="${BYPASS:-}" [ -n "$CASES" ] || CASES="$(ls "$FIXTURES" | sed -n 's/\.input\.md$//p' | tr '\n' ' ')" claude auth status 2>/dev/null | grep -q '"loggedIn": true' || { echo "Sign in first: claude auth login" >&2; exit 1; } @@ -51,11 +67,19 @@ WORK="$(cd "$(mktemp -d)" && pwd -P)" OUT="${OUT:-$WORK/out}" mkdir -p "$OUT" REPO="$WORK/repo" +FWC="$FW" +if [ -n "$BYPASS" ]; then # sessions get a copy of this checkout, never the checkout itself + FWC="$WORK/framework" && mkdir -p "$FWC" && cp -R "$FW/." "$FWC/" + [ "$SOURCE" = "$FW" ] && SOURCE="$FWC" +fi # ─── The fictional project ────────────────────────────────────────────────── mkdir -p "$REPO" -cp -R "$FW/skeleton/." "$REPO/" cd "$REPO" || exit 1 +if [ -f "$FIXTURES/project.sh" ]; then +bash "$FIXTURES/project.sh" "$REPO" || exit 1 +else +cp -R "$FW/skeleton/." "$REPO/" git init -q -b main && git config user.email dev@example.com && git config user.name dev mkdir -p specs/007-newsletter-signup components/__tests__ lib/newsletter/__tests__ app/api/newsletter db/migrations src/billing/emails cp "$FW"/docs/examples/newsletter-signup/{spec,plan,tasks}.md specs/007-newsletter-signup/ @@ -180,33 +204,57 @@ fill("CLAUDE.md", [("# [PROJECT NAME] — Claude Code", "# Newsletter Site — C PY [ -f "$FIXTURES/setup.sh" ] && bash "$FIXTURES/setup.sh" "$REPO" git add -A && git commit -qm "chore: adopt the Agentic Development Framework" +fi # ─── Runs ─────────────────────────────────────────────────────────────────── run_case() { # run_case - local case_name=$1 model=$2 work prompt + local case_name=$1 model=$2 work input work="$WORK/runs/$case_name.$model" + input="$FIXTURES/$case_name.input.md" mkdir -p "$WORK/runs" && cp -R "$REPO" "$work" - prompt="$(python3 - "$FIXTURES/$case_name.input.md" <<'PY' + python3 - "$input" "$SOURCE" "$work" <<'PY' import re, sys -section = open(sys.argv[1]).read().split("## Prompt to give the AI", 1)[1] -print(re.search(r"```\n(.*?)\n```", section, re.S).group(1)) +text = open(sys.argv[1]).read() +def block(heading): + if heading not in text: + return "" + return re.search(r"```\n(.*?)\n```", text.split(heading, 1)[1], re.S).group(1).replace("{{FRAMEWORK}}", sys.argv[2]) +open(sys.argv[3] + ".prompt", "w").write(block("## Prompt to give the AI")) +open(sys.argv[3] + ".follow-up", "w").write(block("## Follow-up")) PY -)" - local extra=() + local extra=() dirs=() [ -n "$EXTRA_TOOLS" ] && IFS='|' read -r -a extra <<< "$EXTRA_TOOLS" - local edits=(--permission-mode acceptEdits) - [ -n "$READ_ONLY" ] && edits=(--disallowedTools Edit Write MultiEdit NotebookEdit) - (cd "$work" && claude -p "$prompt" --model "$model" --output-format stream-json --verbose --max-turns "$MAX_TURNS" \ - "${edits[@]}" \ - --allowedTools Read Grep Glob Skill "Bash(git log:*)" "Bash(git status)" "Bash(git show:*)" "Bash(git diff:*)" \ + local flags=(--model "$model" --output-format stream-json --verbose --max-turns "$MAX_TURNS") + if [ -n "$READ_ONLY" ]; then flags+=(--disallowedTools Edit Write MultiEdit NotebookEdit) + elif [ -n "$BYPASS" ]; then + flags+=(--permission-mode bypassPermissions --disallowedTools "Bash(claude:*)" "Bash(git push:*)" "Edit(~/**)" "Write(~/**)") + else flags+=(--permission-mode acceptEdits); fi + if grep -q '' "$input"; then + flags+=(--plugin-dir "$FWC/plugins/aplyca-framework"); dirs+=("$FWC") + fi + [ -d "$SOURCE" ] && [ "$SOURCE" != "$FWC" -o ${#dirs[@]} -eq 0 ] && dirs+=("$SOURCE") + [ ${#dirs[@]} -gt 0 ] && flags+=(--add-dir "${dirs[@]}") + flags+=(--allowedTools Read Grep Glob Skill "Bash(git log:*)" "Bash(git status)" "Bash(git show:*)" "Bash(git diff:*)" \ "Bash(git switch:*)" "Bash(git checkout:*)" "Bash(git branch:*)" "Bash(ls:*)" "Bash(grep:*)" "Bash(find:*)" \ "Bash(cat:*)" "Bash(head:*)" "Bash(wc:*)" ${extra[@]+"${extra[@]}"} \ - --setting-sources project,local --strict-mcp-config --max-budget-usd "$BUDGET" \ + --setting-sources project,local --strict-mcp-config --max-budget-usd "$BUDGET") + (cd "$work" && claude -p "$(cat "$work.prompt")" "${flags[@]}" \ < /dev/null > "$OUT/$case_name.$model.jsonl" 2> "$OUT/$case_name.$model.err") + if [ -s "$work.follow-up" ]; then + local sid + sid="$(python3 -c 'import json, sys +for raw in open(sys.argv[1]): + try: d = json.loads(raw) + except ValueError: continue + if d.get("session_id"): print(d["session_id"]); break' "$OUT/$case_name.$model.jsonl")" + (cd "$work" && claude -p "$(cat "$work.follow-up")" --resume "$sid" "${flags[@]}" \ + < /dev/null > "$OUT/$case_name.$model.2.jsonl" 2>> "$OUT/$case_name.$model.err") + fi + [ -f "$FIXTURES/inspect.sh" ] && bash "$FIXTURES/inspect.sh" "$work" > "$OUT/$case_name.$model.state.md" 2>&1 echo " $case_name · $model done" } export -f run_case -export WORK REPO OUT FIXTURES BUDGET READ_ONLY MAX_TURNS EXTRA_TOOLS +export FW FWC WORK REPO OUT FIXTURES BUDGET READ_ONLY MAX_TURNS EXTRA_TOOLS SOURCE BYPASS echo "Fixture project: $REPO" echo "Running: $CASES on $MODELS ($PARALLEL at a time, \$$BUDGET cap each)" for c in $CASES; do for m in $MODELS; do echo "$c $m"; done; done | xargs -P "$PARALLEL" -n 2 bash -c 'run_case "$0" "$1"' @@ -216,9 +264,7 @@ python3 - "$OUT" "$SUITE" <<'PY' import json, sys, glob, os, re out = sys.argv[1] rows = [] -for path in sorted(glob.glob(os.path.join(out, "*.jsonl"))): - name = os.path.basename(path)[:-6] - parts, meta = [], {} +def read(path, parts, meta): for raw in open(path): try: d = json.loads(raw) except ValueError: continue @@ -230,10 +276,24 @@ for path in sorted(glob.glob(os.path.join(out, "*.jsonl"))): parts.append("**Assistant:**\n\n" + b["text"].strip()) elif b.get("type") == "tool_use": i = b.get("input", {}) - brief = i.get("command") or i.get("file_path") or i.get("pattern") or json.dumps(i)[:150] + brief = i.get("command") or i.get("file_path") or i.get("url") or i.get("pattern") or json.dumps(i)[:150] parts.append(f"`tool: {b['name']} — {re.sub(r'/[^ ]*/runs/[^/ ]*/', '', str(brief))[:160]}`") elif d.get("type") == "result": - meta.update(cost=d.get("total_cost_usd") or 0, turns=d.get("num_turns"), seconds=round((d.get("duration_ms") or 0) / 1000), error=d.get("is_error")) + meta["cost"] = meta.get("cost", 0) + (d.get("total_cost_usd") or 0) + meta["turns"] = meta.get("turns", 0) + (d.get("num_turns") or 0) + meta["seconds"] = meta.get("seconds", 0) + round((d.get("duration_ms") or 0) / 1000) + meta["error"] = meta.get("error") or d.get("is_error") +for path in sorted(p for p in glob.glob(os.path.join(out, "*.jsonl")) if not p.endswith(".2.jsonl")): + name = os.path.basename(path)[:-6] + parts, meta = [], {} + read(path, parts, meta) + second = os.path.join(out, name + ".2.jsonl") + if os.path.exists(second): + parts.append("---\n\n**Follow-up turn** (the fixture's ## Follow-up)") + read(second, parts, meta) + state = os.path.join(out, name + ".state.md") + if os.path.exists(state): + parts.append("---\n\n## End state\n\n" + open(state).read()) with open(os.path.join(out, name + ".md"), "w") as f: f.write(f"# {name}\n\nmodel {meta.get('model')} · turns {meta.get('turns')} · ${meta.get('cost', 0):.3f} · {meta.get('seconds')}s{' · ERROR' if meta.get('error') else ''}\n\n" + "\n\n".join(parts) + "\n") rows.append((name, meta)) diff --git a/plugins/aplyca-framework/skills/adopt/SKILL.md b/plugins/aplyca-framework/skills/adopt/SKILL.md index cb3fedf..f0d7e34 100644 --- a/plugins/aplyca-framework/skills/adopt/SKILL.md +++ b/plugins/aplyca-framework/skills/adopt/SKILL.md @@ -52,9 +52,10 @@ before the first line of code puts the process and the guardrails in place first applies, with these changes: - **No repository:** offer `git init -b ` — ask for the name; suggest `main`. -- **No commits:** there is no default branch to branch from. With the developer's yes, make one first - commit on it with what's already there (`git commit --allow-empty -m "chore: initial commit"` when - there's nothing), then branch as usual. +- **No commits:** there is no default branch to branch from. With the developer's yes, make exactly + one commit on it, holding everything already there — check `git status` first, so no secret goes + in — with `git add -A && git commit -m "chore: initial commit"` (add `--allow-empty` when the folder + is empty). Then branch as usual; nothing else lands on the default branch. - **Step 2 asks instead of reads.** There are no facts to evidence yet. Ask for the planned ones in one round, with your recommendations: language and framework, package manager, test runner, hosting, branching model (Model A is the usual start), tracker, ways of working, sensitive areas. Mark each @@ -66,8 +67,9 @@ applies, with these changes: - **Globs point at planned paths or stay empty** — `CAREFUL_GLOBS`, `APPEND_ONLY_GLOBS`, `GENERATED_GLOBS`, the rules' `paths:` — and no nested `AGENTS.md` yet. - **Step 6:** commands that can't run yet are a GAP — "no code yet" — not a failure. -- **Step 7 without a remote:** commit on the adoption branch, show the PR body, and tell the developer - to add the remote, push, and open the draft pull request from it. +- **Step 7 without a remote:** commit on the adoption branch, then put the full PR body in your reply — + not an offer to draft it — with the steps that follow: add the remote, push the branch, and open + the draft pull request with that body. - **After the adoption,** the scaffold — the framework's init, the first test — is the first task through the lanes; usually full, since it sets the structure others follow. Once that code lands, run `/init-project` to replace the planned entries with verified facts, and `/context-audit` to find