Skip to content

Commit 7c613bd

Browse files
feat(amp): add deterministic native workflows (#132)
* feat(amp): add deterministic native workflows Generate a complete Amp target with explicit palette commands, read-only specialists, bounded parallel analysis, and lifecycle guards. Keep the source target byte-identical to the validated standalone distribution and cover the runtime policies with model-free tests and smoke checks. Co-authored-by: Oliver Kriska <oliver@ideax.sk> * fix(amp): align hosted profiles and local resolution Amp-Thread-ID: https://ampcode.com/threads/T-019ff034-17cd-735a-900c-f8ac5252b8f4 * fix(amp): fit hosted plugin transport Amp-Thread-ID: https://ampcode.com/threads/T-019ff034-17cd-735a-900c-f8ac5252b8f4 * fix(amp): generate both Amp plugins from one target build main added a second, hand-written Amp plugin (phx-watch-pr.ts, copied verbatim and byte-checked against its canonical source) while this branch added a first one (elixir-phoenix.ts, rendered from Python so it can embed projected agent bodies and skill metadata). Both landed on the same build_target(), the same validate_plugin(), the same OUTPUT_DIR, and the same snapshot digest. Neither generation style can replace the other: the workflow plugin has to be rendered because it inlines canonical content, and the watch plugin has to be copied because it is real TypeScript with no projected data. So the target now emits both. - validate_plugin() keeps main's signature and meaning (watch plugin, byte-identity against source). The rendered one becomes validate_workflow_plugin(), and PLUGIN_RELATIVE_PATH becomes WORKFLOW_PLUGIN_RELATIVE_PATH, so main's committed API is untouched and the rename stays confined to the incoming branch. - build_target() discovers specialists, copies the watch plugin, renders the workflow plugin, and returns skills + commands + plugins. - The runtime smoke test now execs both plugins: session.start for phx-watch-pr, agent.start for elixir-phoenix. - Docs and the runtime-support matrix describe two plugins rather than one, keeping both sides' capability rows. The transport budget needed the whole branch to land: after commit 1 the rendered plugin hit 98,745 bytes against its own 96,000 assert, because phx-watch-pr joined PORTABLE_WORKFLOWS and the shared specialist contract was still inlined five times. Commit 3's compaction brings it to 95,191 with 809 bytes of headroom. --------- Co-authored-by: Amp <amp@ampcode.com>
1 parent 874d2e4 commit 7c613bd

18 files changed

Lines changed: 4008 additions & 214 deletions

File tree

.github/workflows/lint.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,16 @@ jobs:
113113
with:
114114
python-version: "3.12"
115115

116+
- name: Setup Bun for Amp plugin runtime harness
117+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
118+
116119
- name: Install dependencies
117120
run: pip install -r requirements.txt
118121

119122
- name: Run pytest
120123
run: python3 -m pytest lab/eval/tests/ scripts/tests/ -v --tb=short
121124

122-
- name: Check Amp generated skills
125+
- name: Check Amp generated target
123126
run: python3 -m scripts.build_amp_skills --check
124127

125128
- name: Check Codex generated skills

.husky/pre-commit

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,16 @@ fi
7171
# source and projection land in the same commit. CI repeats the drift check from
7272
# the committed tree for contributors without Husky.
7373
if [ -n "$STAGED_CANONICAL_SKILLS" ]; then
74-
echo "→ Checking generated Amp skills..."
75-
if ! make amp-skills-validate > /tmp/precommit-amp-skills.log 2>&1; then
76-
echo "Amp skills are out of date. Run: make amp-skills-sync"
77-
tail -30 /tmp/precommit-amp-skills.log
74+
echo "→ Checking generated Amp target..."
75+
if ! make amp-target-validate > /tmp/precommit-amp-target.log 2>&1; then
76+
echo "Amp target is out of date. Run: make amp-target-sync"
77+
tail -30 /tmp/precommit-amp-target.log
7878
exit 1
7979
fi
8080

81-
UNTRACKED_AMP_SKILLS=$(git ls-files --others --exclude-standard -- targets/amp/skills)
82-
if ! git diff --quiet -- targets/amp/skills || [ -n "$UNTRACKED_AMP_SKILLS" ]; then
83-
echo "Generated Amp skill changes are not staged. Run: git add targets/amp/skills"
81+
UNTRACKED_AMP_TARGET=$(git ls-files --others --exclude-standard -- targets/amp)
82+
if ! git diff --quiet -- targets/amp || [ -n "$UNTRACKED_AMP_TARGET" ]; then
83+
echo "Generated Amp target changes are not staged. Run: git add targets/amp"
8484
exit 1
8585
fi
8686
fi

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
3535
search read the whole README and never learned the docs site exists. The
3636
repo's About → Website field was already set.
3737

38+
- **Deterministic Amp workflow plugin** — the generated Amp target now includes
39+
40 workflow palette commands, five read-only domain specialists, bounded
40+
parallel review and investigation, a persistent classified edit lock, and a
41+
bounded `phx-full` verification gate. The standalone distribution promotes a
42+
validated `stable` branch for direct GitHub installation.
43+
3844
### Changed
3945

4046
- **Release bodies now carry a docs-site footer** — the `release` contributor
@@ -45,6 +51,13 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4551
unique cloners from 51 to 120 in a single day (2.4x), decaying to baseline
4652
over about four days.
4753

54+
- **Amp installation profiles and scope names now match current Amp** — docs
55+
distinguish hosted-native from paired-full behavior, use native `skill:
56+
invoke` for deterministic hosted skill loading, and separate machine-local
57+
`--global` installs from personal/workspace hosted repositories. The public
58+
standalone repository, generated target, gated `stable` branch, paired local
59+
install, and GitHub/curl fallback remain supported.
60+
4861
- **`deep-bug-investigator` synthesizes on opus, its four tracks run on sonnet**
4962
(thanks @BugsBunny338, #133) — the agent coordinates four parallel
5063
investigation tracks and then reconciles their often-conflicting evidence.
@@ -115,6 +128,14 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
115128
all four Claude-facing manifests while the gate looked at one of them. All
116129
five manifests are validated now.
117130

131+
- **Amp wrapper skill resolution follows supported local precedence** — the
132+
generated plugin now checks machine-local skill roots before workspace roots
133+
and searches `.agents/skills` and `.claude/skills` through the parent
134+
directories exposed from `workspaceRoot`. Docs explicitly call out sources
135+
the Plugin API cannot resolve, including hosted repositories, built-ins,
136+
plugin caches, custom `amp.skills.path`, and nested invocation roots that are
137+
not exposed to plugins.
138+
118139
- **`displayName` removed from all Claude Code manifests** (reported by
119140
@ndrean, #130) — the field was introduced alongside the v3 plugin split and
120141
reported as an install blocker one day later. It is undocumented: it appears

Makefile

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: help lint lint-fix eval eval-all eval-fix eval-full eval-ci eval-triggers eval-tournament eval-skills eval-agents eval-multimodel eval-compare-models test validate amp-skills amp-skills-sync amp-skills-validate amp-runtime-smoke codex-skills codex-skills-sync codex-skills-validate codex-runtime-smoke pi-skills pi-skills-sync pi-skills-validate pi-runtime-smoke opencode-skills opencode-skills-sync opencode-skills-validate opencode-runtime-smoke generated-skills-sync generated-skills-snapshots generated-skills-snapshots-validate security ci clean
1+
.PHONY: help lint lint-fix eval eval-all eval-fix eval-full eval-ci eval-triggers eval-tournament eval-skills eval-agents eval-multimodel eval-compare-models test validate amp-target amp-target-sync amp-target-validate amp-skills amp-skills-sync amp-skills-validate amp-runtime-smoke codex-skills codex-skills-sync codex-skills-validate codex-runtime-smoke pi-skills pi-skills-sync pi-skills-validate pi-runtime-smoke opencode-skills opencode-skills-sync opencode-skills-validate opencode-runtime-smoke generated-skills-sync generated-skills-snapshots generated-skills-snapshots-validate security ci clean
22

33
# Default target
44
help: ## Show available commands
@@ -66,14 +66,20 @@ validate: ## Run claude plugin validate on every plugin + marketplace manifest
6666
@claude plugin validate plugins/catchup
6767
@claude plugin validate .
6868

69-
amp-skills: ## Generate Amp skills from the canonical Claude plugin
69+
amp-target: amp-skills ## Generate the complete Amp skills and workflow plugin target
70+
71+
amp-target-sync: amp-skills-sync ## Regenerate and verify the complete Amp target
72+
73+
amp-target-validate: amp-skills-validate ## Check the complete Amp target for generated drift
74+
75+
amp-skills: ## Generate the Amp skills and workflow plugin (backward-compatible name)
7076
@python3 -m scripts.build_amp_skills
7177

7278
amp-skills-sync: ## Regenerate and verify the committed Amp target
7379
@$(MAKE) amp-skills
7480
@$(MAKE) amp-skills-validate
7581

76-
amp-skills-validate: ## Check committed Amp skills for generated drift
82+
amp-skills-validate: ## Check the committed Amp target for generated drift
7783
@python3 -m scripts.build_amp_skills --check
7884

7985
amp-runtime-smoke: ## Optional: smoke-test local target with an isolated Amp runtime

README.md

Lines changed: 55 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ expertise, fresh context, and enforced [Iron Laws](#iron-laws-non-negotiable-rul
1616
that catch the bugs your tests won't.
1717

1818
**Using Amp?** Install the generated edition for the same 51 Elixir, Phoenix,
19-
LiveView, Ecto, Oban, testing, and security skills plus the optional native PR
20-
watch lifecycle plugin. See
21-
[Use with Amp](#use-with-amp) for the important differences from the full Claude
22-
Code plugin, or the [Amp install guide](https://phxagents.dev/install/amp/) on
23-
phxagents.dev.
19+
LiveView, Ecto, Oban, testing, and security skills, plus 40 deterministic
20+
workflow wrappers, five read-only specialist agents, parallel review/investigation, and
21+
native edit/verification guards, and the native PR watch lifecycle
22+
plugin. See [Use with Amp](#use-with-amp) for the
23+
important differences from the full Claude Code plugin, or the
24+
[Amp install guide](https://phxagents.dev/install/amp/) on phxagents.dev.
2425

2526
**Using Codex?** Install the native generated skills plugin for all 51 skills,
2627
including `$elixir-phoenix:phx-investigate` and
@@ -262,69 +263,64 @@ directories provide the legacy `/ecto:*` and `/lv:*` aliases.
262263

263264
### Use with Amp
264265

265-
Amp can install the plugin's 51 skills from the generated Agent Skills target.
266-
Project-local installation is recommended because it keeps the Elixir/Phoenix
267-
guidance scoped to the repository where it applies:
266+
Amp supports two profiles from the same generated snapshot:
267+
268+
- **Hosted-native:** publish the skills and plugin independently to Amp personal
269+
or workspace repositories. Use Amp's native `skill: invoke` command. The
270+
plugin still provides specialists, parallel analysis, and edit lock, but its
271+
filesystem wrappers cannot load hosted-only skills and native `phx-full` does
272+
not arm the plugin verification gate.
273+
- **Paired-full:** install matching skills and plugin locally. This preserves all
274+
40 `phx:*`/`ecto:*`/`lv:*` wrappers and the wrapper-activated `phx-full` gate.
275+
276+
Project-local paired installation is recommended for reproducible full behavior:
268277

269278
```bash
270-
# Install into one Elixir/Phoenix project
271279
cd /path/to/your-phoenix-project
280+
281+
# Install the 51 skills into this project
272282
amp skill add \
273-
https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/targets/amp/skills \
283+
https://github.com/oliver-kriska/amp-elixir-phoenix/tree/stable/skills \
274284
--target "$PWD/.agents/skills"
275285

276-
# Or install for every Amp workspace
277-
amp skill add \
278-
https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/targets/amp/skills \
279-
--global
286+
# Add deterministic workflows, read-only agents, and native guards
287+
mkdir -p .amp/plugins
288+
plugin=".amp/plugins/elixir-phoenix.ts"
289+
temporary="$(mktemp "${plugin}.XXXXXX")"
290+
curl --fail --silent --show-error --location \
291+
https://raw.githubusercontent.com/oliver-kriska/amp-elixir-phoenix/stable/plugins/elixir-phoenix.ts \
292+
--output "$temporary" && mv "$temporary" "$plugin"
280293
```
281294

282-
`phx-watch-pr` additionally needs the generated Amp plugin. Install it into the
283-
project where the worker Orb opens and watches PRs:
295+
Open Amp's command palette with `Ctrl+O`, run `skill: invoke`, choose
296+
`phx-investigate`, and send the bug details. This native path resolves the
297+
effective skill whether it is local, built-in, personal hosted, or workspace
298+
hosted. With paired-full, the familiar `phx: investigate`, `phx: review`,
299+
`ecto: n1-check`, and `lv: assigns` wrappers are also available; they inject the
300+
matching local skill for one turn. `phx: full` activates the plugin verification
301+
gate only through that wrapper, not through native `skill: invoke`.
302+
303+
`amp skill add --global` means **machine-local**, under
304+
`~/.config/agents/skills/`; it is not personal hosted/account-wide. Personal
305+
hosted artifacts are managed through Amp's Personal Settings or personal Git
306+
repositories and work across machines and orbs. See the complete [Amp guide](docs/amp.md)
307+
for hosted publication, source precedence, wrapper limits, updates, fallback
308+
installation, specialists, safety boundaries, and verification.
309+
`phx-watch-pr` additionally needs the separate `phx-watch-pr.ts` plugin.
310+
Install it into the project where the worker Orb opens and watches PRs:
284311

285312
```bash
286313
amp plugins add \
287314
https://raw.githubusercontent.com/oliver-kriska/claude-elixir-phoenix/main/targets/amp/plugins/phx-watch-pr.ts \
288315
--target workspace
289316
```
290317

291-
The plugin holds a bounded Orb keep-alive lease, filters deployment-like checks
292-
out of readiness, persists reload-safe state, and wakes the same worker thread
293-
only for failed/cancelled required CI, unresolved feedback, and terminal
294-
outcomes. Its defaults are a 60-second poll, 15-minute activity-based quiet
295-
period, and 2-hour active-watch cap. Head pushes, required-check transitions,
296-
reviews, and comments restart the quiet period without routine model turns;
297-
deployment-like transitions do neither. With `--fix`, actionable feedback and
298-
branch-owned CI failures are repaired in one serialized same-thread workflow.
299-
It never blindly reruns shared CI, merges, or deploys.
300-
301-
Amp copies skills at installation time; it does not update them automatically.
302-
Rerun the same command with `--overwrite` to install the latest version from
303-
`main`. Cloning this repository is only necessary for local development.
304-
305-
Namespaced Claude commands use hyphenated Amp names: `/phx:plan` becomes
306-
`phx-plan`, `/ecto:n1-check` becomes `ecto-n1-check`, and so on. Start a fresh
307-
Amp session after installation. To invoke the equivalent of `/phx:investigate`
308-
reliably, open Amp's command palette with `Ctrl+O` (or type `/` in the CLI), run
309-
`skill: invoke`, and select `phx-investigate`. Amp forces the selected skill to
310-
load with your next message.
311-
312-
You can also name skills explicitly in a prompt, which is convenient for copied
313-
prompts and non-interactive use:
314-
315-
```text
316-
Load phx-investigate and investigate this LiveView filter reset.
317-
```
318-
319-
Exact Claude-style entries such as `/phx:review` are not registered as Amp slash
320-
commands; Amp uses its command palette and native skill invocation instead. Amp
321-
may also select skills automatically from their descriptions, but automatic
322-
selection is model-driven and is not guaranteed on every prompt. The Amp
323-
edition ships skills, their bundled resources, and the focused `phx-watch-pr`
324-
lifecycle plugin—not the Claude-specific hooks, custom agents, permission
325-
settings, or MCP setup. Read the complete
326-
[Amp installation and usage guide](docs/amp.md) for verification, updates,
327-
skill precedence, examples, troubleshooting, and the portability matrix.
318+
It holds a bounded Orb keep-alive lease, filters deployment-like checks out of
319+
readiness, persists reload-safe state, and wakes the same worker thread only for
320+
failed/cancelled required CI, unresolved feedback, and terminal outcomes. With
321+
`--fix`, actionable feedback and branch-owned CI failures are repaired in one
322+
serialized same-thread workflow. It never blindly reruns shared CI, merges, or
323+
deploys.
328324

329325
### Use with Codex
330326

@@ -393,10 +389,12 @@ updates, uninstall, feature-branch review, discovery debugging, and limitations.
393389

394390
The remainder of this README describes the full Claude Code plugin and uses
395391
Claude Code `/phx:*`, `/ecto:*`, and `/lv:*` syntax. For generated runtimes,
396-
translate invocations using the runtime guide: Amp uses `skill: invoke`, Codex
397-
uses `$elixir-phoenix:<skill>`, Pi uses `/skill:<name>`, and OpenCode uses its
398-
skill tool. Generated editions do not install Claude Code's complete custom
399-
agent, lifecycle-hook, permission, or MCP configuration.
392+
translate invocations using the runtime guide: Amp natively uses
393+
`skill: invoke` and paired-full also provides generated entries such as
394+
`phx: investigate`; Codex uses
395+
`$elixir-phoenix:<skill>`, Pi uses `/skill:<name>`, and OpenCode uses its skill
396+
tool. Generated editions do not install Claude Code's complete custom agent,
397+
lifecycle-hook, permission, or MCP configuration.
400398

401399
New to the plugin? Run the interactive tutorial:
402400

0 commit comments

Comments
 (0)