@really-knows-ai/foundry 1.5.1 → 2.0.0
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 +52 -1
- package/package.json +1 -1
- package/scripts/lib/feedback.js +16 -0
- package/scripts/sort.js +19 -9
- package/skills/add-cycle/SKILL.md +42 -28
- package/skills/add-flow/SKILL.md +20 -18
- package/skills/appraise/SKILL.md +4 -0
- package/skills/cycle/SKILL.md +6 -6
- package/skills/flow/SKILL.md +41 -15
- package/skills/forge/SKILL.md +5 -2
- package/skills/human-appraise/SKILL.md +58 -0
- package/skills/sort/SKILL.md +1 -1
- package/skills/upgrade-foundry/SKILL.md +139 -0
- package/skills/hitl/SKILL.md +0 -48
|
@@ -209,13 +209,17 @@ export const FoundryPlugin = async ({ directory }) => {
|
|
|
209
209
|
}),
|
|
210
210
|
|
|
211
211
|
foundry_workfile_delete: tool({
|
|
212
|
-
description: 'Delete WORK.md',
|
|
212
|
+
description: 'Delete WORK.md and WORK.history.yaml',
|
|
213
213
|
args: {},
|
|
214
214
|
async execute(_args, context) {
|
|
215
215
|
const workPath = path.join(context.worktree, 'WORK.md');
|
|
216
|
+
const historyPath = path.join(context.worktree, 'WORK.history.yaml');
|
|
216
217
|
if (existsSync(workPath)) {
|
|
217
218
|
unlinkSync(workPath);
|
|
218
219
|
}
|
|
220
|
+
if (existsSync(historyPath)) {
|
|
221
|
+
unlinkSync(historyPath);
|
|
222
|
+
}
|
|
219
223
|
return JSON.stringify({ ok: true });
|
|
220
224
|
},
|
|
221
225
|
}),
|
|
@@ -392,6 +396,53 @@ export const FoundryPlugin = async ({ directory }) => {
|
|
|
392
396
|
},
|
|
393
397
|
}),
|
|
394
398
|
|
|
399
|
+
foundry_git_finish: tool({
|
|
400
|
+
description: 'Clean up work files, squash merge to base branch, and delete the work branch',
|
|
401
|
+
args: {
|
|
402
|
+
message: tool.schema.string().describe('Squash merge commit message'),
|
|
403
|
+
baseBranch: tool.schema.string().optional().describe('Target branch (default: main)'),
|
|
404
|
+
},
|
|
405
|
+
async execute(args, context) {
|
|
406
|
+
const base = args.baseBranch || 'main';
|
|
407
|
+
const cwd = context.worktree;
|
|
408
|
+
const opts = { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] };
|
|
409
|
+
|
|
410
|
+
// Get current branch name
|
|
411
|
+
const workBranch = execSync('git branch --show-current', opts).trim();
|
|
412
|
+
if (workBranch === base) {
|
|
413
|
+
return JSON.stringify({ error: `Already on ${base} — nothing to merge` });
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Delete work files
|
|
417
|
+
const workPath = path.join(cwd, 'WORK.md');
|
|
418
|
+
const historyPath = path.join(cwd, 'WORK.history.yaml');
|
|
419
|
+
if (existsSync(workPath)) unlinkSync(workPath);
|
|
420
|
+
if (existsSync(historyPath)) unlinkSync(historyPath);
|
|
421
|
+
|
|
422
|
+
// Commit cleanup if there are changes
|
|
423
|
+
try {
|
|
424
|
+
execSync('git add -A', opts);
|
|
425
|
+
const status = execSync('git status --porcelain', opts).trim();
|
|
426
|
+
if (status) {
|
|
427
|
+
const cleanupMsg = `[${workBranch.replace('work/', '')}] cleanup: remove work files`;
|
|
428
|
+
execSync(`git commit -m "${cleanupMsg.replace(/"/g, '\\"')}"`, opts);
|
|
429
|
+
}
|
|
430
|
+
} catch { /* no changes to commit */ }
|
|
431
|
+
|
|
432
|
+
// Switch to base and squash merge
|
|
433
|
+
execSync(`git checkout ${base}`, opts);
|
|
434
|
+
execSync(`git merge --squash ${workBranch}`, opts);
|
|
435
|
+
const msg = args.message.replace(/"/g, '\\"');
|
|
436
|
+
execSync(`git commit -m "${msg}"`, opts);
|
|
437
|
+
const hash = execSync('git rev-parse --short HEAD', opts).trim();
|
|
438
|
+
|
|
439
|
+
// Force-delete work branch (required after squash)
|
|
440
|
+
execSync(`git branch -D ${workBranch}`, opts);
|
|
441
|
+
|
|
442
|
+
return JSON.stringify({ ok: true, hash, branch: base });
|
|
443
|
+
},
|
|
444
|
+
}),
|
|
445
|
+
|
|
395
446
|
// ── Config tools ──
|
|
396
447
|
foundry_config_cycle: tool({
|
|
397
448
|
description: 'Get a cycle definition from foundry config',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@really-knows-ai/foundry",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A structured framework for AI-driven artefact creation with deterministic routing, quality gates, and iterative refinement cycles.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": ".opencode/plugins/foundry.js",
|
package/scripts/lib/feedback.js
CHANGED
|
@@ -237,6 +237,22 @@ export function listFeedback(text, cycle, artefacts, filterFile) {
|
|
|
237
237
|
return results;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Detect feedback items stuck in a deadlock — rejected N or more times.
|
|
242
|
+
* A deadlock occurs when forge-appraise cycles keep rejecting the same item.
|
|
243
|
+
*/
|
|
244
|
+
export function detectDeadlocks(feedback, history, threshold = 3) {
|
|
245
|
+
// Count forge→appraise cycles (each pair = one iteration)
|
|
246
|
+
const forgeAppraiseCount = history.filter(
|
|
247
|
+
e => (e.stage || '').split(':')[0] === 'appraise'
|
|
248
|
+
).length;
|
|
249
|
+
|
|
250
|
+
if (forgeAppraiseCount < threshold) return [];
|
|
251
|
+
|
|
252
|
+
// Items that are still rejected after threshold iterations are deadlocked
|
|
253
|
+
return feedback.filter(f => f.state === 'rejected' || f.state === 'open');
|
|
254
|
+
}
|
|
255
|
+
|
|
240
256
|
// ---------------------------------------------------------------------------
|
|
241
257
|
// Internal helpers
|
|
242
258
|
// ---------------------------------------------------------------------------
|
package/scripts/sort.js
CHANGED
|
@@ -21,7 +21,7 @@ import { validateTags } from './lib/tags.js';
|
|
|
21
21
|
import { parseFrontmatter } from './lib/workfile.js';
|
|
22
22
|
import { parseArtefactsTable } from './lib/artefacts.js';
|
|
23
23
|
import { loadHistory } from './lib/history.js';
|
|
24
|
-
import { parseFeedback, parseFeedbackItem } from './lib/feedback.js';
|
|
24
|
+
import { parseFeedback, parseFeedbackItem, detectDeadlocks } from './lib/feedback.js';
|
|
25
25
|
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
// Stage helpers
|
|
@@ -73,11 +73,6 @@ function determineRoute(stages, history, feedback, maxIterations) {
|
|
|
73
73
|
|
|
74
74
|
if (lastBase === null) return stages[0];
|
|
75
75
|
|
|
76
|
-
if (lastBase === 'hitl') {
|
|
77
|
-
const next = nextInRoute(stages, lastEntry);
|
|
78
|
-
return next ?? 'done';
|
|
79
|
-
}
|
|
80
|
-
|
|
81
76
|
if (lastBase === 'forge') {
|
|
82
77
|
const next = nextInRoute(stages, lastEntry);
|
|
83
78
|
return next ?? 'done';
|
|
@@ -88,7 +83,11 @@ function determineRoute(stages, history, feedback, maxIterations) {
|
|
|
88
83
|
}
|
|
89
84
|
|
|
90
85
|
if (lastBase === 'appraise') {
|
|
91
|
-
return nextAfterAppraise(stages, feedback, forgeCount, maxIterations);
|
|
86
|
+
return nextAfterAppraise(stages, lastEntry, feedback, forgeCount, maxIterations, nonSortHistory);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (lastBase === 'human-appraise') {
|
|
90
|
+
return nextAfterAppraise(stages, lastEntry, feedback, forgeCount, maxIterations, nonSortHistory);
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
return 'blocked';
|
|
@@ -104,7 +103,18 @@ function nextAfterQuench(stages, current, feedback, forgeCount, maxIterations) {
|
|
|
104
103
|
return nextInRoute(stages, current) ?? 'done';
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
function nextAfterAppraise(stages, feedback, forgeCount, maxIterations) {
|
|
106
|
+
function nextAfterAppraise(stages, current, feedback, forgeCount, maxIterations, history = []) {
|
|
107
|
+
// Check for deadlock escalation
|
|
108
|
+
const deadlocked = detectDeadlocks(feedback, history);
|
|
109
|
+
if (deadlocked.length > 0) {
|
|
110
|
+
const humanAppraise = findFirst(stages, 'human-appraise');
|
|
111
|
+
if (humanAppraise && baseStage(current) !== 'human-appraise') {
|
|
112
|
+
return humanAppraise;
|
|
113
|
+
}
|
|
114
|
+
// Human-appraise not available or we're already in it — blocked
|
|
115
|
+
if (forgeCount >= maxIterations) return 'blocked';
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
const needsForge = feedback.some(f => f.state === 'open' || f.state === 'rejected');
|
|
109
119
|
if (needsForge) {
|
|
110
120
|
if (forgeCount >= maxIterations) return 'blocked';
|
|
@@ -118,7 +128,7 @@ function nextAfterAppraise(stages, feedback, forgeCount, maxIterations) {
|
|
|
118
128
|
return findFirst(stages, 'appraise') ?? 'blocked';
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
return 'done';
|
|
131
|
+
return nextInRoute(stages, current) ?? 'done';
|
|
122
132
|
}
|
|
123
133
|
|
|
124
134
|
// ---------------------------------------------------------------------------
|
|
@@ -6,7 +6,7 @@ description: Creates a new foundry cycle within a foundry flow, specifying the o
|
|
|
6
6
|
|
|
7
7
|
# Add Cycle
|
|
8
8
|
|
|
9
|
-
You help the user create a new foundry cycle and add it to an existing foundry flow. A foundry cycle produces one artefact type (read-write)
|
|
9
|
+
You help the user create a new foundry cycle and add it to an existing foundry flow. A foundry cycle produces one artefact type (read-write), declares its input contract, targets the next cycle(s), and optionally enables human-appraise as a quality gate.
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
@@ -28,7 +28,11 @@ From the user's prompt, establish:
|
|
|
28
28
|
- `id` — lowercase, hyphenated identifier for the foundry cycle
|
|
29
29
|
- `name` — human-readable name
|
|
30
30
|
- `output` — the artefact type this foundry cycle produces (must exist in `foundry/artefacts/`)
|
|
31
|
-
- `inputs` — artefact types
|
|
31
|
+
- `inputs` — artefact types this cycle reads, with a contract type:
|
|
32
|
+
- `type`: `any-of` (at least one must exist) or `all-of` (all must exist)
|
|
33
|
+
- `artefacts`: list of artefact type IDs
|
|
34
|
+
- May be empty for starting cycles
|
|
35
|
+
- `targets` — cycle(s) to route to after this cycle completes (may be empty for terminal cycles)
|
|
32
36
|
- A prose description of what this foundry cycle does
|
|
33
37
|
|
|
34
38
|
If any of these are missing, ask.
|
|
@@ -46,13 +50,22 @@ For each stage in the cycle (forge, quench, appraise), ask the user if they want
|
|
|
46
50
|
|
|
47
51
|
Only stages with an explicitly specified model are included in the `models` frontmatter map.
|
|
48
52
|
|
|
49
|
-
### 4.
|
|
53
|
+
### 4. Configure human appraise
|
|
54
|
+
|
|
55
|
+
Ask the user:
|
|
56
|
+
|
|
57
|
+
> Do you want a human quality gate on this cycle? If enabled, a human reviewer will check the artefact after LLM appraisers pass, and can break deadlocks between forge and appraisers.
|
|
58
|
+
>
|
|
59
|
+
> - Enable human-appraise? (yes/no)
|
|
60
|
+
> - If yes, deadlock threshold (default: 3 — number of forge/appraise iterations before escalating to human)
|
|
61
|
+
|
|
62
|
+
### 5. Validate artefact types
|
|
50
63
|
|
|
51
64
|
For `output` and each entry in `inputs`:
|
|
52
65
|
- Verify the artefact type exists in `foundry/artefacts/<type>/definition.md`
|
|
53
66
|
- If it doesn't, tell the user and ask if they want to create it first (separate skill)
|
|
54
67
|
|
|
55
|
-
###
|
|
68
|
+
### 6. Validate against the foundry flow
|
|
56
69
|
|
|
57
70
|
Read the flow definition from `foundry/flows/<flow-id>.md`. Check:
|
|
58
71
|
|
|
@@ -66,13 +79,13 @@ Read the flow definition from `foundry/flows/<flow-id>.md`. Check:
|
|
|
66
79
|
> 2. Remove `<type>` from inputs (this foundry cycle won't have that context)
|
|
67
80
|
> 3. Proceed anyway (the artefact may exist from a previous foundry flow run)
|
|
68
81
|
|
|
69
|
-
###
|
|
82
|
+
### 7. Check for id conflicts
|
|
70
83
|
|
|
71
84
|
Read all existing cycle definitions in `foundry/cycles/*.md`.
|
|
72
85
|
|
|
73
86
|
- Exact id match → hard conflict, must choose a different id
|
|
74
87
|
|
|
75
|
-
###
|
|
88
|
+
### 8. Check for semantic overlap
|
|
76
89
|
|
|
77
90
|
For foundry cycles already in this foundry flow, check whether the new foundry cycle overlaps in purpose:
|
|
78
91
|
- Does another foundry cycle already transform the same inputs into a similar output?
|
|
@@ -80,7 +93,7 @@ For foundry cycles already in this foundry flow, check whether the new foundry c
|
|
|
80
93
|
|
|
81
94
|
If overlap is found, present it and ask the user to confirm the distinction is real.
|
|
82
95
|
|
|
83
|
-
###
|
|
96
|
+
### 9. Draft the definition
|
|
84
97
|
|
|
85
98
|
Present the foundry cycle definition to the user:
|
|
86
99
|
|
|
@@ -90,41 +103,42 @@ id: <id>
|
|
|
90
103
|
name: <name>
|
|
91
104
|
output: <artefact-type-id>
|
|
92
105
|
inputs:
|
|
93
|
-
|
|
106
|
+
type: <any-of|all-of>
|
|
107
|
+
artefacts:
|
|
108
|
+
- <artefact-type-id>
|
|
109
|
+
targets:
|
|
110
|
+
- <cycle-id>
|
|
111
|
+
human-appraise:
|
|
112
|
+
enabled: <true|false>
|
|
113
|
+
deadlock-threshold: <number>
|
|
94
114
|
models:
|
|
95
|
-
appraise: <model-id>
|
|
115
|
+
appraise: <model-id>
|
|
96
116
|
---
|
|
97
117
|
|
|
98
118
|
# <Name>
|
|
99
119
|
|
|
100
|
-
<description
|
|
120
|
+
<description>
|
|
101
121
|
```
|
|
102
122
|
|
|
103
123
|
Ask: does this capture the foundry cycle correctly?
|
|
104
124
|
|
|
105
|
-
###
|
|
125
|
+
### 10. Validate target routing
|
|
106
126
|
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
127
|
+
For each target cycle:
|
|
128
|
+
- Verify the target cycle exists in `foundry/cycles/`
|
|
129
|
+
- Verify this cycle's output type satisfies at least one of the target's input artefacts
|
|
130
|
+
- If the target doesn't exist yet, note it as pending
|
|
110
131
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
>
|
|
115
|
-
> Proposed order:
|
|
116
|
-
> 1. <existing-cycle>
|
|
117
|
-
> 2. <existing-cycle>
|
|
118
|
-
> 3. <new-cycle> ← here
|
|
119
|
-
>
|
|
120
|
-
> Does this look right?
|
|
132
|
+
For input validation:
|
|
133
|
+
- Verify that at least one cycle in the flow has the input artefact type(s) as its output
|
|
134
|
+
- If using `all-of`, verify all input types are producible
|
|
121
135
|
|
|
122
|
-
###
|
|
136
|
+
### 11. Write files
|
|
123
137
|
|
|
124
|
-
- Create `foundry/cycles/<id>.md` with the
|
|
125
|
-
- Update `foundry/flows/<flow-id>.md` to add the
|
|
138
|
+
- Create `foundry/cycles/<id>.md` with the cycle definition
|
|
139
|
+
- Update `foundry/flows/<flow-id>.md` to add the cycle to the `## Cycles` list (if not already present)
|
|
126
140
|
|
|
127
|
-
###
|
|
141
|
+
### 12. Confirm
|
|
128
142
|
|
|
129
143
|
Show the user the created/modified files and their contents.
|
|
130
144
|
|
package/skills/add-flow/SKILL.md
CHANGED
|
@@ -6,7 +6,7 @@ description: Creates a new foundry flow definition.
|
|
|
6
6
|
|
|
7
7
|
# Add Flow
|
|
8
8
|
|
|
9
|
-
You help the user create a new foundry flow. A foundry flow is
|
|
9
|
+
You help the user create a new foundry flow. A foundry flow is a set of foundry cycles with declared starting points — cycles own their own routing via targets and input contracts.
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
@@ -32,35 +32,35 @@ Read all existing flow definitions in `foundry/flows/*.md`.
|
|
|
32
32
|
- Exact id match → hard conflict, must choose a different id
|
|
33
33
|
- Semantically similar name or description → warn and ask if the new foundry flow is genuinely distinct
|
|
34
34
|
|
|
35
|
-
### 3. Determine foundry cycles
|
|
35
|
+
### 3. Determine foundry cycles and starting cycles
|
|
36
36
|
|
|
37
|
-
Ask the user which foundry cycles this
|
|
37
|
+
Ask the user which foundry cycles this flow includes. List available cycles from `foundry/cycles/*.md` for reference.
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
- Pick from existing foundry cycles
|
|
41
|
-
- Describe foundry cycles that don't exist yet — note these and tell the user they'll need to create them with the add-cycle skill
|
|
39
|
+
Then ask: which of these are **starting cycles** — the cycles that can be entered first when the flow begins?
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
- Starting cycles typically have no input dependencies
|
|
42
|
+
- Multiple starting cycles are fine — the user (or context) determines which one to run first
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
### 4. Validate cycle graph
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
For each non-starting cycle, verify it is reachable:
|
|
47
|
+
- At least one other cycle in the flow has it as a target
|
|
48
|
+
- Its input contract can be satisfied by cycles in the flow
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
- For each foundry cycle, its `inputs` must be produced as `output` by an earlier foundry cycle in the list
|
|
50
|
+
If a cycle is unreachable (no cycle targets it and it's not a starting cycle), warn:
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
> Foundry cycle `<cycle-id>` reads from `<type>`, but no earlier foundry cycle produces it. Either reorder or add a foundry cycle that produces `<type>` first.
|
|
52
|
+
> Cycle `<id>` is not a starting cycle and no other cycle targets it. It will never be reached in this flow.
|
|
55
53
|
|
|
56
54
|
### 5. Draft the definition
|
|
57
55
|
|
|
58
|
-
Present the
|
|
56
|
+
Present the flow definition to the user:
|
|
59
57
|
|
|
60
58
|
```markdown
|
|
61
59
|
---
|
|
62
60
|
id: <id>
|
|
63
61
|
name: <name>
|
|
62
|
+
starting-cycles:
|
|
63
|
+
- <cycle-id>
|
|
64
64
|
---
|
|
65
65
|
|
|
66
66
|
# <Name>
|
|
@@ -69,11 +69,13 @@ name: <name>
|
|
|
69
69
|
|
|
70
70
|
## Cycles
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
- <cycle-id>
|
|
73
|
+
- <cycle-id>
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
The `starting-cycles` field lists entry points. `## Cycles` lists all cycles in the flow (no ordering implied — routing is owned by individual cycle definitions via their `targets` field).
|
|
77
|
+
|
|
78
|
+
Ask: does this capture the flow correctly?
|
|
77
79
|
|
|
78
80
|
### 6. Write the file
|
|
79
81
|
|
package/skills/appraise/SKILL.md
CHANGED
|
@@ -93,6 +93,10 @@ If there are no issues, return an empty list.
|
|
|
93
93
|
|
|
94
94
|
Do NOT call `foundry_history_append` — the sort skill (your caller) is responsible for writing history. Instead, return a clear summary of what you found (e.g., "3 issues found across 2 appraisers" or "No issues found") so sort can log it.
|
|
95
95
|
|
|
96
|
+
### Human override awareness
|
|
97
|
+
|
|
98
|
+
When reviewing an artefact, check the feedback history for `#human` tagged items. If a human has already ruled on a topic in a prior iteration, do not re-raise the same issue — the human's decision is final.
|
|
99
|
+
|
|
96
100
|
## What you do NOT do
|
|
97
101
|
|
|
98
102
|
- You do not revise the artefact
|
package/skills/cycle/SKILL.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
name: cycle
|
|
3
3
|
type: composite
|
|
4
4
|
description: Runs a foundry cycle by delegating all routing to the sort skill.
|
|
5
|
-
composes: [sort, forge, quench, appraise,
|
|
5
|
+
composes: [sort, forge, quench, appraise, human-appraise]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Cycle
|
|
9
9
|
|
|
10
|
-
A foundry cycle reads its definition, sets up the work file for routing, then hands control to the sort skill which drives the forge/quench/appraise loop.
|
|
10
|
+
A foundry cycle reads its definition, sets up the work file for routing, then hands control to the sort skill which drives the forge/quench/appraise/human-appraise loop.
|
|
11
11
|
|
|
12
12
|
## Prerequisites
|
|
13
13
|
|
|
@@ -22,7 +22,7 @@ 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
|
-
-
|
|
25
|
+
- If the cycle definition has `human-appraise.enabled: true`, append `human-appraise` as the final stage
|
|
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
27
|
4. Call `foundry_workfile_set` to configure the work file:
|
|
28
28
|
- `key: "cycle"`, `value: <cycle-id>`
|
|
@@ -47,9 +47,9 @@ When sort returns `blocked`:
|
|
|
47
47
|
- Call `foundry_artefacts_set_status` with status `"blocked"`
|
|
48
48
|
- Return control to the flow skill (the flow decides how to handle it)
|
|
49
49
|
|
|
50
|
-
##
|
|
50
|
+
## Human Appraise
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
If the cycle definition has `human-appraise.enabled: true`, the human-appraise stage is included after appraise. Sort will route to it after LLM appraisers pass, or earlier if a deadlock is detected.
|
|
53
53
|
|
|
54
54
|
## Micro commits
|
|
55
55
|
|
|
@@ -70,7 +70,7 @@ approved - resolved
|
|
|
70
70
|
rejected - re-opened
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
Tag types: `validation` (from quench), `law:<law-id>` (from appraise), `
|
|
73
|
+
Tag types: `validation` (from quench), `law:<law-id>` (from appraise), `human` (from human-appraise) — indicates the source and category of feedback.
|
|
74
74
|
|
|
75
75
|
## What you do NOT do
|
|
76
76
|
|
package/skills/flow/SKILL.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: flow
|
|
3
3
|
type: composite
|
|
4
|
-
description: Orchestrates foundry cycles
|
|
4
|
+
description: Orchestrates foundry cycles as a dependency graph, driven by a flow definition.
|
|
5
5
|
composes: [cycle]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Flow
|
|
9
9
|
|
|
10
|
-
A foundry flow reads a flow definition, creates a work branch,
|
|
10
|
+
A foundry flow reads a flow definition, creates a work branch, and executes cycles by following the dependency graph — each cycle declares its own targets and input contracts.
|
|
11
11
|
|
|
12
12
|
## Prerequisites
|
|
13
13
|
|
|
@@ -15,26 +15,52 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
15
15
|
|
|
16
16
|
> Foundry is not initialized in this project. Run the `init-foundry` skill first to create the foundry/ directory structure.
|
|
17
17
|
|
|
18
|
-
## Starting a
|
|
18
|
+
## Starting a flow
|
|
19
19
|
|
|
20
20
|
1. Call `foundry_config_flow` with the flow ID — get the flow definition
|
|
21
|
-
2. Call `foundry_git_branch` with
|
|
22
|
-
3.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
2. Call `foundry_git_branch` with the flow ID and a short description — create the work branch
|
|
22
|
+
3. Determine the starting cycle:
|
|
23
|
+
- If only one starting cycle, use it
|
|
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
|
+
- If ambiguous, prompt the user to choose
|
|
26
|
+
4. Call `foundry_workfile_create` with the flow ID, chosen cycle ID, and goal
|
|
27
|
+
5. Execute the cycle by invoking the cycle skill
|
|
26
28
|
|
|
27
|
-
##
|
|
29
|
+
## Between cycles
|
|
28
30
|
|
|
29
|
-
When
|
|
30
|
-
- The finished artefacts
|
|
31
|
-
- The full git history of micro commits showing every stage
|
|
31
|
+
When a cycle completes (sort returns `done`):
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
1. Read the completed cycle's definition to find its `targets`
|
|
34
|
+
2. If no targets → this branch of the flow is done. Proceed to "Completing a flow"
|
|
35
|
+
3. If one target:
|
|
36
|
+
- Read the target cycle's definition
|
|
37
|
+
- Check input contract: `any-of` requires at least one listed artefact type to exist as a completed artefact; `all-of` requires all
|
|
38
|
+
- If satisfied → ask the user if they want to proceed, or run another starting cycle first
|
|
39
|
+
- If not satisfied → inform the user which artefacts are missing, offer to run cycles that produce them
|
|
40
|
+
4. If multiple targets:
|
|
41
|
+
- Present the options to the user
|
|
42
|
+
- Check input contracts for each
|
|
43
|
+
- The user chooses which target to pursue (or which to pursue first)
|
|
44
|
+
5. Set up the next cycle:
|
|
45
|
+
- Call `foundry_workfile_set` with `key: "cycle"`, `value: <next-cycle-id>`
|
|
46
|
+
- Reset stages and iteration count for the new cycle
|
|
47
|
+
- Execute the cycle by invoking the cycle skill
|
|
48
|
+
|
|
49
|
+
## Completing a flow
|
|
50
|
+
|
|
51
|
+
When all desired cycles are done:
|
|
52
|
+
|
|
53
|
+
1. Present a summary of what was produced (all artefacts and their status)
|
|
54
|
+
2. Ask the user how they want to finish:
|
|
55
|
+
- **Squash merge** — call `foundry_git_finish` with a commit message and base branch
|
|
56
|
+
- **Keep the branch** — leave as-is for manual handling
|
|
57
|
+
- **Create a PR** — push and create a pull request
|
|
58
|
+
3. Execute the chosen option
|
|
34
59
|
|
|
35
60
|
## What you do NOT do
|
|
36
61
|
|
|
37
|
-
- You do not skip
|
|
38
|
-
- You do not reorder cycles
|
|
62
|
+
- You do not skip input contract validation
|
|
39
63
|
- You do not modify artefacts directly — only cycles modify artefacts
|
|
40
64
|
- You do not delete or rewrite feedback history during the flow
|
|
65
|
+
- You do not route to a target cycle whose input contract is not met
|
|
66
|
+
- You do not assume cycle order — follow the targets declared by each cycle
|
package/skills/forge/SKILL.md
CHANGED
|
@@ -50,9 +50,12 @@ An item is unresolved if it is:
|
|
|
50
50
|
|
|
51
51
|
An item is resolved if it is `approved`.
|
|
52
52
|
|
|
53
|
-
##
|
|
53
|
+
## #human feedback
|
|
54
54
|
|
|
55
|
-
Feedback tagged `
|
|
55
|
+
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
|
|
56
59
|
|
|
57
60
|
## What you do NOT do
|
|
58
61
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: human-appraise
|
|
3
|
+
type: atomic
|
|
4
|
+
description: Human quality gate. Presents the artefact to the human for review and collects feedback tagged #human.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Human Appraise
|
|
8
|
+
|
|
9
|
+
You are a human quality gate. Sort has routed to you either because the LLM appraisers have finished (normal flow) or because a deadlock was detected between forge and appraisers.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
Before running this skill, verify that the `foundry/` directory exists in the project root. If it does not exist, stop and tell the user:
|
|
14
|
+
|
|
15
|
+
> Foundry is not initialized in this project. Run the `init-foundry` skill first to create the foundry/ directory structure.
|
|
16
|
+
|
|
17
|
+
## Protocol
|
|
18
|
+
|
|
19
|
+
1. Gather context by calling:
|
|
20
|
+
- `foundry_workfile_get` — current state, goal, artefacts
|
|
21
|
+
- `foundry_artefacts_list` — current artefact files and status
|
|
22
|
+
- `foundry_feedback_list` — all existing feedback
|
|
23
|
+
- `foundry_history_list` — what has happened so far
|
|
24
|
+
|
|
25
|
+
2. Read the artefact file(s) for this cycle.
|
|
26
|
+
|
|
27
|
+
3. Present to the human:
|
|
28
|
+
- The current artefact content (full file content or multi-file diff)
|
|
29
|
+
- A summary of this iteration's feedback (resolved and open)
|
|
30
|
+
- If this is a deadlock escalation, clearly explain the deadlock:
|
|
31
|
+
- Which feedback item(s) are stuck
|
|
32
|
+
- The appraiser's reasoning
|
|
33
|
+
- Forge's wont-fix or revision justification
|
|
34
|
+
- Ask the human to resolve the disagreement
|
|
35
|
+
|
|
36
|
+
4. Wait for the human's response.
|
|
37
|
+
|
|
38
|
+
5. Act on the response:
|
|
39
|
+
- **Approve** — "looks good" / "continue" — no feedback added, sort will advance
|
|
40
|
+
- **Provide feedback** — call `foundry_feedback_add` with the human's feedback and tag `human`. Sort will route back to forge.
|
|
41
|
+
- **Dismiss deadlocked feedback** — call `foundry_feedback_resolve` with `resolution: "approved"` on the deadlocked item(s). This overrides the appraiser.
|
|
42
|
+
- **Abort** — call `foundry_artefacts_set_status` with status `"blocked"`, cycle ends
|
|
43
|
+
|
|
44
|
+
6. Return a clear summary of what the human decided so sort can log it in history.
|
|
45
|
+
|
|
46
|
+
## #human feedback rules
|
|
47
|
+
|
|
48
|
+
- Feedback tagged `human` takes priority over all LLM appraiser feedback
|
|
49
|
+
- Forge MUST address `#human` feedback — it cannot wont-fix it
|
|
50
|
+
- When `#human` feedback contradicts LLM appraiser feedback, forge follows the human's direction
|
|
51
|
+
|
|
52
|
+
## What you do NOT do
|
|
53
|
+
|
|
54
|
+
- You do not make decisions for the human — present the state and wait
|
|
55
|
+
- You do not modify the artefact
|
|
56
|
+
- You do not skip the pause — the human must respond before continuing
|
|
57
|
+
- You do not filter or summarise away important details — show the full picture
|
|
58
|
+
- You do not call `foundry_history_append` — sort owns history writing
|
package/skills/sort/SKILL.md
CHANGED
|
@@ -24,7 +24,7 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
24
24
|
- `forge:*` — dispatch the forge skill as a sub-agent. Use model dispatch (see below).
|
|
25
25
|
- `quench:*` — dispatch the quench skill as a sub-agent. Use model dispatch.
|
|
26
26
|
- `appraise:*` — dispatch the appraise skill as a sub-agent. Use model dispatch. Note: the appraise skill handles its own per-appraiser model resolution internally.
|
|
27
|
-
- `
|
|
27
|
+
- `human-appraise:*` — invoke the human-appraise skill (no model dispatch — human stage)
|
|
28
28
|
- `done` — foundry cycle is complete, return to the cycle skill
|
|
29
29
|
- `blocked` — foundry cycle is blocked (iteration limit hit with unresolved feedback), return to the cycle skill
|
|
30
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
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upgrade-foundry
|
|
3
|
+
type: atomic
|
|
4
|
+
description: Analyses and migrates foundry configuration to the current version format.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Upgrade Foundry
|
|
8
|
+
|
|
9
|
+
You analyse the entire `foundry/` directory and migrate configuration files to the current format, asking the user for clarification where needed.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
Before running this skill, verify that the `foundry/` directory exists in the project root. If it does not exist, stop and tell the user:
|
|
14
|
+
|
|
15
|
+
> Foundry is not initialized in this project. Run the `init-foundry` skill first to create the foundry/ directory structure.
|
|
16
|
+
|
|
17
|
+
## Protocol
|
|
18
|
+
|
|
19
|
+
### 1. Scan entire foundry directory
|
|
20
|
+
|
|
21
|
+
Read all configuration files:
|
|
22
|
+
- `foundry/flows/*.md` — flow definitions
|
|
23
|
+
- `foundry/cycles/*.md` — cycle definitions
|
|
24
|
+
- `foundry/artefacts/*/definition.md` — artefact type definitions
|
|
25
|
+
- `foundry/artefacts/*/laws.md` — type-specific laws
|
|
26
|
+
- `foundry/artefacts/*/validation.md` — validation commands
|
|
27
|
+
- `foundry/laws/*.md` — global laws
|
|
28
|
+
- `foundry/appraisers/*.md` — appraiser definitions
|
|
29
|
+
|
|
30
|
+
For each file, parse the frontmatter and body content.
|
|
31
|
+
|
|
32
|
+
### 2. Detect what needs migration
|
|
33
|
+
|
|
34
|
+
Check each file against the current expected format:
|
|
35
|
+
|
|
36
|
+
**Flows:**
|
|
37
|
+
- Has `starting-cycles` field? If not → needs DAG migration
|
|
38
|
+
- Has ordered numbered list under `## Cycles`? → needs conversion to unordered list
|
|
39
|
+
|
|
40
|
+
**Cycles:**
|
|
41
|
+
- Has `targets` field? If not → needs target routing
|
|
42
|
+
- Has `inputs.type` (`any-of`/`all-of`)? If `inputs` is a plain list → needs contract type
|
|
43
|
+
- Has `hitl` in stages or frontmatter? → needs human-appraise migration
|
|
44
|
+
- Has `human-appraise` config? Check format is correct
|
|
45
|
+
- Has `models` map? Check format
|
|
46
|
+
|
|
47
|
+
**Artefact types:**
|
|
48
|
+
- Has required frontmatter fields (`id`, `name`, `file-patterns`)?
|
|
49
|
+
- Has `appraisers` config if applicable?
|
|
50
|
+
|
|
51
|
+
**Appraisers:**
|
|
52
|
+
- Has `id` and personality content?
|
|
53
|
+
- Has optional `model` field?
|
|
54
|
+
- References any deprecated stage types?
|
|
55
|
+
|
|
56
|
+
**Laws:**
|
|
57
|
+
- Uses `## heading` per law?
|
|
58
|
+
- Any structural issues?
|
|
59
|
+
|
|
60
|
+
**Validation:**
|
|
61
|
+
- Uses `Command:` / `Failure means:` format?
|
|
62
|
+
- Commands have backticks that could cause issues? (Suggest removing — the parser strips them but clean is better)
|
|
63
|
+
|
|
64
|
+
### 3. Present findings
|
|
65
|
+
|
|
66
|
+
Present a grouped summary of all issues found:
|
|
67
|
+
|
|
68
|
+
> **Migration Report**
|
|
69
|
+
>
|
|
70
|
+
> **Flows (N issues):**
|
|
71
|
+
> - `creative-flow.md` — missing `starting-cycles`, has ordered cycle list
|
|
72
|
+
>
|
|
73
|
+
> **Cycles (N issues):**
|
|
74
|
+
> - `create-haiku.md` — missing `targets` field
|
|
75
|
+
> - `create-short-story.md` — inputs is plain list, needs `any-of`/`all-of` contract
|
|
76
|
+
>
|
|
77
|
+
> **Artefact types (N issues):**
|
|
78
|
+
> - (none found)
|
|
79
|
+
>
|
|
80
|
+
> **Appraisers (N issues):**
|
|
81
|
+
> - (none found)
|
|
82
|
+
>
|
|
83
|
+
> **Everything else clean**
|
|
84
|
+
|
|
85
|
+
If nothing needs migration, say so and stop.
|
|
86
|
+
|
|
87
|
+
### 4. Migrate flows
|
|
88
|
+
|
|
89
|
+
For each flow needing migration:
|
|
90
|
+
- Show the current ordered cycle list
|
|
91
|
+
- Ask: which cycles are starting cycles?
|
|
92
|
+
- Infer targets from adjacency (cycle N → cycle N+1)
|
|
93
|
+
- Present the proposed `starting-cycles` and confirm
|
|
94
|
+
- Convert numbered `## Cycles` list to unordered
|
|
95
|
+
|
|
96
|
+
### 5. Migrate cycles
|
|
97
|
+
|
|
98
|
+
For each cycle needing migration:
|
|
99
|
+
|
|
100
|
+
**Targets:** Infer from the flow's old ordering. Present and confirm:
|
|
101
|
+
> Cycle `create-haiku` was followed by `create-short-story` in the flow. Set `targets: [create-short-story]`?
|
|
102
|
+
|
|
103
|
+
**Input contracts:** If inputs exist as a plain list, ask:
|
|
104
|
+
> Cycle `create-short-story` has inputs `[haiku, limerick]`. Should it require:
|
|
105
|
+
> 1. `any-of` — at least one must exist
|
|
106
|
+
> 2. `all-of` — all must exist
|
|
107
|
+
|
|
108
|
+
**HITL migration:** If `hitl` is found in stages:
|
|
109
|
+
> Cycle `create-haiku` has an `hitl` stage. This has been replaced by `human-appraise`.
|
|
110
|
+
> - Enable human-appraise? (yes/no)
|
|
111
|
+
> - Deadlock threshold? (default: 3)
|
|
112
|
+
|
|
113
|
+
Remove `hitl` from stages and add `human-appraise` config if enabled.
|
|
114
|
+
|
|
115
|
+
### 6. Migrate other config
|
|
116
|
+
|
|
117
|
+
For artefact types, appraisers, laws, and validation with issues:
|
|
118
|
+
- Present each issue with a suggested fix
|
|
119
|
+
- Ask the user to confirm or adjust
|
|
120
|
+
|
|
121
|
+
### 7. Present migration plan
|
|
122
|
+
|
|
123
|
+
Before writing anything, show the complete list of changes:
|
|
124
|
+
- Group by category
|
|
125
|
+
- Show each file and the specific changes
|
|
126
|
+
- Ask for confirmation
|
|
127
|
+
|
|
128
|
+
### 8. Apply changes
|
|
129
|
+
|
|
130
|
+
- Update all affected files
|
|
131
|
+
- Commit with message: `[foundry] upgrade: migrate to current format`
|
|
132
|
+
|
|
133
|
+
## What you do NOT do
|
|
134
|
+
|
|
135
|
+
- You do not create new cycles, artefact types, or appraisers
|
|
136
|
+
- You do not delete existing files without confirmation
|
|
137
|
+
- You do not modify artefact content (produced artefacts, not config)
|
|
138
|
+
- You do not run automatically — the user invokes it explicitly
|
|
139
|
+
- You do not guess when uncertain — ask the user
|
package/skills/hitl/SKILL.md
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: hitl
|
|
3
|
-
type: atomic
|
|
4
|
-
description: Human-in-the-loop checkpoint. Pauses the cycle for human input before continuing.
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# HITL
|
|
8
|
-
|
|
9
|
-
You are a human-in-the-loop checkpoint. Sort has routed to you because the cycle definition includes a pause point here. Your job is to present context, ask the human whatever needs asking, record their response, and return control to sort.
|
|
10
|
-
|
|
11
|
-
## Prerequisites
|
|
12
|
-
|
|
13
|
-
Before running this skill, verify that the `foundry/` directory exists in the project root. If it does not exist, stop and tell the user:
|
|
14
|
-
|
|
15
|
-
> Foundry is not initialized in this project. Run the `init-foundry` skill first to create the foundry/ directory structure.
|
|
16
|
-
|
|
17
|
-
## Protocol
|
|
18
|
-
|
|
19
|
-
1. Gather context by calling:
|
|
20
|
-
- `foundry_workfile_get` — current state, goal, artefacts
|
|
21
|
-
- `foundry_config_cycle` — cycle definition and hitl configuration
|
|
22
|
-
- `foundry_history_list` — what has happened so far
|
|
23
|
-
- `foundry_feedback_list` — any existing feedback
|
|
24
|
-
|
|
25
|
-
2. Present to the human:
|
|
26
|
-
- A summary of where we are in the cycle (what's happened so far)
|
|
27
|
-
- The current state of the artefact (show it or summarise it)
|
|
28
|
-
- Any feedback that exists
|
|
29
|
-
- The prompt from the hitl configuration (or a sensible default: "The cycle has paused for your input. Here's the current state. How would you like to proceed?")
|
|
30
|
-
|
|
31
|
-
3. Wait for the human's response.
|
|
32
|
-
|
|
33
|
-
4. Act on the response:
|
|
34
|
-
- **Approve** — "looks good, continue" — no changes needed, sort will route to next stage
|
|
35
|
-
- **Request changes** — call `foundry_feedback_add` with the human's request and tag `"hitl"`
|
|
36
|
-
- **Provide context** — note in the history comment for future stages to reference
|
|
37
|
-
- **Abort** — call `foundry_artefacts_set_status` with status `"blocked"`, cycle ends
|
|
38
|
-
|
|
39
|
-
5. Do NOT call `foundry_history_append` — the sort skill (your caller) is responsible for writing history. Instead, return a clear summary of what the human said or decided so sort can log it.
|
|
40
|
-
|
|
41
|
-
6. Return control to the sort skill.
|
|
42
|
-
|
|
43
|
-
## What you do NOT do
|
|
44
|
-
|
|
45
|
-
- You do not make decisions for the human — present the state and wait
|
|
46
|
-
- You do not modify the artefact
|
|
47
|
-
- You do not skip the pause — the human must respond before continuing
|
|
48
|
-
- You do not filter or summarise away important details — show the full picture
|