@really-knows-ai/foundry 2.0.1 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/plugins/foundry.js +343 -51
- package/CHANGELOG.md +55 -0
- package/package.json +4 -3
- package/scripts/lib/artefacts.js +6 -0
- package/scripts/lib/feedback-transitions.js +25 -0
- package/scripts/lib/feedback.js +146 -9
- package/scripts/lib/finalize.js +41 -0
- package/scripts/lib/history.js +15 -3
- package/scripts/lib/pending.js +18 -0
- package/scripts/lib/secret.js +23 -0
- package/scripts/lib/slug.js +33 -0
- package/scripts/lib/stage-guard.js +25 -0
- package/scripts/lib/state.js +31 -0
- package/scripts/lib/token.js +26 -0
- package/scripts/lib/workfile.js +12 -1
- package/scripts/sort.js +99 -15
- package/skills/add-cycle/SKILL.md +11 -6
- package/skills/appraise/SKILL.md +33 -17
- package/skills/cycle/SKILL.md +25 -19
- package/skills/flow/SKILL.md +9 -2
- package/skills/forge/SKILL.md +38 -26
- package/skills/human-appraise/SKILL.md +29 -17
- package/skills/quench/SKILL.md +31 -15
- package/skills/refresh-agents/SKILL.md +6 -3
- package/skills/sort/SKILL.md +86 -16
- package/skills/upgrade-foundry/SKILL.md +52 -6
|
@@ -16,11 +16,14 @@ Regenerate `.opencode/agents/foundry-*.md` files from the currently available mo
|
|
|
16
16
|
|
|
17
17
|
### Agent file format
|
|
18
18
|
|
|
19
|
-
Filename: `.opencode/agents/foundry-<
|
|
19
|
+
Filename: `.opencode/agents/foundry-<slug>.md`
|
|
20
20
|
|
|
21
|
-
Where `<
|
|
21
|
+
Where `<slug>` is the model ID with **both** `/` and `.` replaced by `-`. This keeps filenames shell-safe and unambiguous.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Examples:
|
|
24
|
+
- `opencode/claude-sonnet-4` → `.opencode/agents/foundry-opencode-claude-sonnet-4.md`
|
|
25
|
+
- `github-copilot/claude-sonnet-4.6` → `.opencode/agents/foundry-github-copilot-claude-sonnet-4-6.md`
|
|
26
|
+
- `github-copilot/gpt-5.4` → `.opencode/agents/foundry-github-copilot-gpt-5-4.md`
|
|
24
27
|
|
|
25
28
|
Content:
|
|
26
29
|
|
package/skills/sort/SKILL.md
CHANGED
|
@@ -6,7 +6,7 @@ description: Deterministic routing for a foundry cycle. Runs the foundry_sort to
|
|
|
6
6
|
|
|
7
7
|
# Sort
|
|
8
8
|
|
|
9
|
-
You are the central dispatcher for a foundry cycle. You call
|
|
9
|
+
You are the central dispatcher for a foundry cycle. You call `foundry_sort` to determine what stage to execute next, dispatch that stage to a fresh subagent, finalize the stage's disk output, and log history. You are the sole writer of history and git commits.
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
@@ -16,26 +16,96 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
16
16
|
|
|
17
17
|
## Protocol
|
|
18
18
|
|
|
19
|
-
1. Call `foundry_sort` (optionally passing `cycleDef`
|
|
19
|
+
1. Call `foundry_sort` (optionally passing `cycleDef`). It returns `{route, model?, token?, details?}`. For dispatchable routes (`forge|quench|appraise|human-appraise:*`) the tool mints a single-use, time-limited `token`.
|
|
20
20
|
|
|
21
|
-
2. Call `foundry_history_append
|
|
21
|
+
2. Call `foundry_history_append({cycle, stage: 'sort', comment, route})` — the `route` field records what sort decided, and **subsequent** `history_append` calls for non-sort stages are enforced to match this route. This is your audit trail.
|
|
22
22
|
|
|
23
23
|
3. Act on the route:
|
|
24
|
-
- `forge:*`
|
|
25
|
-
- `
|
|
26
|
-
- `
|
|
27
|
-
- `
|
|
28
|
-
- `
|
|
29
|
-
- `blocked` — foundry cycle is blocked (iteration limit hit with unresolved feedback), return to the cycle skill
|
|
30
|
-
- `violation` — file modification or tag validation violation detected (see `details`). The cycle halts — call `foundry_artefacts_set_status` with status `"blocked"`, and return to the cycle skill
|
|
24
|
+
- `forge:*` / `quench:*` / `appraise:*` — **dispatch** (see §Dispatch).
|
|
25
|
+
- `human-appraise:*` — invoke the human-appraise skill inline (human stage, no subagent) but still pass the `token`; the skill must call `foundry_stage_begin` with it.
|
|
26
|
+
- `done` — cycle is complete, return to the cycle skill.
|
|
27
|
+
- `blocked` — iteration limit hit with unresolved feedback, return to the cycle skill.
|
|
28
|
+
- `violation` — a validation, file-modification, or missing-subagent violation was detected (see `details`). Halt the cycle: call `foundry_artefacts_set_status(file, 'blocked')` for each affected artefact, and return to the cycle skill. If `details` mentions a missing subagent, tell the user to run `refresh-agents` and restart.
|
|
31
29
|
|
|
32
|
-
4. After the subagent
|
|
30
|
+
4. **After** the dispatched subagent returns, call `foundry_stage_finalize({cycle})`. Handle three outcomes:
|
|
31
|
+
- `{ok: true, artefacts: [...]}` — the tool has already registered output artefact rows in WORK.md. Proceed to step 5.
|
|
32
|
+
- `{error: 'unexpected_files', files: [...]}` — the subagent wrote outside the artefact type's `file-patterns`. Mark the cycle's target artefact `blocked` via `foundry_artefacts_set_status` and do **not** re-run the stage. Add a `violation` feedback item describing the offending files, then return to the cycle skill.
|
|
33
|
+
- Any other error — surface it to the user and halt.
|
|
33
34
|
|
|
34
|
-
5.
|
|
35
|
+
5. Call `foundry_history_append({cycle, stage: <dispatched-stage-alias>, comment})` summarizing what the subagent reported. The tool enforces that the stage alias matches the most recent sort's `route` — this is why step 2's `route` field matters.
|
|
36
|
+
|
|
37
|
+
6. Call `foundry_git_commit({cycle, stage, description})` to record the stage's disk changes. **This is mandatory.** The next `foundry_sort` call will return `{route: 'violation', details: 'Uncommitted tool-managed files...'}` if WORK.md, WORK.history.yaml, or anything under `.foundry/` is dirty — the tool enforces one commit per stage.
|
|
38
|
+
|
|
39
|
+
7. Return to step 1. Repeat until `done`, `blocked`, or `violation`.
|
|
40
|
+
|
|
41
|
+
## Dispatch
|
|
42
|
+
|
|
43
|
+
Every forge, quench, and appraise stage runs in a **fresh subagent**. Never inline the stage work in the orchestrator conversation — even if the chosen model matches the orchestrator's. The orchestrator's job is to route, dispatch, finalize, and log. Nothing else.
|
|
44
|
+
|
|
45
|
+
### Choosing the subagent
|
|
46
|
+
|
|
47
|
+
- If `foundry_sort` returned a `model` field, use it verbatim as `subagent_type`. It is already in `foundry-<slug>` form.
|
|
48
|
+
- If no `model` field, dispatch to `general`.
|
|
49
|
+
|
|
50
|
+
### Token handling
|
|
51
|
+
|
|
52
|
+
The `token` returned by `foundry_sort` is an opaque signed string. Pass it through the dispatch prompt verbatim. **Never** invent, edit, or re-sign tokens. The subagent's first tool call must be `foundry_stage_begin({stage, cycle, token})` using this exact string; `stage_begin` verifies the signature, expiry, and single-use nonce.
|
|
53
|
+
|
|
54
|
+
### Dispatch call shape
|
|
55
|
+
|
|
56
|
+
Use the `task` tool:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
task tool:
|
|
60
|
+
subagent_type: <model-slug-from-foundry_sort, or "general">
|
|
61
|
+
description: "Run <stage-alias> for <cycle-id>"
|
|
62
|
+
prompt: |
|
|
63
|
+
You are a Foundry stage agent. Invoke the <stage-base> skill and follow its instructions exactly.
|
|
64
|
+
|
|
65
|
+
Stage: <stage-alias>
|
|
66
|
+
Cycle: <cycle-id>
|
|
67
|
+
Token: <token-verbatim>
|
|
68
|
+
Working directory: <worktree>
|
|
69
|
+
File patterns (forge only): <file-patterns-list>
|
|
70
|
+
|
|
71
|
+
Your FIRST tool call MUST be foundry_stage_begin({stage, cycle, token}) using the values above.
|
|
72
|
+
Your LAST tool call MUST be foundry_stage_end({summary}).
|
|
73
|
+
|
|
74
|
+
When done, report back a brief summary. Do NOT call foundry_history_append, foundry_git_commit, or foundry_artefacts_add — the orchestrator handles all of those.
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Substitute:
|
|
78
|
+
- `<stage-alias>` — the full route string from `foundry_sort` (e.g., `forge:write-haiku`)
|
|
79
|
+
- `<stage-base>` — the base of the alias
|
|
80
|
+
- `<cycle-id>` — current cycle ID from WORK.md frontmatter
|
|
81
|
+
- `<token-verbatim>` — exactly the `token` string from `foundry_sort` — no quoting transforms, no re-encoding
|
|
82
|
+
- `<file-patterns-list>` — for forge stages, read via `foundry_config_artefact_type` and include so the subagent can avoid violations
|
|
83
|
+
- `<worktree>` — current working directory
|
|
84
|
+
|
|
85
|
+
### Missing subagent (fail-fast)
|
|
86
|
+
|
|
87
|
+
`foundry_sort` verifies that `.opencode/agents/foundry-<slug>.md` exists before returning a `model`. If it doesn't, sort returns `{route: 'violation', details: 'Missing required subagent: ...'}`. Handle as in step 3 above.
|
|
88
|
+
|
|
89
|
+
## Violation handling
|
|
90
|
+
|
|
91
|
+
If `foundry_stage_finalize` returns `{error: 'unexpected_files', files}`:
|
|
92
|
+
|
|
93
|
+
- The stage wrote outside its permitted `file-patterns`. This is unrecoverable within the current cycle.
|
|
94
|
+
- Mark the target artefact `blocked`: `foundry_artefacts_set_status(file, 'blocked')`.
|
|
95
|
+
- Add a feedback item describing the offense: `foundry_feedback_add(file, text: 'unexpected files: …', tag: 'violation')` (if permitted by your stage), or log in the history comment.
|
|
96
|
+
- Do NOT attempt to re-run the stage — the subagent already consumed the stage slot.
|
|
97
|
+
- Return to the cycle skill so the operator can intervene.
|
|
98
|
+
|
|
99
|
+
If `foundry_sort` returns `{route: 'violation', details: 'Uncommitted tool-managed files...'}`:
|
|
100
|
+
|
|
101
|
+
- A prior stage skipped step 6 (the micro-commit). The work is not lost — it's still in the working tree.
|
|
102
|
+
- Call `foundry_git_commit({cycle, stage: <the-stage-that-just-ran>, description})` to record it.
|
|
103
|
+
- Then call `foundry_sort` again. It should route normally.
|
|
104
|
+
- If you genuinely don't know which stage produced the dirty files, read `WORK.history.yaml` — the most recent non-sort entry is the culprit.
|
|
35
105
|
|
|
36
106
|
## What you do NOT do
|
|
37
107
|
|
|
38
|
-
- You do not
|
|
39
|
-
- You do not
|
|
40
|
-
- You do not
|
|
41
|
-
- You do not
|
|
108
|
+
- You do not inline forge/quench/appraise work — always dispatch.
|
|
109
|
+
- You do not mint, modify, or cache tokens — they come from `foundry_sort` and go straight to `foundry_stage_begin`.
|
|
110
|
+
- You do not skip `foundry_stage_finalize` — it is the only mechanism that registers artefacts and detects file-pattern violations.
|
|
111
|
+
- You do not let subagents call `foundry_history_append`, `foundry_git_commit`, or `foundry_artefacts_add` (the last has been removed anyway).
|
|
@@ -27,12 +27,17 @@ Read all configuration files:
|
|
|
27
27
|
- `foundry/laws/*.md` — global laws
|
|
28
28
|
- `foundry/appraisers/*.md` — appraiser definitions
|
|
29
29
|
|
|
30
|
+
Also scan `.opencode/agents/foundry-*.md` for agent-filename migration (see §2).
|
|
31
|
+
|
|
30
32
|
For each file, parse the frontmatter and body content.
|
|
31
33
|
|
|
32
34
|
### 2. Detect what needs migration
|
|
33
35
|
|
|
34
36
|
Check each file against the current expected format:
|
|
35
37
|
|
|
38
|
+
**Agent files (v2.1 migration):**
|
|
39
|
+
- Any `.opencode/agents/foundry-*.md` filename containing a `.` character? → needs renaming to all-dashes format. The v2.1 naming convention replaces both `/` and `.` in the model ID with `-`. For example, `foundry-github-copilot-claude-sonnet-4.6.md` must become `foundry-github-copilot-claude-sonnet-4-6.md`. The inner `model:` frontmatter field is **not** changed — only the filename.
|
|
40
|
+
|
|
36
41
|
**Flows:**
|
|
37
42
|
- Has `starting-cycles` field? If not → needs DAG migration
|
|
38
43
|
- Has ordered numbered list under `## Cycles`? → needs conversion to unordered list
|
|
@@ -41,7 +46,7 @@ Check each file against the current expected format:
|
|
|
41
46
|
- Has `targets` field? If not → needs target routing
|
|
42
47
|
- Has `inputs.type` (`any-of`/`all-of`)? If `inputs` is a plain list → needs contract type
|
|
43
48
|
- Has `hitl` in stages or frontmatter? → needs human-appraise migration
|
|
44
|
-
- Has `human-appraise
|
|
49
|
+
- Has nested `human-appraise: {enabled, deadlock-threshold}`? → v2.2.1 flat-keys migration (see §4b)
|
|
45
50
|
- Has `models` map? Check format
|
|
46
51
|
|
|
47
52
|
**Artefact types:**
|
|
@@ -84,7 +89,48 @@ Present a grouped summary of all issues found:
|
|
|
84
89
|
|
|
85
90
|
If nothing needs migration, say so and stop.
|
|
86
91
|
|
|
87
|
-
### 4. Migrate
|
|
92
|
+
### 4. Migrate agent files (v2.1)
|
|
93
|
+
|
|
94
|
+
For each `.opencode/agents/foundry-*.md` file with a `.` in its filename:
|
|
95
|
+
- Compute the new filename by replacing all `.` with `-` (keep the `.md` extension)
|
|
96
|
+
- `git mv <old> <new>` to preserve history
|
|
97
|
+
- Do **not** modify the file contents — the `model:` field inside retains its original dots
|
|
98
|
+
|
|
99
|
+
After renaming, remind the user: **Restart OpenCode** for the new agent filenames to register.
|
|
100
|
+
|
|
101
|
+
### 4a. v2.2.0 lifecycle upgrade
|
|
102
|
+
|
|
103
|
+
Foundry v2.2.0 introduces a tool-enforced stage lifecycle (`stage_begin` / `stage_end` / `stage_finalize`) backed by a per-project state directory and HMAC-signed dispatch tokens. The upgrade is non-destructive — no WORK.md or artefact migration is required — but the project needs three small changes:
|
|
104
|
+
|
|
105
|
+
1. **Create `.foundry/`** (if absent):
|
|
106
|
+
- `mkdir -p .foundry`
|
|
107
|
+
- The plugin auto-creates `.foundry/.secret` on first boot via `readOrCreateSecret`. You do not need to generate it by hand; just ensure the directory exists and is writable.
|
|
108
|
+
2. **Gitignore `.foundry/`**:
|
|
109
|
+
- Ensure `.gitignore` contains a line `.foundry/` (append if missing; do not duplicate). The directory holds a per-worktree HMAC secret and transient active-stage state — neither should be committed.
|
|
110
|
+
3. **Pre-existing state:** v2.2.0 is a fresh state system. There is no `active-stage.json` to migrate. If one happens to exist from a manually-aborted prior run, leave it alone — the new plugin treats its absence as "no active stage" and its presence as a legitimate in-flight stage.
|
|
111
|
+
|
|
112
|
+
The `foundry_artefacts_add` tool has been removed in v2.2.0 — artefact registration now happens automatically via `foundry_stage_finalize`. No existing config references this tool, so there is nothing to migrate in `foundry/`.
|
|
113
|
+
|
|
114
|
+
### 4b. v2.2.1 cycle-definition flat human-appraise keys
|
|
115
|
+
|
|
116
|
+
v2.2.1 replaces the nested `human-appraise: {enabled, deadlock-threshold}` block in cycle definitions with three flat keys:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
human-appraise: <true|false> # default: false — run human-appraise every iteration
|
|
120
|
+
deadlock-appraise: <true|false> # default: true — pull in human-appraise when LLM appraisers deadlock
|
|
121
|
+
deadlock-iterations: <number> # default: 5 — deadlock detection threshold
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
For each `foundry/cycles/*.md` whose frontmatter has the old nested form, migrate:
|
|
125
|
+
|
|
126
|
+
- `human-appraise.enabled: true` → `human-appraise: true`
|
|
127
|
+
- `human-appraise.enabled: false` (or missing) → `human-appraise: false`
|
|
128
|
+
- `human-appraise.deadlock-threshold: N` → `deadlock-iterations: N`
|
|
129
|
+
- Always add `deadlock-appraise: true` unless the user explicitly wants the stricter "no human ever" behavior (`deadlock-appraise: false` → deadlock marks the cycle `blocked`).
|
|
130
|
+
|
|
131
|
+
The old nested form is no longer read. After migration, verify by asking: "cycle `<id>`: human-appraise every iteration? deadlock-appraise on? deadlock-iterations = N?".
|
|
132
|
+
|
|
133
|
+
### 5. Migrate flows
|
|
88
134
|
|
|
89
135
|
For each flow needing migration:
|
|
90
136
|
- Show the current ordered cycle list
|
|
@@ -93,7 +139,7 @@ For each flow needing migration:
|
|
|
93
139
|
- Present the proposed `starting-cycles` and confirm
|
|
94
140
|
- Convert numbered `## Cycles` list to unordered
|
|
95
141
|
|
|
96
|
-
###
|
|
142
|
+
### 6. Migrate cycles
|
|
97
143
|
|
|
98
144
|
For each cycle needing migration:
|
|
99
145
|
|
|
@@ -112,20 +158,20 @@ For each cycle needing migration:
|
|
|
112
158
|
|
|
113
159
|
Remove `hitl` from stages and add `human-appraise` config if enabled.
|
|
114
160
|
|
|
115
|
-
###
|
|
161
|
+
### 7. Migrate other config
|
|
116
162
|
|
|
117
163
|
For artefact types, appraisers, laws, and validation with issues:
|
|
118
164
|
- Present each issue with a suggested fix
|
|
119
165
|
- Ask the user to confirm or adjust
|
|
120
166
|
|
|
121
|
-
###
|
|
167
|
+
### 8. Present migration plan
|
|
122
168
|
|
|
123
169
|
Before writing anything, show the complete list of changes:
|
|
124
170
|
- Group by category
|
|
125
171
|
- Show each file and the specific changes
|
|
126
172
|
- Ask for confirmation
|
|
127
173
|
|
|
128
|
-
###
|
|
174
|
+
### 9. Apply changes
|
|
129
175
|
|
|
130
176
|
- Update all affected files
|
|
131
177
|
- Commit with message: `[foundry] upgrade: migrate to current format`
|