@really-knows-ai/foundry 2.3.2 → 3.0.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/README.md +180 -369
- package/dist/.opencode/plugins/foundry-tools/appraiser-tools.js +28 -0
- package/dist/.opencode/plugins/foundry-tools/artefact-tools.js +58 -0
- package/dist/.opencode/plugins/foundry-tools/assay-tools.js +92 -0
- package/dist/.opencode/plugins/foundry-tools/attestation-tools.js +191 -0
- package/dist/.opencode/plugins/foundry-tools/config-create-tools.js +128 -0
- package/dist/.opencode/plugins/foundry-tools/config-law-tools.js +380 -0
- package/dist/.opencode/plugins/foundry-tools/config-tools.js +43 -0
- package/dist/.opencode/plugins/foundry-tools/feedback-tools.js +234 -0
- package/dist/.opencode/plugins/foundry-tools/git-helpers.js +354 -0
- package/dist/.opencode/plugins/foundry-tools/git-tools.js +181 -0
- package/dist/.opencode/plugins/foundry-tools/helpers.js +340 -0
- package/dist/.opencode/plugins/foundry-tools/history-tools.js +20 -0
- package/dist/.opencode/plugins/foundry-tools/memory-admin-tools.js +296 -0
- package/dist/.opencode/plugins/foundry-tools/memory-helpers.js +104 -0
- package/dist/.opencode/plugins/foundry-tools/memory-tools.js +286 -0
- package/dist/.opencode/plugins/foundry-tools/orchestrate-tool.js +159 -0
- package/dist/.opencode/plugins/foundry-tools/snapshot-tools.js +104 -0
- package/dist/.opencode/plugins/foundry-tools/stage-tools.js +186 -0
- package/dist/.opencode/plugins/foundry-tools/validate-tools.js +263 -0
- package/dist/.opencode/plugins/foundry-tools/workfile-tools.js +102 -0
- package/dist/.opencode/plugins/foundry.js +105 -0
- package/dist/CHANGELOG.md +533 -0
- package/dist/LICENSE +21 -0
- package/dist/README.md +278 -0
- package/dist/docs/README.md +59 -0
- package/dist/docs/architecture.md +433 -0
- package/dist/docs/concepts.md +395 -0
- package/dist/docs/getting-started.md +344 -0
- package/dist/docs/memory-maintenance.md +176 -0
- package/dist/docs/tools.md +1411 -0
- package/dist/docs/work-spec.md +283 -0
- package/dist/scripts/lib/artefacts.js +151 -0
- package/dist/scripts/lib/assay/loader.js +151 -0
- package/dist/scripts/lib/assay/parse-jsonl.js +102 -0
- package/dist/scripts/lib/assay/permissions.js +52 -0
- package/dist/scripts/lib/assay/run.js +219 -0
- package/dist/scripts/lib/assay/spawn-with-timeout.js +138 -0
- package/dist/scripts/lib/attestation/attest.js +111 -0
- package/dist/scripts/lib/attestation/canonical-json.js +109 -0
- package/dist/scripts/lib/attestation/hash.js +17 -0
- package/dist/scripts/lib/attestation/parse.js +14 -0
- package/dist/scripts/lib/attestation/payload.js +106 -0
- package/dist/scripts/lib/attestation/render.js +16 -0
- package/dist/scripts/lib/attestation/verify.js +15 -0
- package/dist/scripts/lib/branch-guard.js +72 -0
- package/dist/scripts/lib/config-creators/appraiser.js +9 -0
- package/dist/scripts/lib/config-creators/artefact-type.js +9 -0
- package/dist/scripts/lib/config-creators/cycle.js +11 -0
- package/dist/scripts/lib/config-creators/factory.js +49 -0
- package/dist/scripts/lib/config-creators/flow.js +11 -0
- package/dist/scripts/lib/config-validators/appraiser.js +49 -0
- package/dist/scripts/lib/config-validators/artefact-type.js +38 -0
- package/dist/scripts/lib/config-validators/cycle.js +131 -0
- package/dist/scripts/lib/config-validators/flow.js +57 -0
- package/dist/scripts/lib/config-validators/helpers.js +96 -0
- package/dist/scripts/lib/config-validators/law.js +96 -0
- package/dist/scripts/lib/config.js +328 -0
- package/dist/scripts/lib/failed-flow.js +131 -0
- package/dist/scripts/lib/feedback-store.js +249 -0
- package/dist/scripts/lib/feedback-transitions.js +105 -0
- package/dist/scripts/lib/finalize.js +70 -0
- package/dist/scripts/lib/foundational-guards.js +13 -0
- package/dist/scripts/lib/git-bridge.js +77 -0
- package/dist/scripts/lib/git-finish/work-finish.js +233 -0
- package/dist/scripts/lib/git-policy.js +101 -0
- package/dist/scripts/lib/guards.js +125 -0
- package/dist/scripts/lib/history.js +132 -0
- package/dist/scripts/lib/memory/admin/create-edge-type.js +91 -0
- package/dist/scripts/lib/memory/admin/create-entity-type.js +43 -0
- package/dist/scripts/lib/memory/admin/create-extractor.js +67 -0
- package/dist/scripts/lib/memory/admin/drop-edge-type.js +40 -0
- package/dist/scripts/lib/memory/admin/drop-entity-type.js +172 -0
- package/dist/scripts/lib/memory/admin/dump.js +47 -0
- package/dist/scripts/lib/memory/admin/helpers.js +31 -0
- package/dist/scripts/lib/memory/admin/init.js +170 -0
- package/dist/scripts/lib/memory/admin/live-store.js +76 -0
- package/dist/scripts/lib/memory/admin/reembed.js +285 -0
- package/dist/scripts/lib/memory/admin/rename-edge-type.js +54 -0
- package/dist/scripts/lib/memory/admin/rename-entity-type.js +151 -0
- package/dist/scripts/lib/memory/admin/reset.js +24 -0
- package/dist/scripts/lib/memory/admin/vacuum.js +9 -0
- package/dist/scripts/lib/memory/admin/validate.js +19 -0
- package/dist/scripts/lib/memory/config.js +149 -0
- package/dist/scripts/lib/memory/cozo.js +136 -0
- package/dist/scripts/lib/memory/drift.js +71 -0
- package/dist/scripts/lib/memory/embeddings.js +128 -0
- package/dist/scripts/lib/memory/frontmatter.js +75 -0
- package/dist/scripts/lib/memory/ndjson.js +84 -0
- package/dist/scripts/lib/memory/paths.js +25 -0
- package/dist/scripts/lib/memory/permissions.js +41 -0
- package/dist/scripts/lib/memory/prompt.js +109 -0
- package/dist/scripts/lib/memory/query.js +56 -0
- package/dist/scripts/lib/memory/reads.js +109 -0
- package/dist/scripts/lib/memory/schema.js +64 -0
- package/dist/scripts/lib/memory/search.js +73 -0
- package/dist/scripts/lib/memory/singleton.js +49 -0
- package/dist/scripts/lib/memory/store.js +162 -0
- package/dist/scripts/lib/memory/types.js +93 -0
- package/dist/scripts/lib/memory/validate.js +58 -0
- package/dist/scripts/lib/memory/writes.js +40 -0
- package/{scripts → dist/scripts}/lib/pending.js +7 -2
- package/dist/scripts/lib/secret.js +59 -0
- package/{scripts → dist/scripts}/lib/slug.js +3 -2
- package/dist/scripts/lib/snapshot/finish.js +103 -0
- package/dist/scripts/lib/snapshot/inspect.js +253 -0
- package/dist/scripts/lib/snapshot/render.js +55 -0
- package/dist/scripts/lib/sort-fs-check.js +121 -0
- package/dist/scripts/lib/sort-routing.js +101 -0
- package/{scripts → dist/scripts}/lib/stage-guard.js +12 -6
- package/{scripts → dist/scripts}/lib/state.js +4 -0
- package/dist/scripts/lib/token.js +57 -0
- package/dist/scripts/lib/tracing.js +59 -0
- package/dist/scripts/lib/ulid.js +100 -0
- package/dist/scripts/lib/validator-jsonl.js +162 -0
- package/{scripts → dist/scripts}/lib/workfile.js +38 -20
- package/dist/scripts/orchestrate-cycle.js +215 -0
- package/dist/scripts/orchestrate-phases.js +314 -0
- package/dist/scripts/orchestrate.js +163 -0
- package/dist/scripts/sort.js +278 -0
- package/{skills → dist/skills}/add-appraiser/SKILL.md +39 -9
- package/{skills → dist/skills}/add-artefact-type/SKILL.md +62 -40
- package/{skills → dist/skills}/add-cycle/SKILL.md +57 -17
- package/dist/skills/add-extractor/SKILL.md +133 -0
- package/{skills → dist/skills}/add-flow/SKILL.md +36 -10
- package/dist/skills/add-law/SKILL.md +191 -0
- package/dist/skills/add-memory-edge-type/SKILL.md +52 -0
- package/dist/skills/add-memory-entity-type/SKILL.md +74 -0
- package/{skills → dist/skills}/appraise/SKILL.md +62 -13
- package/dist/skills/assay/SKILL.md +72 -0
- package/dist/skills/change-embedding-model/SKILL.md +58 -0
- package/dist/skills/drop-memory-edge-type/SKILL.md +54 -0
- package/dist/skills/drop-memory-entity-type/SKILL.md +57 -0
- package/dist/skills/dry-run/SKILL.md +116 -0
- package/{skills → dist/skills}/flow/SKILL.md +15 -2
- package/dist/skills/forge/SKILL.md +121 -0
- package/dist/skills/human-appraise/SKILL.md +153 -0
- package/{skills → dist/skills}/init-foundry/SKILL.md +23 -4
- package/dist/skills/init-memory/SKILL.md +92 -0
- package/{skills → dist/skills}/orchestrate/SKILL.md +30 -4
- package/dist/skills/quench/SKILL.md +99 -0
- package/{skills → dist/skills}/refresh-agents/SKILL.md +1 -1
- package/dist/skills/rename-memory-edge-type/SKILL.md +50 -0
- package/dist/skills/rename-memory-entity-type/SKILL.md +51 -0
- package/dist/skills/reset-memory/SKILL.md +54 -0
- package/dist/skills/upgrade-foundry/SKILL.md +191 -0
- package/package.json +34 -17
- package/.opencode/plugins/foundry.js +0 -761
- package/CHANGELOG.md +0 -100
- package/docs/concepts.md +0 -122
- package/docs/getting-started.md +0 -187
- package/docs/work-spec.md +0 -207
- package/scripts/lib/artefacts.js +0 -124
- package/scripts/lib/config.js +0 -175
- package/scripts/lib/feedback-transitions.js +0 -25
- package/scripts/lib/feedback.js +0 -440
- package/scripts/lib/finalize.js +0 -41
- package/scripts/lib/history.js +0 -59
- package/scripts/lib/secret.js +0 -23
- package/scripts/lib/tags.js +0 -108
- package/scripts/lib/token.js +0 -26
- package/scripts/orchestrate.js +0 -418
- package/scripts/sort.js +0 -370
- package/scripts/validate-tags.js +0 -54
- package/skills/add-law/SKILL.md +0 -111
- package/skills/forge/SKILL.md +0 -88
- package/skills/human-appraise/SKILL.md +0 -82
- package/skills/quench/SKILL.md +0 -62
- package/skills/upgrade-foundry/SKILL.md +0 -216
- /package/{skills → dist/skills}/list-agents/SKILL.md +0 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sort — deterministic routing for a Foundry Cycle.
|
|
5
|
+
*
|
|
6
|
+
* Reads WORK.md, WORK.feedback.yaml, and WORK.history.yaml to determine
|
|
7
|
+
* the next stage to execute, or signal completion/blocked.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/sort.js [--work WORK.md] [--history WORK.history.yaml]
|
|
11
|
+
*
|
|
12
|
+
* Output (stdout): a full stage alias (e.g., forge:write-haiku), 'done', or 'blocked'
|
|
13
|
+
* Exit code: 0 on success, 1 on error
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { parseFrontmatter } from './lib/workfile.js';
|
|
17
|
+
import { loadHistory } from './lib/history.js';
|
|
18
|
+
import { openFeedbackStore } from './lib/feedback-store.js';
|
|
19
|
+
import { ulid as defaultUlid } from './lib/ulid.js';
|
|
20
|
+
import {
|
|
21
|
+
baseStage,
|
|
22
|
+
findFirst,
|
|
23
|
+
determineRoute,
|
|
24
|
+
} from './lib/sort-routing.js';
|
|
25
|
+
import {
|
|
26
|
+
defaultIO,
|
|
27
|
+
checkModifiedFiles,
|
|
28
|
+
getDirtyToolManagedFiles,
|
|
29
|
+
} from './lib/sort-fs-check.js';
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Top-level deadlock pass (spec §6.1)
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Walk the feedback store and write a `state=deadlocked` snapshot for every
|
|
37
|
+
* non-resolved item whose history depth has reached the configured threshold.
|
|
38
|
+
* One atomic batch write via `store.writeDeadlockedSnapshots(ids, ...)`.
|
|
39
|
+
*
|
|
40
|
+
* Sort is the only writer of `state=deadlocked` per spec §6.1.
|
|
41
|
+
*
|
|
42
|
+
* @returns {boolean} true iff at least one snapshot was written.
|
|
43
|
+
*/
|
|
44
|
+
function runDeadlockPass(store, { threshold, enabled, cycle }) {
|
|
45
|
+
if (!enabled) return false;
|
|
46
|
+
const qualifying = store.list().filter(item => {
|
|
47
|
+
// history[0] is the most recent state per the feedback-store invariant
|
|
48
|
+
// (entries are prepended to keep newest at head).
|
|
49
|
+
const head = item.history[0];
|
|
50
|
+
if (head.state === 'resolved' || head.state === 'deadlocked') return false;
|
|
51
|
+
return item.history.length >= threshold;
|
|
52
|
+
});
|
|
53
|
+
if (qualifying.length === 0) return false;
|
|
54
|
+
store.writeDeadlockedSnapshots(
|
|
55
|
+
qualifying.map(it => it.id),
|
|
56
|
+
`depth >= threshold=${threshold}`,
|
|
57
|
+
'sort',
|
|
58
|
+
cycle,
|
|
59
|
+
);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// runSort — structured result for programmatic use
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
function isDispatchableRoute(route) {
|
|
68
|
+
return typeof route === 'string' && /^(assay|forge|quench|appraise|human-appraise):/.test(route);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function validateStages(stages) {
|
|
72
|
+
if (!stages || !Array.isArray(stages)) return { error: 'No stages in WORK.md frontmatter' };
|
|
73
|
+
if (!findFirst(stages, 'forge')) return { error: 'stages must include at least one forge stage' };
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function validateWorkMd(workPath, io) {
|
|
78
|
+
if (!io.exists(workPath)) return { error: 'WORK.md not found' };
|
|
79
|
+
const workText = io.readFile(workPath);
|
|
80
|
+
const frontmatter = parseFrontmatter(workText);
|
|
81
|
+
if (!frontmatter.cycle) return { error: 'No cycle in WORK.md frontmatter' };
|
|
82
|
+
const stagesError = validateStages(frontmatter.stages);
|
|
83
|
+
if (stagesError) return stagesError;
|
|
84
|
+
return { frontmatter, cycle: frontmatter.cycle, stages: frontmatter.stages };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function extractFrontmatterDefaults(frontmatter) {
|
|
88
|
+
return {
|
|
89
|
+
maxIterations: frontmatter['max-iterations'] ?? 3,
|
|
90
|
+
humanAppraiseEnabled: frontmatter['human-appraise'] === true,
|
|
91
|
+
deadlockAppraise: frontmatter['deadlock-appraise'] !== false,
|
|
92
|
+
deadlockIterations: frontmatter['deadlock-iterations'] ?? 5,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function checkDirtyFiles(history, io) {
|
|
97
|
+
if (history.length === 0) return null;
|
|
98
|
+
const dirty = getDirtyToolManagedFiles(io);
|
|
99
|
+
if (dirty.length === 0) return null;
|
|
100
|
+
return `Uncommitted tool-managed files since last sort: ${dirty.join(', ')}. `
|
|
101
|
+
+ `Each stage's commit is performed internally by foundry_orchestrate; `
|
|
102
|
+
+ `if you see this, the prior stage's commit was skipped or aborted. `
|
|
103
|
+
+ `Re-run foundry_orchestrate or commit the listed files manually before retrying.`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function loadFeedbackAndRunDeadlock(cycle, deadlockIterations, deadlockAppraise, io) {
|
|
107
|
+
const store = openFeedbackStore('WORK.feedback.yaml', io);
|
|
108
|
+
runDeadlockPass(store, { threshold: deadlockIterations, enabled: deadlockAppraise, cycle });
|
|
109
|
+
const feedback = store.list().map(item => ({
|
|
110
|
+
id: item.id,
|
|
111
|
+
file: item.file,
|
|
112
|
+
state: item.history[0].state,
|
|
113
|
+
depth: item.history.length,
|
|
114
|
+
}));
|
|
115
|
+
const anyDeadlocked = feedback.some(f => f.state === 'deadlocked');
|
|
116
|
+
return { feedback, anyDeadlocked };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resolveCycleDef(cycleDef, frontmatter, foundryDir, cycle) {
|
|
120
|
+
return cycleDef || frontmatter['cycle-def'] || `${foundryDir}/cycles/${cycle}.md`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function checkModifiedFilesAfterLastStage({ history, foundryDir, cycleDef, cycle, frontmatter, io }) {
|
|
124
|
+
const nonSortHistory = history.filter(e => baseStage(e.stage || '') !== 'sort');
|
|
125
|
+
if (nonSortHistory.length === 0) return { nonSortHistory };
|
|
126
|
+
const lastBase = baseStage(nonSortHistory[nonSortHistory.length - 1].stage || '');
|
|
127
|
+
const resolvedCycleDef = resolveCycleDef(cycleDef, frontmatter, foundryDir, cycle);
|
|
128
|
+
const result = checkModifiedFiles(lastBase, foundryDir, resolvedCycleDef, cycle, io);
|
|
129
|
+
if (!result.ok) {
|
|
130
|
+
return { error: `File modification violation after ${lastBase} stage: ${result.violations.join(', ')}` };
|
|
131
|
+
}
|
|
132
|
+
return { nonSortHistory };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getCurrentNonSortStage(nonSortHistory) {
|
|
136
|
+
return nonSortHistory.length > 0 ? nonSortHistory[nonSortHistory.length - 1].stage : null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function resolveDeadlockRoute(stages, nonSortHistory, cycle) {
|
|
140
|
+
const currentNonSort = getCurrentNonSortStage(nonSortHistory);
|
|
141
|
+
if (currentNonSort && baseStage(currentNonSort) === 'human-appraise') return 'blocked';
|
|
142
|
+
return findFirst(stages, 'human-appraise') || `human-appraise:${cycle}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function resolveRoute(ctx) {
|
|
146
|
+
if (ctx.anyDeadlocked) return resolveDeadlockRoute(ctx.stages, ctx.nonSortHistory, ctx.cycle);
|
|
147
|
+
return determineRoute(ctx.stages, ctx.history, ctx.feedback, ctx.maxIterations);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function resolveModel(route, frontmatter, agentsDir, io) {
|
|
151
|
+
const routeBase = baseStage(route);
|
|
152
|
+
if (!frontmatter.models || !frontmatter.models[routeBase]) return null;
|
|
153
|
+
const modelId = frontmatter.models[routeBase];
|
|
154
|
+
const model = `foundry-${modelId.replace(/[/.]/g, '-')}`;
|
|
155
|
+
const agentPath = `${agentsDir}/${model}.md`;
|
|
156
|
+
if (!io.exists(agentPath)) {
|
|
157
|
+
return {
|
|
158
|
+
error: `Missing required subagent: ${model}.md is not present in ${agentsDir}/. `
|
|
159
|
+
+ `Run the refresh-agents skill to regenerate agent files, then restart.`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return model;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function checkModel(route, frontmatter, agentsDir, io) {
|
|
166
|
+
const modelResult = resolveModel(route, frontmatter, agentsDir, io);
|
|
167
|
+
if (modelResult && modelResult.error) return { error: modelResult.error };
|
|
168
|
+
return { model: typeof modelResult === 'string' ? modelResult : null };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function mintToken({ route, model, mint, cycle, now, ulid }) {
|
|
172
|
+
const result = { route, ...(model ? { model } : {}) };
|
|
173
|
+
if (mint && isDispatchableRoute(route)) {
|
|
174
|
+
const token = mint({ route, cycle, exp: now + 10 * 60 * 1000, nonce: ulid(now) });
|
|
175
|
+
if (token) result.token = token;
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// runSort is decomposed into single-purpose phase helpers above so the
|
|
181
|
+
// orchestrating function itself stays within the configured complexity
|
|
182
|
+
// limit. Each phase either returns an error envelope (handled by
|
|
183
|
+
// firstError) or contributes data to the routing decision.
|
|
184
|
+
|
|
185
|
+
function firstError(...envelopes) {
|
|
186
|
+
for (const env of envelopes) {
|
|
187
|
+
if (env && env.error) return env.error;
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function preparePhases({ workPath, historyPath, foundryDir, cycleDef, io }) {
|
|
193
|
+
const validation = validateWorkMd(workPath, io);
|
|
194
|
+
if (validation.error) return { kind: 'blocked', details: validation.error };
|
|
195
|
+
const { frontmatter, cycle, stages } = validation;
|
|
196
|
+
const defaults = extractFrontmatterDefaults(frontmatter);
|
|
197
|
+
const history = loadHistory(historyPath, cycle, io);
|
|
198
|
+
const dirtyError = checkDirtyFiles(history, io);
|
|
199
|
+
if (dirtyError) return { kind: 'violation', details: dirtyError };
|
|
200
|
+
const { feedback, anyDeadlocked } = loadFeedbackAndRunDeadlock(
|
|
201
|
+
cycle, defaults.deadlockIterations, defaults.deadlockAppraise, io,
|
|
202
|
+
);
|
|
203
|
+
const fileCheck = checkModifiedFilesAfterLastStage({
|
|
204
|
+
history, foundryDir, cycleDef, cycle, frontmatter, io,
|
|
205
|
+
});
|
|
206
|
+
const violation = firstError(fileCheck);
|
|
207
|
+
if (violation) return { kind: 'violation', details: violation };
|
|
208
|
+
return {
|
|
209
|
+
kind: 'ok',
|
|
210
|
+
frontmatter, cycle, stages, defaults, history, feedback, anyDeadlocked,
|
|
211
|
+
nonSortHistory: fileCheck.nonSortHistory,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const RUN_SORT_DEFAULTS = Object.freeze({
|
|
216
|
+
workPath: 'WORK.md',
|
|
217
|
+
historyPath: 'WORK.history.yaml',
|
|
218
|
+
foundryDir: 'foundry',
|
|
219
|
+
cycleDef: undefined,
|
|
220
|
+
agentsDir: '.opencode/agents',
|
|
221
|
+
mint: undefined,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
function withRunSortDefaults(args) {
|
|
225
|
+
const merged = { ...RUN_SORT_DEFAULTS, ...args };
|
|
226
|
+
if (merged.now === undefined) merged.now = Date.now();
|
|
227
|
+
if (merged.ulid === undefined) merged.ulid = defaultUlid;
|
|
228
|
+
return merged;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function buildRouteCtx(prep) {
|
|
232
|
+
return {
|
|
233
|
+
stages: prep.stages,
|
|
234
|
+
history: prep.history,
|
|
235
|
+
feedback: prep.feedback,
|
|
236
|
+
maxIterations: prep.defaults.maxIterations,
|
|
237
|
+
cycle: prep.cycle,
|
|
238
|
+
anyDeadlocked: prep.anyDeadlocked,
|
|
239
|
+
nonSortHistory: prep.nonSortHistory,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function runSort(args = {}, io = defaultIO) {
|
|
244
|
+
const opts = withRunSortDefaults(args);
|
|
245
|
+
const prep = preparePhases({ ...opts, io });
|
|
246
|
+
if (prep.kind !== 'ok') return { route: prep.kind, details: prep.details };
|
|
247
|
+
|
|
248
|
+
const route = resolveRoute(buildRouteCtx(prep));
|
|
249
|
+
const modelCheck = checkModel(route, prep.frontmatter, opts.agentsDir, io);
|
|
250
|
+
if (modelCheck.error) return { route: 'violation', details: modelCheck.error };
|
|
251
|
+
|
|
252
|
+
return mintToken({
|
|
253
|
+
route, model: modelCheck.model, mint: opts.mint, cycle: prep.cycle, now: opts.now, ulid: opts.ulid,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
// Exports (for testing) — keep main() private
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
|
|
261
|
+
export { parseArtefactsTable } from './lib/artefacts.js';
|
|
262
|
+
export { loadHistory } from './lib/history.js';
|
|
263
|
+
export { parseFrontmatter } from './lib/workfile.js';
|
|
264
|
+
export {
|
|
265
|
+
baseStage,
|
|
266
|
+
findFirst,
|
|
267
|
+
nextInRoute,
|
|
268
|
+
determineRoute,
|
|
269
|
+
nextAfterQuench,
|
|
270
|
+
nextAfterAppraise,
|
|
271
|
+
} from './lib/sort-routing.js';
|
|
272
|
+
export {
|
|
273
|
+
globMatch,
|
|
274
|
+
getModifiedFiles,
|
|
275
|
+
getAllowedPatterns,
|
|
276
|
+
checkModifiedFiles,
|
|
277
|
+
getDirtyToolManagedFiles,
|
|
278
|
+
} from './lib/sort-fs-check.js';
|
|
@@ -10,15 +10,32 @@ You help the user create a new appraiser personality. You ensure it's genuinely
|
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
13
|
-
Before running this skill, verify
|
|
13
|
+
Before running this skill, verify all three of the following:
|
|
14
14
|
|
|
15
|
-
1. The `foundry/` directory exists in the project root. If it does not
|
|
15
|
+
1. The `foundry/` directory exists in the project root. If it does not
|
|
16
|
+
exist, stop and tell the user:
|
|
16
17
|
|
|
17
|
-
> Foundry is not initialized in this project. Run the
|
|
18
|
+
> Foundry is not initialized in this project. Run the
|
|
19
|
+
> `init-foundry` skill first to create the foundry/ directory
|
|
20
|
+
> structure.
|
|
18
21
|
|
|
19
|
-
2. The current git branch is
|
|
22
|
+
2. The current git branch is a `config/*` branch. Run
|
|
23
|
+
`git rev-parse --abbrev-ref HEAD` and confirm it matches
|
|
24
|
+
`config/<description>`.
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
3. If the branch does not start with `config/`, instruct the user to
|
|
27
|
+
create one before continuing:
|
|
28
|
+
|
|
29
|
+
> Foundry configuration changes must be made on a config/* branch.
|
|
30
|
+
> From a clean main branch, call:
|
|
31
|
+
>
|
|
32
|
+
> `foundry_git_branch({ kind: "config", description: "<short-name>" })`
|
|
33
|
+
>
|
|
34
|
+
> Then re-run this skill.
|
|
35
|
+
|
|
36
|
+
If the user is on a `dry-run/*/*` branch, they must finish
|
|
37
|
+
that dry-run first (`foundry_git_finish({ message, confirm: true })`)
|
|
38
|
+
before re-running this skill on the parent `config/*`.
|
|
22
39
|
|
|
23
40
|
## Protocol
|
|
24
41
|
|
|
@@ -87,11 +104,25 @@ Iterate until the user is happy with the personality description. Key things to
|
|
|
87
104
|
- Does the description give the LLM enough direction to adopt a consistent voice?
|
|
88
105
|
- Is it clear what this appraiser would flag vs let pass?
|
|
89
106
|
|
|
90
|
-
### 6.
|
|
107
|
+
### 6. Validate the draft
|
|
108
|
+
|
|
109
|
+
Call `foundry_config_validate_appraiser({ name: "<id>", body: "<full markdown>" })`.
|
|
110
|
+
|
|
111
|
+
If the result is `{ ok: false, errors: [...] }`, address each error (adjust the body) and re-run until you get `{ ok: true }`. Common issues: missing required frontmatter keys, references to artefact types or flows that don't exist yet.
|
|
112
|
+
|
|
113
|
+
### 7. Create the file
|
|
114
|
+
|
|
115
|
+
Call `foundry_config_create_appraiser({ name: "<id>", body: "<full markdown>" })`. The tool:
|
|
116
|
+
|
|
117
|
+
- re-validates the body (TOCTOU);
|
|
118
|
+
- writes `foundry/appraisers/<id>.md`;
|
|
119
|
+
- produces one git commit on the current `config/*` branch.
|
|
120
|
+
|
|
121
|
+
If the tool returns `{ ok: false, errors }` because the target file already exists, the user should edit the file by hand on this `config/*` branch — `foundry_config_create_appraiser` does not support updates.
|
|
91
122
|
|
|
92
|
-
|
|
123
|
+
Show the user the resulting commit hash from the response.
|
|
93
124
|
|
|
94
|
-
###
|
|
125
|
+
### 8. Mention artefact type configuration
|
|
95
126
|
|
|
96
127
|
After creating the appraiser, remind the user:
|
|
97
128
|
|
|
@@ -107,7 +138,6 @@ After creating the appraiser, remind the user:
|
|
|
107
138
|
|
|
108
139
|
## What you do NOT do
|
|
109
140
|
|
|
110
|
-
- You do not write files without showing the user first
|
|
111
141
|
- You do not skip the semantic overlap check
|
|
112
142
|
- You do not modify artefact type definitions — that is the user's choice
|
|
113
143
|
- You do not create appraisers with duplicate ids
|
|
@@ -6,19 +6,36 @@ description: Creates a new artefact type, checking for conflicts with existing t
|
|
|
6
6
|
|
|
7
7
|
# Add Artefact Type
|
|
8
8
|
|
|
9
|
-
You help the user create a new artefact type. You ensure it
|
|
9
|
+
You help the user create a new artefact type. You ensure it avoids conflicts with existing types, scaffold the directory structure, and walk the user through defining laws and their optional validators.
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
13
|
-
Before running this skill, verify
|
|
13
|
+
Before running this skill, verify all three of the following:
|
|
14
14
|
|
|
15
|
-
1. The `foundry/` directory exists in the project root. If it does not
|
|
15
|
+
1. The `foundry/` directory exists in the project root. If it does not
|
|
16
|
+
exist, stop and tell the user:
|
|
16
17
|
|
|
17
|
-
> Foundry is not initialized in this project. Run the
|
|
18
|
+
> Foundry is not initialized in this project. Run the
|
|
19
|
+
> `init-foundry` skill first to create the foundry/ directory
|
|
20
|
+
> structure.
|
|
18
21
|
|
|
19
|
-
2. The current git branch is
|
|
22
|
+
2. The current git branch is a `config/*` branch. Run
|
|
23
|
+
`git rev-parse --abbrev-ref HEAD` and confirm it matches
|
|
24
|
+
`config/<description>`.
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
3. If the branch does not start with `config/`, instruct the user to
|
|
27
|
+
create one before continuing:
|
|
28
|
+
|
|
29
|
+
> Foundry configuration changes must be made on a config/* branch.
|
|
30
|
+
> From a clean main branch, call:
|
|
31
|
+
>
|
|
32
|
+
> `foundry_git_branch({ kind: "config", description: "<short-name>" })`
|
|
33
|
+
>
|
|
34
|
+
> Then re-run this skill.
|
|
35
|
+
|
|
36
|
+
If the user is on a `dry-run/*/*` branch, they must finish
|
|
37
|
+
that dry-run first (`foundry_git_finish({ message, confirm: true })`)
|
|
38
|
+
before re-running this skill on the parent `config/*`.
|
|
22
39
|
|
|
23
40
|
## Protocol
|
|
24
41
|
|
|
@@ -27,8 +44,7 @@ Before running this skill, verify both of the following:
|
|
|
27
44
|
From the user's prompt, establish:
|
|
28
45
|
- `id` — lowercase, hyphenated identifier
|
|
29
46
|
- `name` — human-readable name
|
|
30
|
-
- `file-patterns` — glob patterns for files this type produces
|
|
31
|
-
- `output` — output directory
|
|
47
|
+
- `file-patterns` — glob patterns for files this type produces (forge's write scope is exactly these patterns)
|
|
32
48
|
- A prose description of what this artefact type is
|
|
33
49
|
|
|
34
50
|
If any of these are missing, ask.
|
|
@@ -71,16 +87,12 @@ Present the definition to the user:
|
|
|
71
87
|
|
|
72
88
|
```markdown
|
|
73
89
|
---
|
|
74
|
-
id: <id>
|
|
75
90
|
name: <name>
|
|
76
91
|
file-patterns:
|
|
77
92
|
- "<pattern>"
|
|
78
|
-
output: <output-dir>
|
|
79
|
-
appraisers:
|
|
80
|
-
count: 3
|
|
81
93
|
---
|
|
82
94
|
|
|
83
|
-
|
|
95
|
+
## Definition
|
|
84
96
|
|
|
85
97
|
<description>
|
|
86
98
|
```
|
|
@@ -94,10 +106,32 @@ Ask:
|
|
|
94
106
|
> Do you want to define any type-specific laws for this artefact type? (Global laws in `foundry/laws/` will apply automatically.)
|
|
95
107
|
|
|
96
108
|
If yes, walk through each law using the same format as `add-law`:
|
|
97
|
-
- Draft each law
|
|
109
|
+
- Draft each law, adding validators where a deterministic check applies
|
|
98
110
|
- Check for conflicts with global laws and any existing type-specific laws
|
|
99
111
|
- Confirm with the user
|
|
100
112
|
|
|
113
|
+
Each law may declare an optional `validators:` block. Include validators only when a deterministic check is needed. The format matches `add-law`:
|
|
114
|
+
|
|
115
|
+
```markdown
|
|
116
|
+
## <law-id>
|
|
117
|
+
|
|
118
|
+
<What this law checks — one or two sentences.>
|
|
119
|
+
|
|
120
|
+
validators:
|
|
121
|
+
- id: validator-id
|
|
122
|
+
command: ./script.sh
|
|
123
|
+
failure-means: (optional description)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The `validators` block is optional. When present, `quench` runs each validator for this law. Validator scripts live within the artefact type directory (e.g., `foundry/artefacts/<type>/check-foo.mjs`).
|
|
127
|
+
|
|
128
|
+
**Use existing libraries:** Before writing custom validation logic, search npm for well-tested libraries that solve the problem (e.g., `syllable` for syllable counting, `natural` for NLP tasks). Hand-rolled heuristics are fragile — prefer battle-tested packages. Install them as project dependencies.
|
|
129
|
+
|
|
130
|
+
Check the project's `package.json` for `"type": "module"`:
|
|
131
|
+
- If ESM (`"type": "module"`): use `import` syntax, or name scripts with `.mjs` extension
|
|
132
|
+
- If CommonJS (no `"type"` field or `"type": "commonjs"`): `require()` is fine, or use `.cjs` extension
|
|
133
|
+
- When in doubt, use `.mjs` or `.cjs` extensions to be explicit regardless of project settings
|
|
134
|
+
|
|
101
135
|
### 6. Appraisers (optional)
|
|
102
136
|
|
|
103
137
|
Ask:
|
|
@@ -123,46 +157,34 @@ appraisers:
|
|
|
123
157
|
|
|
124
158
|
List the available appraisers from `foundry/appraisers/*.md` so the user can see their options.
|
|
125
159
|
|
|
126
|
-
### 7.
|
|
160
|
+
### 7. Validate the draft
|
|
127
161
|
|
|
128
|
-
|
|
162
|
+
Call `foundry_config_validate_artefact_type({ name: "<id>", body: "<full markdown>" })`.
|
|
129
163
|
|
|
130
|
-
|
|
164
|
+
If the result is `{ ok: false, errors: [...] }`, address each error (adjust the body) and re-run until you get `{ ok: true }`. Common issues: missing required frontmatter keys, references to artefact types or flows that don't exist yet.
|
|
131
165
|
|
|
132
|
-
|
|
133
|
-
- A `## heading` (identifier)
|
|
134
|
-
- A `Command:` line with `{file}` placeholder
|
|
135
|
-
- A `Failure means:` line explaining what a non-zero exit indicates
|
|
166
|
+
### 8. Create the file
|
|
136
167
|
|
|
137
|
-
|
|
168
|
+
Call `foundry_config_create_artefact_type({ name: "<id>", body: "<full markdown>" })`. The tool:
|
|
138
169
|
|
|
139
|
-
|
|
170
|
+
- re-validates the body (TOCTOU);
|
|
171
|
+
- writes `foundry/artefacts/<id>/definition.md`;
|
|
172
|
+
- produces one git commit on the current `config/*` branch.
|
|
140
173
|
|
|
141
|
-
|
|
142
|
-
- If ESM (`"type": "module"`): use `import` syntax, or name scripts with `.mjs` extension
|
|
143
|
-
- If CommonJS (no `"type"` field or `"type": "commonjs"`): `require()` is fine, or use `.cjs` extension
|
|
144
|
-
- When in doubt, use `.mjs` or `.cjs` extensions to be explicit regardless of project settings
|
|
174
|
+
If the tool returns `{ ok: false, errors }` because the target file already exists, the user should edit the file by hand on this `config/*` branch — `foundry_config_create_artefact_type` does not support updates.
|
|
145
175
|
|
|
146
|
-
|
|
176
|
+
Show the user the resulting commit hash from the response.
|
|
147
177
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
```
|
|
151
|
-
foundry/artefacts/<id>/
|
|
152
|
-
definition.md # always created
|
|
153
|
-
laws.md # created if laws were defined
|
|
154
|
-
validation.md # created if validation commands were defined
|
|
155
|
-
```
|
|
178
|
+
### 9. Add laws file (if defined)
|
|
156
179
|
|
|
157
|
-
If laws
|
|
180
|
+
The create tool writes only `definition.md`. If you drafted any type-specific laws in step 5, append them to `foundry/artefacts/<id>/laws.md` by hand on this same `config/*` branch (use the `Edit` tool to create the file) and commit that as a separate microcommit.
|
|
158
181
|
|
|
159
|
-
###
|
|
182
|
+
### 10. Confirm
|
|
160
183
|
|
|
161
|
-
Show the user the complete file listing and
|
|
184
|
+
Show the user the complete file listing and the commit hashes.
|
|
162
185
|
|
|
163
186
|
## What you do NOT do
|
|
164
187
|
|
|
165
188
|
- You do not create artefact types with overlapping file patterns — this is a hard block
|
|
166
|
-
- You do not write files without showing the user first
|
|
167
189
|
- You do not skip the naming or glob checks
|
|
168
190
|
- You do not create laws without checking for conflicts (delegate to add-law pattern)
|
|
@@ -10,15 +10,32 @@ You help the user create a new foundry cycle and add it to an existing foundry f
|
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
13
|
-
Before running this skill, verify
|
|
13
|
+
Before running this skill, verify all three of the following:
|
|
14
14
|
|
|
15
|
-
1. The `foundry/` directory exists in the project root. If it does not
|
|
15
|
+
1. The `foundry/` directory exists in the project root. If it does not
|
|
16
|
+
exist, stop and tell the user:
|
|
16
17
|
|
|
17
|
-
> Foundry is not initialized in this project. Run the
|
|
18
|
+
> Foundry is not initialized in this project. Run the
|
|
19
|
+
> `init-foundry` skill first to create the foundry/ directory
|
|
20
|
+
> structure.
|
|
18
21
|
|
|
19
|
-
2. The current git branch is
|
|
22
|
+
2. The current git branch is a `config/*` branch. Run
|
|
23
|
+
`git rev-parse --abbrev-ref HEAD` and confirm it matches
|
|
24
|
+
`config/<description>`.
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
3. If the branch does not start with `config/`, instruct the user to
|
|
27
|
+
create one before continuing:
|
|
28
|
+
|
|
29
|
+
> Foundry configuration changes must be made on a config/* branch.
|
|
30
|
+
> From a clean main branch, call:
|
|
31
|
+
>
|
|
32
|
+
> `foundry_git_branch({ kind: "config", description: "<short-name>" })`
|
|
33
|
+
>
|
|
34
|
+
> Then re-run this skill.
|
|
35
|
+
|
|
36
|
+
If the user is on a `dry-run/*/*` branch, they must finish
|
|
37
|
+
that dry-run first (`foundry_git_finish({ message, confirm: true })`)
|
|
38
|
+
before re-running this skill on the parent `config/*`.
|
|
22
39
|
|
|
23
40
|
## Protocol
|
|
24
41
|
|
|
@@ -33,7 +50,7 @@ Verify the flow exists. If it doesn't, tell the user and ask if they want to cre
|
|
|
33
50
|
From the user's prompt, establish:
|
|
34
51
|
- `id` — lowercase, hyphenated identifier for the foundry cycle
|
|
35
52
|
- `name` — human-readable name
|
|
36
|
-
- `output` — the artefact type this foundry cycle produces (must exist in `foundry/artefacts/`)
|
|
53
|
+
- `output-type` — the artefact type this foundry cycle produces (must exist in `foundry/artefacts/`)
|
|
37
54
|
- `inputs` — artefact types this cycle reads, with a contract type:
|
|
38
55
|
- `type`: `any-of` (at least one must exist) or `all-of` (all must exist)
|
|
39
56
|
- `artefacts`: list of artefact type IDs
|
|
@@ -72,7 +89,7 @@ Ask the user:
|
|
|
72
89
|
|
|
73
90
|
### 5. Validate artefact types
|
|
74
91
|
|
|
75
|
-
For `output` and each entry in `inputs`:
|
|
92
|
+
For `output-type` and each entry in `inputs`:
|
|
76
93
|
- Verify the artefact type exists in `foundry/artefacts/<type>/definition.md`
|
|
77
94
|
- If it doesn't, tell the user and ask if they want to create it first (separate skill)
|
|
78
95
|
|
|
@@ -81,12 +98,12 @@ For `output` and each entry in `inputs`:
|
|
|
81
98
|
Read the flow definition from `foundry/flows/<flow-id>.md`. Check:
|
|
82
99
|
|
|
83
100
|
- No existing foundry cycle in the foundry flow already outputs the same artefact type. Two foundry cycles producing the same type in one foundry flow is a conflict — the file modification enforcement can't distinguish which foundry cycle owns the files.
|
|
84
|
-
- Each `input` artefact type is produced by
|
|
101
|
+
- Each `input` artefact type is produced by some cycle that can run before this one according to the flow's `targets` graph (a reachable predecessor). If an input references an artefact type that no reachable predecessor outputs, warn:
|
|
85
102
|
|
|
86
|
-
> Input `<type>` is not produced by any
|
|
103
|
+
> Input `<type>` is not produced by any reachable predecessor of this foundry cycle in the flow's `targets` graph. The artefact won't exist when this foundry cycle runs.
|
|
87
104
|
>
|
|
88
105
|
> Options:
|
|
89
|
-
> 1. Add a foundry cycle that produces `<type>`
|
|
106
|
+
> 1. Add a foundry cycle that produces `<type>` and route to this cycle via `targets`
|
|
90
107
|
> 2. Remove `<type>` from inputs (this foundry cycle won't have that context)
|
|
91
108
|
> 3. Proceed anyway (the artefact may exist from a previous foundry flow run)
|
|
92
109
|
|
|
@@ -112,7 +129,7 @@ Present the foundry cycle definition to the user:
|
|
|
112
129
|
---
|
|
113
130
|
id: <id>
|
|
114
131
|
name: <name>
|
|
115
|
-
output: <artefact-type-id>
|
|
132
|
+
output-type: <artefact-type-id>
|
|
116
133
|
inputs:
|
|
117
134
|
type: <any-of|all-of>
|
|
118
135
|
artefacts:
|
|
@@ -144,19 +161,42 @@ For input validation:
|
|
|
144
161
|
- Verify that at least one cycle in the flow has the input artefact type(s) as its output
|
|
145
162
|
- If using `all-of`, verify all input types are producible
|
|
146
163
|
|
|
147
|
-
### 11.
|
|
164
|
+
### 11. Validate the draft
|
|
165
|
+
|
|
166
|
+
Call `foundry_config_validate_cycle({ name: "<id>", body: "<full markdown>" })`.
|
|
167
|
+
|
|
168
|
+
If the result is `{ ok: false, errors: [...] }`, address each error (adjust the body) and re-run until you get `{ ok: true }`. Common issues: missing required frontmatter keys, references to artefact types or flows that don't exist yet.
|
|
169
|
+
|
|
170
|
+
### 12. Create the cycle file
|
|
171
|
+
|
|
172
|
+
Call `foundry_config_create_cycle({ name: "<id>", body: "<full markdown>" })`. The tool:
|
|
173
|
+
|
|
174
|
+
- re-validates the body (TOCTOU);
|
|
175
|
+
- writes `foundry/cycles/<id>.md`;
|
|
176
|
+
- produces one git commit on the current `config/*` branch.
|
|
148
177
|
|
|
149
|
-
|
|
150
|
-
|
|
178
|
+
If the tool returns `{ ok: false, errors }` because the target file already exists, the user should edit the file by hand on this `config/*` branch — `foundry_config_create_cycle` does not support updates.
|
|
179
|
+
|
|
180
|
+
Show the user the resulting commit hash from the response.
|
|
181
|
+
|
|
182
|
+
### 13. Add the cycle to the flow's cycle list
|
|
183
|
+
|
|
184
|
+
`foundry_config_create_cycle` writes the cycle file only. The cycle still needs to appear in the parent flow's `## Cycles` list.
|
|
185
|
+
|
|
186
|
+
Edit `foundry/flows/<flow-id>.md` by hand on this same `config/*` branch using the `Edit` tool. Add the new cycle id under `## Cycles` (if not already present). Commit that edit by hand as a separate microcommit, e.g.:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
git add foundry/flows/<flow-id>.md
|
|
190
|
+
git commit -m "config(flow): add <cycle-id> to <flow-id>"
|
|
191
|
+
```
|
|
151
192
|
|
|
152
|
-
###
|
|
193
|
+
### 14. Confirm
|
|
153
194
|
|
|
154
|
-
Show the user the
|
|
195
|
+
Show the user the cycle file, the updated flow file, and both commit hashes.
|
|
155
196
|
|
|
156
197
|
## What you do NOT do
|
|
157
198
|
|
|
158
199
|
- You do not create foundry cycles that output an artefact type already produced by another foundry cycle in the same foundry flow
|
|
159
|
-
- You do not write files without showing the user first
|
|
160
200
|
- You do not skip artefact type validation
|
|
161
201
|
- You do not create artefact types — that is a separate skill
|
|
162
202
|
- You do not create foundry flows — that is a separate skill
|