@really-knows-ai/foundry 2.1.0 → 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 +329 -46
- package/CHANGELOG.md +55 -0
- package/package.json +3 -2
- 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/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 +89 -14
- 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/sort/SKILL.md +60 -28
- package/skills/upgrade-foundry/SKILL.md +33 -1
package/skills/appraise/SKILL.md
CHANGED
|
@@ -14,34 +14,48 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
14
14
|
|
|
15
15
|
> Foundry is not initialized in this project. Run the `init-foundry` skill first to create the foundry/ directory structure.
|
|
16
16
|
|
|
17
|
+
## Stage lifecycle (mandatory)
|
|
18
|
+
|
|
19
|
+
Appraise runs inside an enforced stage. Your **first** and **last** tool calls are fixed:
|
|
20
|
+
|
|
21
|
+
1. **First:** `foundry_stage_begin({stage, cycle, token})` — copy the token verbatim from the dispatch prompt.
|
|
22
|
+
2. **Last:** `foundry_stage_end({summary})`.
|
|
23
|
+
|
|
24
|
+
Appraise makes **no disk writes**. All output flows through `foundry_feedback_add`. `foundry_stage_finalize` flags any unexpected writes as a violation.
|
|
25
|
+
|
|
17
26
|
## Protocol
|
|
18
27
|
|
|
19
|
-
1.
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
28
|
+
1. `foundry_stage_begin(...)`.
|
|
29
|
+
2. Gather context:
|
|
30
|
+
- `foundry_workfile_get` — read the `cycle` from frontmatter
|
|
31
|
+
- `foundry_artefacts_list({cycle: <current-cycle>})` — enumerate this cycle's artefacts. Always pass the `cycle` filter; omitting it returns stale rows from prior sessions. Skip rows whose status is `done` or `blocked`.
|
|
32
|
+
- For each remaining row, gather its type-specific context:
|
|
33
|
+
- `foundry_config_laws` with the row's type — applicable laws (global + type-specific)
|
|
34
|
+
- `foundry_config_artefact_type` with the type ID — the artefact type definition
|
|
35
|
+
- `foundry_appraisers_select` with the type ID — selected appraiser personalities with their raw model IDs
|
|
24
36
|
|
|
25
|
-
|
|
37
|
+
3. Dispatch each appraiser as an independent sub-agent (see Dispatch below). If this cycle produced multiple artefacts, appraisers evaluate each.
|
|
26
38
|
|
|
27
|
-
|
|
39
|
+
4. Collect results from all appraisers
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
5. Consolidate (this is judgment):
|
|
30
42
|
- Union of all issues — if any one appraiser flags it, it's feedback
|
|
31
43
|
- De-duplicate: merge overlapping observations into a single feedback item
|
|
32
44
|
- Preserve which appraiser(s) raised each issue (for traceability)
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
6. For each consolidated issue: `foundry_feedback_add(file, text, tag: 'law:<law-id>')`. Tag MUST start with `law:` — the tool rejects other tags during appraise. The tool also de-duplicates by text-hash.
|
|
47
|
+
|
|
48
|
+
7. If no appraiser found any issues, the artefact clears appraisal.
|
|
35
49
|
|
|
36
|
-
|
|
50
|
+
8. `foundry_stage_end({summary})`.
|
|
37
51
|
|
|
38
52
|
## Reviewing actioned and wont-fix feedback
|
|
39
53
|
|
|
40
54
|
On subsequent passes, review previously actioned and wont-fix items:
|
|
41
55
|
|
|
42
|
-
1.
|
|
43
|
-
2.
|
|
44
|
-
3.
|
|
56
|
+
1. `foundry_feedback_list` — find `actioned` and `wont-fix` items for this artefact.
|
|
57
|
+
2. Appraiser sub-agents evaluate whether the change addresses the issue (`actioned`) or the justification is sound (`wont-fix`).
|
|
58
|
+
3. `foundry_feedback_resolve(file, index, resolution: 'approved'|'rejected', reason?)`. Appraise is the only stage (other than human-appraise) allowed to resolve `wont-fix` items.
|
|
45
59
|
|
|
46
60
|
## Dispatch
|
|
47
61
|
|
|
@@ -91,7 +105,7 @@ If there are no issues, return an empty list.
|
|
|
91
105
|
|
|
92
106
|
## History
|
|
93
107
|
|
|
94
|
-
Do NOT call `foundry_history_append` — the sort skill
|
|
108
|
+
Do NOT call `foundry_history_append` or `foundry_git_commit` — the sort skill handles those. Return a summary via `foundry_stage_end` (e.g., "3 issues found across 2 appraisers" or "No issues found").
|
|
95
109
|
|
|
96
110
|
### Human override awareness
|
|
97
111
|
|
|
@@ -99,6 +113,8 @@ When reviewing an artefact, check the feedback history for `#human` tagged items
|
|
|
99
113
|
|
|
100
114
|
## What you do NOT do
|
|
101
115
|
|
|
102
|
-
- You do not
|
|
103
|
-
- You do not
|
|
104
|
-
- You do not
|
|
116
|
+
- You do not write files — all output goes through `foundry_feedback_add`.
|
|
117
|
+
- You do not revise the artefact.
|
|
118
|
+
- You do not check deterministic rules — that is the quench skill's job.
|
|
119
|
+
- You do not filter out feedback because only one appraiser raised it — one is enough.
|
|
120
|
+
- You do not register artefacts — that happens automatically via `foundry_stage_finalize`.
|
package/skills/cycle/SKILL.md
CHANGED
|
@@ -22,38 +22,42 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
22
22
|
3. Determine the stage route:
|
|
23
23
|
- Use the cycle definition's `stages` field if present
|
|
24
24
|
- Otherwise generate defaults: always `forge`, add `quench` if `foundry_config_validation` returns non-null for the type, always `appraise`
|
|
25
|
-
- If the cycle definition has `human-appraise
|
|
25
|
+
- If the cycle definition has `human-appraise: true`, append `human-appraise` as the final stage (runs every iteration). If `human-appraise: false` (default), do NOT include it in `stages` — sort will synthesize `human-appraise:<cycle>` on deadlock when needed.
|
|
26
26
|
- Stages should use `base:alias` format (e.g. `forge:write-haiku`, `quench:check-syllables`). If you pass bare names, the tool will auto-append the cycle ID as the alias.
|
|
27
|
-
4. Call `foundry_workfile_set`
|
|
28
|
-
- `key: "cycle"`, `value: <cycle-id>`
|
|
29
|
-
- `key: "stages"`, `value: <determined stages list>`
|
|
30
|
-
- `key: "max-iterations"`, `value: <default 3 or from cycle definition>`
|
|
31
|
-
- If the cycle definition has a `models` map: `key: "models"`, `value: <models map>`
|
|
27
|
+
4. Call `foundry_workfile_configure_from_cycle({cycleId, stages})` with the cycle ID and the stages list from step 3. The tool reads the cycle definition and writes `cycle`, `stages`, `max-iterations`, `human-appraise`, `deadlock-appraise`, `deadlock-iterations`, and (if present) `models` into WORK.md in a single call, applying defaults for anything the cycle def omits. Do **not** use `foundry_workfile_set` for this — the configure tool is the authoritative cycle-def → WORK.md translator.
|
|
32
28
|
5. Invoke the sort skill
|
|
33
29
|
|
|
34
30
|
## Sort drives everything
|
|
35
31
|
|
|
36
|
-
Once sort is invoked, it calls `foundry_sort` to determine the next stage,
|
|
32
|
+
Once sort is invoked, it calls `foundry_sort` to determine the next stage, dispatches the corresponding skill to a fresh subagent with a single-use token, calls `foundry_stage_finalize` to register outputs (or detect file-pattern violations), writes history, and commits. This repeats until sort returns `done`, `blocked`, or `violation`.
|
|
37
33
|
|
|
38
|
-
The cycle skill does not contain routing logic — sort owns all of that.
|
|
34
|
+
The cycle skill does not contain routing, finalization, history, or commit logic — sort owns all of that. The cycle skill only sets up the work file and reacts to sort's terminal result.
|
|
39
35
|
|
|
40
36
|
## Completing a foundry cycle
|
|
41
37
|
|
|
42
38
|
When sort returns `done`:
|
|
43
|
-
- Call `foundry_artefacts_set_status`
|
|
44
|
-
- Return control to the flow skill
|
|
39
|
+
- Call `foundry_artefacts_set_status(file, 'done')` for the cycle's output artefact.
|
|
40
|
+
- Return control to the flow skill.
|
|
45
41
|
|
|
46
42
|
When sort returns `blocked`:
|
|
47
|
-
-
|
|
48
|
-
- Return control to the flow skill
|
|
43
|
+
- The target artefact is usually already marked `blocked` by sort (on violations) or by human-appraise (on explicit abort). If not, call `foundry_artefacts_set_status(file, 'blocked')`.
|
|
44
|
+
- Return control to the flow skill — the flow decides how to handle it.
|
|
45
|
+
|
|
46
|
+
When sort returns `violation` (e.g., `stage_finalize` `unexpected_files`, missing subagent, or file-pattern violation):
|
|
47
|
+
- Sort has already marked affected artefacts blocked and returned. Treat as the blocked path.
|
|
48
|
+
- Return control to the flow skill.
|
|
49
49
|
|
|
50
50
|
## Human Appraise
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Human-appraise is controlled by two flat cycle-def keys:
|
|
53
|
+
|
|
54
|
+
- `human-appraise: true` — human-appraise runs every iteration as part of the normal stage flow (appended to `stages`).
|
|
55
|
+
- `deadlock-appraise: true` (default) — if LLM appraisers deadlock on the same feedback for `deadlock-iterations` rounds (default 5), sort routes to human-appraise to resolve it, even when it isn't in `stages`.
|
|
56
|
+
- `deadlock-appraise: false` — no human intervention; deadlock → `blocked`.
|
|
53
57
|
|
|
54
58
|
## Micro commits
|
|
55
59
|
|
|
56
|
-
Every stage
|
|
60
|
+
Every stage ends with a micro commit, written by sort (not cycle, not subagents). The message format is `[<cycle-id>] <base>:<alias>: <brief description>`.
|
|
57
61
|
|
|
58
62
|
Examples:
|
|
59
63
|
- `[haiku-creation] forge:write-haiku: initial draft`
|
|
@@ -74,8 +78,10 @@ Tag types: `validation` (from quench), `law:<law-id>` (from appraise), `human` (
|
|
|
74
78
|
|
|
75
79
|
## What you do NOT do
|
|
76
80
|
|
|
77
|
-
- You do not make routing decisions — sort does that
|
|
78
|
-
- You do not
|
|
79
|
-
- You do not
|
|
80
|
-
- You do not
|
|
81
|
-
- You do not
|
|
81
|
+
- You do not make routing decisions — sort does that.
|
|
82
|
+
- You do not register artefacts — `foundry_stage_finalize` does that (invoked by sort).
|
|
83
|
+
- You do not write history or commits — sort does that.
|
|
84
|
+
- You do not change the laws mid-cycle.
|
|
85
|
+
- You do not decide the artefact is "close enough" — it passes or it doesn't.
|
|
86
|
+
- You do not proceed past a file modification violation — honor sort's `violation`/`blocked` return.
|
|
87
|
+
- You do not modify input artefacts — they are read-only.
|
package/skills/flow/SKILL.md
CHANGED
|
@@ -23,8 +23,15 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
23
23
|
- If only one starting cycle, use it
|
|
24
24
|
- If multiple starting cycles, check whether the user's request makes the choice obvious (e.g., "write a haiku" clearly maps to `create-haiku`)
|
|
25
25
|
- If ambiguous, prompt the user to choose
|
|
26
|
-
4.
|
|
27
|
-
|
|
26
|
+
4. Pre-check for an existing workfile (prevents silent data loss from an aborted prior session):
|
|
27
|
+
a. Call `foundry_workfile_get`.
|
|
28
|
+
b. If it returns `{error: ...}` (no WORK.md), proceed to step 5.
|
|
29
|
+
c. If it returns an existing workfile, present its `flow`, `cycle`, and `goal` to the user alongside the values just requested, then prompt for one of:
|
|
30
|
+
- **Resume** — keep the existing workfile and skip to step 6. **Only offer resume if the existing `flow` AND `cycle` match what the user just asked for.** If either differs, do not offer resume — running the wrong cycle against stale state corrupts the workflow.
|
|
31
|
+
- **Discard** — call `foundry_workfile_delete`, then proceed to step 5.
|
|
32
|
+
- **Abort** — stop the skill without modifying anything.
|
|
33
|
+
5. Call `foundry_workfile_create` with **only** the flow ID, chosen cycle ID, and goal — do **not** pass `stages` or `maxIterations`. The `cycle` skill will read the cycle definition and populate those via `foundry_workfile_set` in the next step.
|
|
34
|
+
6. Execute the cycle by invoking the cycle skill
|
|
28
35
|
|
|
29
36
|
## Between cycles
|
|
30
37
|
|
package/skills/forge/SKILL.md
CHANGED
|
@@ -14,33 +14,42 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
14
14
|
|
|
15
15
|
> Foundry is not initialized in this project. Run the `init-foundry` skill first to create the foundry/ directory structure.
|
|
16
16
|
|
|
17
|
+
## Stage lifecycle (mandatory)
|
|
18
|
+
|
|
19
|
+
Forge runs inside an enforced stage. Your **first** and **last** tool calls are fixed:
|
|
20
|
+
|
|
21
|
+
1. **First:** `foundry_stage_begin({stage, cycle, token})` — the orchestrator hands you `stage`, `cycle`, and an opaque `token` string in the dispatch prompt. Copy the token verbatim; never invent, edit, or re-sign it. No other tool call is permitted before this one. Any writes before `stage_begin` will be blocked by preconditions.
|
|
22
|
+
2. **Last:** `foundry_stage_end({summary})` — return control to the orchestrator. After `stage_end`, the orchestrator calls `foundry_stage_finalize` which scans the disk and registers your output artefact. **You do not register artefacts yourself.**
|
|
23
|
+
|
|
17
24
|
## Protocol
|
|
18
25
|
|
|
19
26
|
### First generation (no artefact registered yet)
|
|
20
27
|
|
|
21
|
-
1.
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
24
|
-
4.
|
|
25
|
-
5.
|
|
26
|
-
6.
|
|
27
|
-
7.
|
|
28
|
-
8.
|
|
28
|
+
1. `foundry_stage_begin(...)` with the token from the dispatch prompt.
|
|
29
|
+
2. `foundry_workfile_get` — understand the goal.
|
|
30
|
+
3. `foundry_config_cycle` — understand what to produce and what inputs are available.
|
|
31
|
+
4. `foundry_config_artefact_type` with the output type ID — get the artefact type definition, especially its `file-patterns`.
|
|
32
|
+
5. `foundry_config_laws` — get all applicable laws (global + type-specific).
|
|
33
|
+
6. If the cycle has inputs, read the input artefacts (read-only context).
|
|
34
|
+
7. Produce the artefact, respecting all applicable laws from the start.
|
|
35
|
+
8. Write the artefact file to a location that matches the artefact type's `file-patterns`.
|
|
36
|
+
9. `foundry_stage_end({summary})`.
|
|
29
37
|
|
|
30
38
|
### Revision (feedback exists)
|
|
31
39
|
|
|
32
|
-
1.
|
|
33
|
-
2.
|
|
34
|
-
3.
|
|
35
|
-
4.
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
6.
|
|
40
|
+
1. `foundry_stage_begin(...)`.
|
|
41
|
+
2. `foundry_feedback_list` — find unresolved feedback for the artefact.
|
|
42
|
+
3. Read the artefact file.
|
|
43
|
+
4. If the cycle has inputs, read the input artefacts (read-only context).
|
|
44
|
+
5. For each unresolved feedback item, either:
|
|
45
|
+
- Address it and call `foundry_feedback_action` (marks item `actioned`), or
|
|
46
|
+
- Call `foundry_feedback_wontfix` with a justification — available only for `law:` / `human` tags (validation feedback must be actioned).
|
|
47
|
+
6. Update the artefact file.
|
|
48
|
+
7. `foundry_stage_end({summary})`.
|
|
40
49
|
|
|
41
|
-
|
|
50
|
+
## File-pattern hygiene
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
Writes during forge must match the output artefact type's `file-patterns`. Writing to any other path causes `foundry_stage_finalize` to return `{error: 'unexpected_files'}` and the orchestrator will mark the cycle's target artefact `blocked`. You will not get a retry. Plus `WORK.md` and `WORK.history.yaml` (managed by tools). Nothing else.
|
|
44
53
|
|
|
45
54
|
## Unresolved feedback
|
|
46
55
|
|
|
@@ -53,14 +62,17 @@ An item is resolved if it is `approved`.
|
|
|
53
62
|
## #human feedback
|
|
54
63
|
|
|
55
64
|
Feedback tagged `human` (from the human-appraise stage) takes absolute priority:
|
|
56
|
-
- You MUST address it — you cannot wont-fix `#human` feedback
|
|
57
|
-
- When `#human` feedback contradicts LLM appraiser feedback on the same topic, follow the human's direction
|
|
58
|
-
- Acknowledge the human's input in your revision
|
|
65
|
+
- You MUST address it — you cannot wont-fix `#human` feedback.
|
|
66
|
+
- When `#human` feedback contradicts LLM appraiser feedback on the same topic, follow the human's direction.
|
|
67
|
+
- Acknowledge the human's input in your revision.
|
|
59
68
|
|
|
60
69
|
## What you do NOT do
|
|
61
70
|
|
|
62
|
-
- You do not
|
|
63
|
-
- You do not
|
|
64
|
-
- You do not
|
|
65
|
-
- You do not
|
|
66
|
-
- You do not
|
|
71
|
+
- You do not add feedback — that is the quench and appraise skills' job. (`foundry_feedback_add` is blocked for you at the tool layer.)
|
|
72
|
+
- You do not `foundry_feedback_resolve` — that belongs to quench/appraise/human-appraise.
|
|
73
|
+
- You do not register artefacts — `foundry_stage_finalize` handles that automatically.
|
|
74
|
+
- You do not call `foundry_history_append` or `foundry_git_commit` — the sort skill does.
|
|
75
|
+
- You do not evaluate or score the artefact.
|
|
76
|
+
- You do not mark feedback as actioned unless you actually changed the artefact to address it.
|
|
77
|
+
- You do not wont-fix validation feedback.
|
|
78
|
+
- You do not modify input artefacts — they are read-only.
|
|
@@ -14,17 +14,27 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
14
14
|
|
|
15
15
|
> Foundry is not initialized in this project. Run the `init-foundry` skill first to create the foundry/ directory structure.
|
|
16
16
|
|
|
17
|
+
## Stage lifecycle (mandatory)
|
|
18
|
+
|
|
19
|
+
Human-appraise runs inside an enforced stage. Your **first** and **last** tool calls are fixed:
|
|
20
|
+
|
|
21
|
+
1. **First:** `foundry_stage_begin({stage, cycle, token})` — copy the token verbatim from the dispatch prompt.
|
|
22
|
+
2. **Last:** `foundry_stage_end({summary})`.
|
|
23
|
+
|
|
24
|
+
Human-appraise makes **no disk writes**. All output flows through `foundry_feedback_add` / `foundry_feedback_resolve` / `foundry_artefacts_set_status`. `foundry_stage_finalize` flags unexpected writes as a violation.
|
|
25
|
+
|
|
17
26
|
## Protocol
|
|
18
27
|
|
|
19
|
-
1.
|
|
20
|
-
|
|
21
|
-
- `
|
|
28
|
+
1. `foundry_stage_begin(...)`.
|
|
29
|
+
2. Gather context by calling:
|
|
30
|
+
- `foundry_workfile_get` — current state, goal, cycle
|
|
31
|
+
- `foundry_artefacts_list({cycle: <current-cycle>})` — this cycle's artefact files and status (always pass the `cycle` filter; omitting it returns stale rows from prior sessions)
|
|
22
32
|
- `foundry_feedback_list` — all existing feedback
|
|
23
33
|
- `foundry_history_list` — what has happened so far
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
3. Read the artefact file(s) for this cycle.
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
4. Present to the human:
|
|
28
38
|
- The current artefact content (full file content or multi-file diff)
|
|
29
39
|
- A summary of this iteration's feedback (resolved and open)
|
|
30
40
|
- If this is a deadlock escalation, clearly explain the deadlock:
|
|
@@ -33,15 +43,15 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
33
43
|
- Forge's wont-fix or revision justification
|
|
34
44
|
- Ask the human to resolve the disagreement
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
5. Wait for the human's response.
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
- **Approve** — "looks good" / "continue" — no feedback added, sort will advance
|
|
40
|
-
- **Provide feedback** —
|
|
41
|
-
- **Dismiss deadlocked feedback** —
|
|
42
|
-
- **Abort** —
|
|
48
|
+
6. Act on the response (tag MUST be `human` on any added feedback — the tool rejects other tags during human-appraise):
|
|
49
|
+
- **Approve** — "looks good" / "continue" — no feedback added, sort will advance.
|
|
50
|
+
- **Provide feedback** — `foundry_feedback_add(file, text, tag: 'human')`. Sort will route back to forge.
|
|
51
|
+
- **Dismiss deadlocked feedback** — `foundry_feedback_resolve(file, index, resolution: 'approved')`. Human-appraise may resolve items in state `actioned` or `wont-fix`. This overrides the appraiser.
|
|
52
|
+
- **Abort** — `foundry_artefacts_set_status(file, 'blocked')`, cycle ends.
|
|
43
53
|
|
|
44
|
-
|
|
54
|
+
7. `foundry_stage_end({summary})` — describe what the human decided so sort can log it.
|
|
45
55
|
|
|
46
56
|
## #human feedback rules
|
|
47
57
|
|
|
@@ -51,8 +61,10 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
51
61
|
|
|
52
62
|
## What you do NOT do
|
|
53
63
|
|
|
54
|
-
- You do not
|
|
55
|
-
- You do not
|
|
56
|
-
- You do not
|
|
57
|
-
- You do not
|
|
58
|
-
- You do not
|
|
64
|
+
- You do not write files — all output goes through foundry tools.
|
|
65
|
+
- You do not make decisions for the human — present the state and wait.
|
|
66
|
+
- You do not modify the artefact.
|
|
67
|
+
- You do not skip the pause — the human must respond before continuing.
|
|
68
|
+
- You do not filter or summarise away important details — show the full picture.
|
|
69
|
+
- You do not call `foundry_history_append` or `foundry_git_commit` — sort owns those.
|
|
70
|
+
- You do not register artefacts — handled by `foundry_stage_finalize`.
|
package/skills/quench/SKILL.md
CHANGED
|
@@ -14,33 +14,49 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
14
14
|
|
|
15
15
|
> Foundry is not initialized in this project. Run the `init-foundry` skill first to create the foundry/ directory structure.
|
|
16
16
|
|
|
17
|
+
## Stage lifecycle (mandatory)
|
|
18
|
+
|
|
19
|
+
Quench runs inside an enforced stage. Your **first** and **last** tool calls are fixed:
|
|
20
|
+
|
|
21
|
+
1. **First:** `foundry_stage_begin({stage, cycle, token})` — copy the token verbatim from the dispatch prompt. Any other tool call before this will be blocked.
|
|
22
|
+
2. **Last:** `foundry_stage_end({summary})`.
|
|
23
|
+
|
|
24
|
+
Quench makes **no disk writes**. You produce feedback via `foundry_feedback_add`, never by creating or modifying files. `foundry_stage_finalize` (run by the orchestrator after you return) will flag any unexpected writes as a violation.
|
|
25
|
+
|
|
17
26
|
## Protocol
|
|
18
27
|
|
|
19
|
-
1.
|
|
20
|
-
2.
|
|
21
|
-
3.
|
|
22
|
-
4. For each
|
|
23
|
-
|
|
28
|
+
1. `foundry_stage_begin(...)`.
|
|
29
|
+
2. `foundry_workfile_get` — read the `cycle` from frontmatter.
|
|
30
|
+
3. `foundry_artefacts_list({cycle: <current-cycle>})` — enumerate the artefacts produced by **this** cycle. Always pass the `cycle` filter; omitting it returns rows from prior sessions and validates stale files. Skip rows whose status is `done` or `blocked`.
|
|
31
|
+
4. For each remaining row:
|
|
32
|
+
a. `foundry_config_validation` with the row's type. If it returns null, skip this row.
|
|
33
|
+
b. `foundry_validate_run` with the type ID and the row's file path — executes all validation commands and returns results.
|
|
34
|
+
c. For each failure: `foundry_feedback_add(file, text, tag: 'validation')`. Tag MUST be `validation` — the tool rejects other tags during quench.
|
|
35
|
+
5. If every command passes for every row, add no new feedback.
|
|
36
|
+
6. If the artefact table has no rows for this cycle, `foundry_stage_end({summary: 'SKIP: no artefacts registered for this cycle'})` and stop.
|
|
37
|
+
7. `foundry_stage_end({summary})`.
|
|
24
38
|
|
|
25
39
|
## Reviewing actioned feedback
|
|
26
40
|
|
|
27
41
|
On subsequent passes, review previously actioned items:
|
|
28
42
|
|
|
29
|
-
1.
|
|
43
|
+
1. `foundry_feedback_list` — find `actioned` items tagged `validation` for artefacts in this cycle (use the file list from step 3 above).
|
|
30
44
|
2. Re-run the relevant command via `foundry_validate_run`.
|
|
31
|
-
3. If the check now passes:
|
|
32
|
-
4. If it still fails:
|
|
45
|
+
3. If the check now passes: `foundry_feedback_resolve(file, index, resolution: 'approved')`.
|
|
46
|
+
4. If it still fails: `foundry_feedback_resolve(file, index, resolution: 'rejected', reason)`.
|
|
33
47
|
|
|
34
|
-
There is no wont-fix for validation feedback
|
|
48
|
+
There is no wont-fix for validation feedback — deterministic rules are not negotiable. Quench may only resolve items in state `actioned`; the feedback tool enforces this.
|
|
35
49
|
|
|
36
50
|
## History
|
|
37
51
|
|
|
38
|
-
Do NOT call `foundry_history_append` — the sort skill
|
|
52
|
+
Do NOT call `foundry_history_append` or `foundry_git_commit` — the sort skill handles those. Return a clear summary via `foundry_stage_end` (e.g., "2 validation issues found" or "Validation passed").
|
|
39
53
|
|
|
40
54
|
## What you do NOT do
|
|
41
55
|
|
|
42
|
-
- You do not
|
|
43
|
-
- You do not
|
|
44
|
-
- You do not
|
|
45
|
-
- You do not
|
|
46
|
-
- You do not
|
|
56
|
+
- You do not write files — all output goes through `foundry_feedback_add`.
|
|
57
|
+
- You do not make subjective judgments.
|
|
58
|
+
- You do not revise the artefact (forge's job).
|
|
59
|
+
- You do not evaluate laws — that is the appraise skill's job.
|
|
60
|
+
- You do not invent validation rules — you only run commands from the validation config.
|
|
61
|
+
- You do not duplicate feedback that already exists (the tool de-duplicates by text-hash, but don't rely on it).
|
|
62
|
+
- You do not register artefacts — that happens automatically.
|
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,31 +16,40 @@ 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:*` — **dispatch** (see §Dispatch
|
|
25
|
-
- `
|
|
26
|
-
- `
|
|
27
|
-
- `
|
|
28
|
-
- `
|
|
29
|
-
- `blocked` — foundry cycle is blocked (iteration limit hit with unresolved feedback), return to the cycle skill
|
|
30
|
-
- `violation` — a validation, file-modification, or missing-subagent violation was detected (see `details`). The cycle halts — call `foundry_artefacts_set_status` with status `"blocked"` for each affected artefact, and return to the cycle skill. If `details` mentions a missing subagent, tell the user to run the `refresh-agents` skill and restart.
|
|
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`.
|
|
35
40
|
|
|
36
41
|
## Dispatch
|
|
37
42
|
|
|
38
|
-
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
|
|
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.
|
|
39
44
|
|
|
40
45
|
### Choosing the subagent
|
|
41
46
|
|
|
42
|
-
- If `foundry_sort` returned a `model` field
|
|
43
|
-
- If
|
|
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.
|
|
44
53
|
|
|
45
54
|
### Dispatch call shape
|
|
46
55
|
|
|
@@ -48,32 +57,55 @@ Use the `task` tool:
|
|
|
48
57
|
|
|
49
58
|
```
|
|
50
59
|
task tool:
|
|
51
|
-
subagent_type: <model-slug-from-foundry_sort
|
|
60
|
+
subagent_type: <model-slug-from-foundry_sort, or "general">
|
|
52
61
|
description: "Run <stage-alias> for <cycle-id>"
|
|
53
62
|
prompt: |
|
|
54
63
|
You are a Foundry stage agent. Invoke the <stage-base> skill and follow its instructions exactly.
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
Stage: <stage-alias>
|
|
66
|
+
Cycle: <cycle-id>
|
|
67
|
+
Token: <token-verbatim>
|
|
58
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}).
|
|
59
73
|
|
|
60
|
-
When done, report back a brief summary
|
|
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.
|
|
61
75
|
```
|
|
62
76
|
|
|
63
77
|
Substitute:
|
|
64
78
|
- `<stage-alias>` — the full route string from `foundry_sort` (e.g., `forge:write-haiku`)
|
|
65
|
-
- `<stage-base>` — the base of the alias
|
|
66
|
-
- `<cycle-id>` —
|
|
67
|
-
- `<
|
|
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
|
|
68
84
|
|
|
69
85
|
### Missing subagent (fail-fast)
|
|
70
86
|
|
|
71
|
-
|
|
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.
|
|
72
105
|
|
|
73
106
|
## What you do NOT do
|
|
74
107
|
|
|
75
|
-
- You do not
|
|
76
|
-
- You do not
|
|
77
|
-
- You do not
|
|
78
|
-
- You do not
|
|
79
|
-
- You do **not** inline forge/quench/appraise work — always dispatch to a subagent via the `task` tool, even when the resolved model matches the orchestrator's own model.
|
|
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).
|
|
@@ -46,7 +46,7 @@ Check each file against the current expected format:
|
|
|
46
46
|
- Has `targets` field? If not → needs target routing
|
|
47
47
|
- Has `inputs.type` (`any-of`/`all-of`)? If `inputs` is a plain list → needs contract type
|
|
48
48
|
- Has `hitl` in stages or frontmatter? → needs human-appraise migration
|
|
49
|
-
- Has `human-appraise
|
|
49
|
+
- Has nested `human-appraise: {enabled, deadlock-threshold}`? → v2.2.1 flat-keys migration (see §4b)
|
|
50
50
|
- Has `models` map? Check format
|
|
51
51
|
|
|
52
52
|
**Artefact types:**
|
|
@@ -98,6 +98,38 @@ For each `.opencode/agents/foundry-*.md` file with a `.` in its filename:
|
|
|
98
98
|
|
|
99
99
|
After renaming, remind the user: **Restart OpenCode** for the new agent filenames to register.
|
|
100
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
|
+
|
|
101
133
|
### 5. Migrate flows
|
|
102
134
|
|
|
103
135
|
For each flow needing migration:
|