@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.
@@ -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": "1.5.1",
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",
@@ -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) and optionally reads from artefact types produced by earlier foundry cycles (read-only).
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 from earlier foundry cycles that this foundry cycle reads (may be empty)
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. Validate artefact types
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
- ### 5. Validate against the foundry flow
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
- ### 6. Check for id conflicts
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
- ### 7. Check for semantic overlap
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
- ### 8. Draft the definition
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
- - <artefact-type-id>
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> # optional, only if specified
115
+ appraise: <model-id>
96
116
  ---
97
117
 
98
118
  # <Name>
99
119
 
100
- <description — what this foundry cycle does, what it reads, what it produces>
120
+ <description>
101
121
  ```
102
122
 
103
123
  Ask: does this capture the foundry cycle correctly?
104
124
 
105
- ### 9. Determine position in foundry flow
125
+ ### 10. Validate target routing
106
126
 
107
- The foundry cycle needs a position in the foundry flow's ordered cycle list. Based on its inputs:
108
- - If no inputs: it can go first (or early)
109
- - If it reads from other foundry cycles: it must come after those foundry cycles
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
- Propose a position and confirm with the user:
112
-
113
- > This foundry cycle reads from `<inputs>`, which are produced by `<cycle-ids>`. It should go after those foundry cycles.
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
- ### 10. Write files
136
+ ### 11. Write files
123
137
 
124
- - Create `foundry/cycles/<id>.md` with the foundry cycle definition
125
- - Update `foundry/flows/<flow-id>.md` to add the foundry cycle at the agreed position
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
- ### 11. Confirm
141
+ ### 12. Confirm
128
142
 
129
143
  Show the user the created/modified files and their contents.
130
144
 
@@ -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 an ordered list of foundry cycles that transforms a request into finished artefacts.
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 foundry flow should include, in order. List available cycles from `foundry/cycles/*.md` for reference.
37
+ Ask the user which foundry cycles this flow includes. List available cycles from `foundry/cycles/*.md` for reference.
38
38
 
39
- The user may:
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
- For each selected foundry cycle, verify it exists in `foundry/cycles/`. If it doesn't, note it as pending:
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
- > Foundry cycle `<id>` doesn't exist yet. I'll include it in the foundry flow definition, but you'll need to create it before running the foundry flow.
44
+ ### 4. Validate cycle graph
46
45
 
47
- ### 4. Validate foundry cycle ordering
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
- Check that input dependencies are satisfied:
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
- If a dependency is unmet, warn:
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 foundry flow definition to the user:
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
- 1. <cycle-id>
73
- 2. <cycle-id>
72
+ - <cycle-id>
73
+ - <cycle-id>
74
74
  ```
75
75
 
76
- Ask: does this capture the foundry flow correctly?
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
 
@@ -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
@@ -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, hitl]
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
- - Cycle definitions can include `hitl` entries for human-in-the-loop checkpoints
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
- ## HITL stages
50
+ ## Human Appraise
51
51
 
52
- Cycle definitions can include `hitl` entries in their stages list to pause for human input. When sort routes to a `hitl` stage, the hitl skill presents the configured prompt and records the human's response.
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), `hitl` (from human) — indicates the source and category of feedback.
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
 
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: flow
3
3
  type: composite
4
- description: Orchestrates foundry cycles on a work branch, driven by a foundry flow definition.
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, initialises the work file, and executes each foundry cycle in sequence.
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 foundry flow
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 name `work/<flow-id>-<short-description>` — create the work branch
22
- 3. Call `foundry_workfile_create` with the flow ID, first cycle ID, and goal from the flow definition + human context
23
- 4. Execute each cycle in order by invoking the cycle skill
24
- 5. Between cycles: call `foundry_workfile_set` with `key: "cycle"`, `value: <next-cycle-id>`
25
- 6. When all cycles are done: call `foundry_workfile_delete` — the artefacts and git history are the record
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
- ## Completing a foundry flow
29
+ ## Between cycles
28
30
 
29
- When the flow is complete, the branch contains:
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
- The human decides whether to merge, open a PR, or discard.
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 cycles
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
@@ -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
- ## Feedback tagged `#hitl`
53
+ ## #human feedback
54
54
 
55
- Feedback tagged `hitl` (human-in-the-loop) is treated the same as any other open feedback. Address it or wont-fix it using the same rules as other feedback items.
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
@@ -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
- - `hitl:*` — invoke the hitl skill (no model dispatch — human stage)
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
@@ -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