@really-knows-ai/foundry 2.3.2 → 3.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.
Files changed (170) hide show
  1. package/README.md +180 -369
  2. package/dist/.opencode/plugins/foundry-tools/appraiser-tools.js +28 -0
  3. package/dist/.opencode/plugins/foundry-tools/artefact-tools.js +58 -0
  4. package/dist/.opencode/plugins/foundry-tools/assay-tools.js +92 -0
  5. package/dist/.opencode/plugins/foundry-tools/attestation-tools.js +191 -0
  6. package/dist/.opencode/plugins/foundry-tools/config-create-tools.js +128 -0
  7. package/dist/.opencode/plugins/foundry-tools/config-law-tools.js +380 -0
  8. package/dist/.opencode/plugins/foundry-tools/config-tools.js +43 -0
  9. package/dist/.opencode/plugins/foundry-tools/feedback-tools.js +234 -0
  10. package/dist/.opencode/plugins/foundry-tools/git-helpers.js +354 -0
  11. package/dist/.opencode/plugins/foundry-tools/git-tools.js +181 -0
  12. package/dist/.opencode/plugins/foundry-tools/helpers.js +340 -0
  13. package/dist/.opencode/plugins/foundry-tools/history-tools.js +20 -0
  14. package/dist/.opencode/plugins/foundry-tools/memory-admin-tools.js +296 -0
  15. package/dist/.opencode/plugins/foundry-tools/memory-helpers.js +104 -0
  16. package/dist/.opencode/plugins/foundry-tools/memory-tools.js +286 -0
  17. package/dist/.opencode/plugins/foundry-tools/orchestrate-tool.js +159 -0
  18. package/dist/.opencode/plugins/foundry-tools/snapshot-tools.js +104 -0
  19. package/dist/.opencode/plugins/foundry-tools/stage-tools.js +186 -0
  20. package/dist/.opencode/plugins/foundry-tools/validate-tools.js +263 -0
  21. package/dist/.opencode/plugins/foundry-tools/workfile-tools.js +102 -0
  22. package/dist/.opencode/plugins/foundry.js +105 -0
  23. package/dist/CHANGELOG.md +490 -0
  24. package/dist/LICENSE +21 -0
  25. package/dist/README.md +278 -0
  26. package/dist/docs/README.md +59 -0
  27. package/dist/docs/architecture.md +434 -0
  28. package/dist/docs/concepts.md +396 -0
  29. package/dist/docs/getting-started.md +345 -0
  30. package/dist/docs/memory-maintenance.md +176 -0
  31. package/dist/docs/tools.md +1411 -0
  32. package/dist/docs/work-spec.md +283 -0
  33. package/dist/scripts/lib/artefacts.js +151 -0
  34. package/dist/scripts/lib/assay/loader.js +151 -0
  35. package/dist/scripts/lib/assay/parse-jsonl.js +102 -0
  36. package/dist/scripts/lib/assay/permissions.js +52 -0
  37. package/dist/scripts/lib/assay/run.js +219 -0
  38. package/dist/scripts/lib/assay/spawn-with-timeout.js +138 -0
  39. package/dist/scripts/lib/attestation/attest.js +111 -0
  40. package/dist/scripts/lib/attestation/canonical-json.js +109 -0
  41. package/dist/scripts/lib/attestation/hash.js +17 -0
  42. package/dist/scripts/lib/attestation/parse.js +14 -0
  43. package/dist/scripts/lib/attestation/payload.js +106 -0
  44. package/dist/scripts/lib/attestation/render.js +16 -0
  45. package/dist/scripts/lib/attestation/verify.js +15 -0
  46. package/dist/scripts/lib/branch-guard.js +72 -0
  47. package/dist/scripts/lib/config-creators/appraiser.js +9 -0
  48. package/dist/scripts/lib/config-creators/artefact-type.js +9 -0
  49. package/dist/scripts/lib/config-creators/cycle.js +11 -0
  50. package/dist/scripts/lib/config-creators/factory.js +49 -0
  51. package/dist/scripts/lib/config-creators/flow.js +11 -0
  52. package/dist/scripts/lib/config-validators/appraiser.js +49 -0
  53. package/dist/scripts/lib/config-validators/artefact-type.js +38 -0
  54. package/dist/scripts/lib/config-validators/cycle.js +131 -0
  55. package/dist/scripts/lib/config-validators/flow.js +57 -0
  56. package/dist/scripts/lib/config-validators/helpers.js +96 -0
  57. package/dist/scripts/lib/config-validators/law.js +96 -0
  58. package/dist/scripts/lib/config.js +393 -0
  59. package/dist/scripts/lib/failed-flow.js +131 -0
  60. package/dist/scripts/lib/feedback-store.js +249 -0
  61. package/dist/scripts/lib/feedback-transitions.js +105 -0
  62. package/dist/scripts/lib/finalize.js +70 -0
  63. package/dist/scripts/lib/foundational-guards.js +13 -0
  64. package/dist/scripts/lib/git-bridge.js +77 -0
  65. package/dist/scripts/lib/git-finish/work-finish.js +233 -0
  66. package/dist/scripts/lib/git-policy.js +101 -0
  67. package/dist/scripts/lib/guards.js +125 -0
  68. package/dist/scripts/lib/history.js +132 -0
  69. package/dist/scripts/lib/memory/admin/create-edge-type.js +91 -0
  70. package/dist/scripts/lib/memory/admin/create-entity-type.js +43 -0
  71. package/dist/scripts/lib/memory/admin/create-extractor.js +67 -0
  72. package/dist/scripts/lib/memory/admin/drop-edge-type.js +40 -0
  73. package/dist/scripts/lib/memory/admin/drop-entity-type.js +172 -0
  74. package/dist/scripts/lib/memory/admin/dump.js +47 -0
  75. package/dist/scripts/lib/memory/admin/helpers.js +31 -0
  76. package/dist/scripts/lib/memory/admin/init.js +170 -0
  77. package/dist/scripts/lib/memory/admin/live-store.js +76 -0
  78. package/dist/scripts/lib/memory/admin/reembed.js +285 -0
  79. package/dist/scripts/lib/memory/admin/rename-edge-type.js +54 -0
  80. package/dist/scripts/lib/memory/admin/rename-entity-type.js +151 -0
  81. package/dist/scripts/lib/memory/admin/reset.js +24 -0
  82. package/dist/scripts/lib/memory/admin/vacuum.js +9 -0
  83. package/dist/scripts/lib/memory/admin/validate.js +19 -0
  84. package/dist/scripts/lib/memory/config.js +149 -0
  85. package/dist/scripts/lib/memory/cozo.js +136 -0
  86. package/dist/scripts/lib/memory/drift.js +71 -0
  87. package/dist/scripts/lib/memory/embeddings.js +128 -0
  88. package/dist/scripts/lib/memory/frontmatter.js +75 -0
  89. package/dist/scripts/lib/memory/ndjson.js +84 -0
  90. package/dist/scripts/lib/memory/paths.js +25 -0
  91. package/dist/scripts/lib/memory/permissions.js +41 -0
  92. package/dist/scripts/lib/memory/prompt.js +109 -0
  93. package/dist/scripts/lib/memory/query.js +56 -0
  94. package/dist/scripts/lib/memory/reads.js +109 -0
  95. package/dist/scripts/lib/memory/schema.js +64 -0
  96. package/dist/scripts/lib/memory/search.js +73 -0
  97. package/dist/scripts/lib/memory/singleton.js +49 -0
  98. package/dist/scripts/lib/memory/store.js +162 -0
  99. package/dist/scripts/lib/memory/types.js +93 -0
  100. package/dist/scripts/lib/memory/validate.js +58 -0
  101. package/dist/scripts/lib/memory/writes.js +40 -0
  102. package/{scripts → dist/scripts}/lib/pending.js +7 -2
  103. package/dist/scripts/lib/secret.js +59 -0
  104. package/{scripts → dist/scripts}/lib/slug.js +3 -2
  105. package/dist/scripts/lib/snapshot/finish.js +103 -0
  106. package/dist/scripts/lib/snapshot/inspect.js +253 -0
  107. package/dist/scripts/lib/snapshot/render.js +55 -0
  108. package/dist/scripts/lib/sort-fs-check.js +121 -0
  109. package/dist/scripts/lib/sort-routing.js +101 -0
  110. package/{scripts → dist/scripts}/lib/stage-guard.js +12 -6
  111. package/{scripts → dist/scripts}/lib/state.js +4 -0
  112. package/dist/scripts/lib/token.js +57 -0
  113. package/dist/scripts/lib/tracing.js +59 -0
  114. package/dist/scripts/lib/ulid.js +100 -0
  115. package/dist/scripts/lib/validator-jsonl.js +162 -0
  116. package/{scripts → dist/scripts}/lib/workfile.js +38 -20
  117. package/dist/scripts/orchestrate-cycle.js +215 -0
  118. package/dist/scripts/orchestrate-phases.js +314 -0
  119. package/dist/scripts/orchestrate.js +163 -0
  120. package/dist/scripts/sort.js +278 -0
  121. package/{skills → dist/skills}/add-appraiser/SKILL.md +39 -9
  122. package/{skills → dist/skills}/add-artefact-type/SKILL.md +46 -24
  123. package/{skills → dist/skills}/add-cycle/SKILL.md +57 -17
  124. package/dist/skills/add-extractor/SKILL.md +133 -0
  125. package/{skills → dist/skills}/add-flow/SKILL.md +36 -10
  126. package/dist/skills/add-law/SKILL.md +191 -0
  127. package/dist/skills/add-memory-edge-type/SKILL.md +52 -0
  128. package/dist/skills/add-memory-entity-type/SKILL.md +74 -0
  129. package/{skills → dist/skills}/appraise/SKILL.md +62 -13
  130. package/dist/skills/assay/SKILL.md +72 -0
  131. package/dist/skills/change-embedding-model/SKILL.md +58 -0
  132. package/dist/skills/drop-memory-edge-type/SKILL.md +54 -0
  133. package/dist/skills/drop-memory-entity-type/SKILL.md +57 -0
  134. package/dist/skills/dry-run/SKILL.md +116 -0
  135. package/{skills → dist/skills}/flow/SKILL.md +15 -2
  136. package/dist/skills/forge/SKILL.md +121 -0
  137. package/dist/skills/human-appraise/SKILL.md +153 -0
  138. package/{skills → dist/skills}/init-foundry/SKILL.md +23 -4
  139. package/dist/skills/init-memory/SKILL.md +92 -0
  140. package/{skills → dist/skills}/orchestrate/SKILL.md +30 -4
  141. package/dist/skills/quench/SKILL.md +99 -0
  142. package/{skills → dist/skills}/refresh-agents/SKILL.md +1 -1
  143. package/dist/skills/rename-memory-edge-type/SKILL.md +50 -0
  144. package/dist/skills/rename-memory-entity-type/SKILL.md +51 -0
  145. package/dist/skills/reset-memory/SKILL.md +54 -0
  146. package/dist/skills/upgrade-foundry/SKILL.md +192 -0
  147. package/package.json +34 -17
  148. package/.opencode/plugins/foundry.js +0 -761
  149. package/CHANGELOG.md +0 -100
  150. package/docs/concepts.md +0 -122
  151. package/docs/getting-started.md +0 -187
  152. package/docs/work-spec.md +0 -207
  153. package/scripts/lib/artefacts.js +0 -124
  154. package/scripts/lib/config.js +0 -175
  155. package/scripts/lib/feedback-transitions.js +0 -25
  156. package/scripts/lib/feedback.js +0 -440
  157. package/scripts/lib/finalize.js +0 -41
  158. package/scripts/lib/history.js +0 -59
  159. package/scripts/lib/secret.js +0 -23
  160. package/scripts/lib/tags.js +0 -108
  161. package/scripts/lib/token.js +0 -26
  162. package/scripts/orchestrate.js +0 -418
  163. package/scripts/sort.js +0 -370
  164. package/scripts/validate-tags.js +0 -54
  165. package/skills/add-law/SKILL.md +0 -111
  166. package/skills/forge/SKILL.md +0 -88
  167. package/skills/human-appraise/SKILL.md +0 -82
  168. package/skills/quench/SKILL.md +0 -62
  169. package/skills/upgrade-foundry/SKILL.md +0 -216
  170. /package/{skills → dist/skills}/list-agents/SKILL.md +0 -0
@@ -0,0 +1,396 @@
1
+ # Concepts
2
+
3
+ This is the glossary. Every term here has a single definition and links out to the spec document that elaborates it. Concepts are arranged roughly top-down: flows contain cycles, cycles contain stages, stages operate on artefacts, artefacts are governed by laws and evaluated by appraisers.
4
+
5
+ ---
6
+
7
+ ## Flow
8
+
9
+ The top-level unit of work. Defined in `foundry/flows/*.md`. A flow declares:
10
+
11
+ - A `starting-cycles` list — hints about which cycles can be entered first when the flow begins.
12
+ - A set of cycles (listed under `## Cycles`). Order is not implied — routing between cycles is owned by cycles themselves via their `targets` field.
13
+
14
+ Running a flow creates a work branch and a `WORK.md`. The flow completes when no more reachable cycles remain to run, or when the user decides to stop.
15
+
16
+ ## Cycle
17
+
18
+ An iterative unit that produces one artefact type and routes to later cycles through its `targets`. Defined in `foundry/cycles/*.md`. A cycle declares:
19
+
20
+ - `output-type` — the artefact type it produces (read-write).
21
+ - `inputs` — a contract (`any-of` / `all-of`) over other artefact types. Inputs are discovered on disk; they are read-only unless the output type's patterns happen to cover them.
22
+ - `targets` — the cycle(s) that may run after this one. May be empty (terminal cycle).
23
+ - `human-appraise` — whether a human quality gate runs every iteration (default: `false`).
24
+ - `deadlock-appraise` — whether a human is pulled in when LLM appraisers deadlock (default: `true`).
25
+ - `deadlock-iterations` — deadlock threshold (default: `5`).
26
+ - `models` — optional per-stage model overrides.
27
+
28
+ A cycle runs **assay** first when configured, then **forge → quench → appraise** (and optionally **human-appraise**), looping until all feedback is resolved or `max-iterations` is hit.
29
+
30
+ ## Stage
31
+
32
+ A single step within a cycle. Every stage is referenced as `base:alias` (e.g. `forge:write-haiku`, `quench:check-syllables`) — the base is the stage type; the alias makes the stage's role self-documenting in WORK.md.
33
+
34
+ The stage names come from the foundry metaphor because the system treats AI output as work that must be processed into a trusted artefact, not merely generated. Each base names a distinct part of that process.
35
+
36
+ - **assay** — opt-in pre-forge stage that populates flow memory by running project-authored extractor scripts (iteration 0 only). No artefact, no feedback, no output beyond memory writes. See the [Assay](#assay) and [Extractor](#extractor) entries below.
37
+ - **forge** — produce or revise the artefact.
38
+ - **quench** — run deterministic CLI checks (skipped if the artefact type has no `validation.md`).
39
+ - **appraise** — subjective evaluation by multiple appraiser sub-agents.
40
+ - **human-appraise** — human quality gate. Can run every iteration, only on deadlock, or both.
41
+
42
+ Feedback is always *about an artefact* and flows backward to forge. Assay sits outside the artefact-feedback loop because it precedes the artefact and its only failure mode (a broken extractor under `foundry/memory/extractors/`) lives outside forge's `file-patterns`.
43
+
44
+ Every stage runs inside a token-gated lifecycle bracketed by `foundry_stage_begin` and `foundry_stage_end`, with an internal finalize step run by `foundry_orchestrate` after `stage_end` to scan the disk and register artefacts. Mutation tools are stage-locked: a forge stage can't add feedback, a quench stage can't register artefacts. See the enforcement section of the [README](../README.md#enforcement-model).
45
+
46
+ ## Assay
47
+
48
+ A deterministic stage that runs before the first `forge` of a cycle. For each extractor listed in the cycle's `assay.extractors` frontmatter, it runs the extractor's `command`, parses the JSONL output, and upserts rows into flow memory via the existing memory write tools.
49
+
50
+ In metallurgy, to *assay* an ore or alloy is to determine its composition before working it. The stage plays the same role for a codebase: it determines what material is actually present so forge can plan from measured project facts.
51
+
52
+ Properties:
53
+
54
+ - **Opt-in per cycle.** A cycle declares `assay: { extractors: [name, ...] }`. Cycles without this block behave exactly as they always have.
55
+ - **Iteration 0 only.** Runs once, before the first forge. Re-extraction on later iterations is out of scope for v1.
56
+ - **Requires memory.** A cycle with `assay:` but no `foundry/memory/` fails to load with a clear error.
57
+ - **Strict failure.** Any non-zero exit, parse error, permission violation, or timeout marks the workfile failed and aborts the cycle. The user must fix the root cause, then either clear the failed state with `foundry_stage_retry()` or abandon the cycle and start again.
58
+
59
+ See also: [Extractor](#extractor).
60
+
61
+ ## Artefact type
62
+
63
+ A definition of what is being produced. Lives in `foundry/artefacts/<type>/`:
64
+
65
+ - `definition.md` — identity, file patterns, output directory, appraiser config, prose description.
66
+ - `laws.md` *(optional)* — type-specific subjective criteria.
67
+ - `validation.md` *(optional)* — CLI commands for deterministic quench checks.
68
+
69
+ File patterns must not overlap with any other artefact type's patterns — the write-invariant enforcer needs to know which type owns a given file.
70
+
71
+ ## Law
72
+
73
+ A subjective pass/fail criterion. Two scopes:
74
+
75
+ - **Global** — `foundry/laws/*.md`, all files concatenated, applies to every artefact.
76
+ - **Type-specific** — `foundry/artefacts/<type>/laws.md`.
77
+
78
+ Each law is a `## heading` (its identifier, used in feedback tags as `law:<id>`) with a description, passing criteria, and failing criteria.
79
+
80
+ ## Appraiser
81
+
82
+ An independent evaluator with a defined personality. Lives in `foundry/appraisers/*.md`. Appraisers may specify a `model` field to override the cycle-level appraise model. Each artefact type picks which appraisers may evaluate it (`appraisers.allowed`) and how many run per iteration (`appraisers.count`). Selection distributes evenly across allowed personalities.
83
+
84
+ ## WORK.md
85
+
86
+ The transient shared state for a flow. Created on the work branch by the flow skill, it tracks:
87
+
88
+ - Current position (flow, cycle, stage list, iteration limits) in frontmatter.
89
+ - The goal (prose — written once).
90
+ - An artefact registry (file, type, cycle, status).
91
+ - Feedback state lives alongside it in `WORK.feedback.yaml`.
92
+
93
+ See [work-spec.md](work-spec.md) for the full spec.
94
+
95
+ ## WORK.history.yaml
96
+
97
+ Append-only log of every stage execution, sitting next to WORK.md. Used by sort to reconstruct what has happened in the current cycle. See [work-spec.md](work-spec.md).
98
+
99
+ ## Feedback
100
+
101
+ Feedback items live in `WORK.feedback.yaml` — a yaml file at the worktree
102
+ root, alongside `WORK.md`. Every item has a ULID, a source stage, and a
103
+ full history of state transitions (open → actioned → resolved, or variants
104
+ including wont-fix / rejected / deadlocked).
105
+
106
+ Plugins read and write feedback through the `foundry_feedback_*` tools;
107
+ skills never edit the yaml directly. Sort-side detection of deadlocked
108
+ items (per-item history depth) replaces the earlier global-iteration
109
+ counter.
110
+
111
+ See `docs/work-spec.md` for the full schema and state machine.
112
+
113
+ ## HITL / human-appraise
114
+
115
+ Human-in-the-loop checkpoint. A stage where Foundry pauses and asks a human for input. Two triggers:
116
+
117
+ 1. **Every-iteration** — the cycle declares `human-appraise: true`. The `human-appraise` stage runs after LLM appraise each iteration.
118
+ 2. **Deadlock** — the cycle declares `deadlock-appraise: true` (default). If forge and appraisers ping-pong on the same items for `deadlock-iterations` (default 5) iterations, sort inserts a `human-appraise` stage to break the tie.
119
+
120
+ Human feedback is tagged `human` and takes priority over LLM feedback on the same topic.
121
+
122
+ ## Micro-commit
123
+
124
+ Every stage ends with a commit made by the orchestrator. This enables two things: file-modification enforcement (the write-invariant check compares the stage's diff to its allowed patterns) and recoverability (a crash mid-flow leaves a clean commit boundary to resume from). Orchestration refuses to proceed if unrelated dirty files are present anywhere in the worktree, or if tool-managed files and the current stage's allowed patterns do not account for the pending changes.
125
+
126
+ ## Branch namespaces
127
+
128
+ Foundry partitions mutation across three disjoint branch kinds, and the
129
+ plugin enforces the split at tool-call time.
130
+
131
+ - **`config/<description>`** — schema and config mutation. Owns
132
+ `foundry/`. Typically created from `main` via
133
+ `foundry_git_branch({ kind: "config", description })`. The
134
+ `foundry_config_create_*`, `foundry_memory_create_*`,
135
+ `foundry_extractor_create`, and the schema-mutating memory admin
136
+ tools all refuse off this kind.
137
+ - **`work/<flowId>-<description>`** — flow-data mutation. Owns
138
+ `WORK.md`, `WORK.feedback.yaml`, `WORK.history.yaml`, and
139
+ `foundry-memory/` row data. Typically created from `main` via
140
+ `foundry_git_branch({ kind: "work", flowId, description })`. The
141
+ `foundry_orchestrate`, workfile, feedback, artefact-status,
142
+ assay/validate/appraisers-select, stage-begin/end, and
143
+ `foundry_memory_put`/`_relate`/`_unrelate` tools refuse off this
144
+ kind (and off dry-run, see below). On `foundry_git_finish`, the work
145
+ branch is preserved as `archive/work/<flowId>-<description>-<hash>`,
146
+ providing immutable forensic history. The signed squash commit on
147
+ the base branch embeds a canonical Foundry attestation block.
148
+ - **`dry-run/<parentConfig>/<flowId>-<description>`** — trial run of
149
+ in-progress config against a real flow. Created from a `config/*`
150
+ branch via
151
+ `foundry_git_branch({ kind: "dry-run", flowId, description })`. Has
152
+ the same flow-data write permissions as `work/*`, but on
153
+ `foundry_git_finish` it writes a forensic snapshot
154
+ (`README.md`, `work/WORK*`, `diff.patch`, `trace.jsonl`) under
155
+ `.snapshots/<run-id>/` on the parent `config/*` working tree and
156
+ force-deletes the dry-run branch. No merge, no commit. The two
157
+ spaces stay deliberately disjoint.
158
+
159
+ ## Branch guards
160
+
161
+ The plugin enforces the branch-namespace split at tool-call time
162
+ through a small library of guards in `src/scripts/lib/branch-guard.js`.
163
+ Every mutating tool composes one of three guards before its handler
164
+ runs:
165
+
166
+ - **`requireOnConfigBranch`** — accepts only `config/<description>`.
167
+ Schema-mutation tools (`foundry_config_create_*`,
168
+ `foundry_memory_create_*`, `foundry_extractor_create`, the memory
169
+ admin family) use this guard. `dry-run/<x>/<y>` is rejected by
170
+ design — schema must change on a real config branch so the change
171
+ can be merged to `main`.
172
+ - **`requireOnFlowBranch`** — accepts `work/<flow>-<desc>` or
173
+ `dry-run/<x>/<y>`. Flow-data tools (`foundry_orchestrate`, the
174
+ workfile / feedback / artefact-status / assay / appraisers
175
+ families, `foundry_memory_put` / `_relate` / `_unrelate`) use this
176
+ guard.
177
+ - **`requireOnConfigOrFlowBranch`** — accepts any of the three
178
+ namespaces. Read-only diagnostic tools that touch files in either
179
+ tree use this guard.
180
+
181
+ `currentBranch()` resolves the active branch in a single place,
182
+ including unborn HEADs (fresh repos with no commits) and detached
183
+ HEAD. When no branch can be resolved the guards return a structured
184
+ refusal envelope, giving the LLM the same shape for branch refusals as
185
+ for any other tool failure.
186
+
187
+ ## Archive branch
188
+
189
+ When `foundry_git_finish` completes a `work/*` branch, it preserves the
190
+ full branch as `archive/work/<flowId>-<description>-<hash>` before
191
+ squash-merging. The archive branch is immutable forensic history: every
192
+ stage micro-commit, `WORK.md`, `WORK.feedback.yaml`,
193
+ `WORK.history.yaml`, and all intermediate artefact states remain intact.
194
+ The signed squash commit on the base branch references the archive
195
+ branch tip SHA in its attestation block. Archive branches accumulate
196
+ indefinitely — periodic manual pruning is outside the tool's scope.
197
+
198
+ ## Stage token
199
+
200
+ A single-use HMAC-signed string, minted by `foundry_orchestrate` when a stage is dispatched. The sub-agent must redeem the token via `foundry_stage_begin`; mutation tools then check the active stage matches their role. Keys live in `.foundry/.secret` (mode 0600, gitignored, one per worktree). This prevents out-of-band mutations, replayed stages, and sub-agents skipping the lifecycle.
201
+
202
+ ## `.foundry/` state directory
203
+
204
+ A gitignored directory created on first plugin boot, holding runtime state:
205
+
206
+ - `.secret` — the HMAC key.
207
+ - `active-stage.json` — present only during an active stage.
208
+ - `last-stage.json` — used by the orchestrator's internal finalize step after `stage_end`.
209
+ - `trace/<branch-slug>.jsonl` — per-branch tool-call trace (see Tracing).
210
+
211
+ ## Tracing
212
+
213
+ Every guarded `foundry_*` tool call made on a `dry-run/<x>/<y>` branch appends
214
+ one JSONL record to `.foundry/trace/<branch-slug>.jsonl`, where the
215
+ slug is the branch name with `/` replaced by `-`. The trace captures
216
+ tool name, args, result envelope, and timing — enough to reconstruct
217
+ what the dry-run did without rerunning it. The trace file is created
218
+ fresh when the dry-run branch starts (any prior content is truncated)
219
+ and is captured into the snapshot at finish-time. Records on `work/*`
220
+ and `config/*` branches are not written; tracing is a dry-run-only
221
+ concept. `foundry_orchestrate` is currently excluded because it is not
222
+ wrapped by the shared guarded-tool path. Implementation:
223
+ `src/scripts/lib/tracing.js`.
224
+
225
+ ## Dry-run
226
+
227
+ Trial execution of an in-progress `config/*` branch against a real
228
+ flow. Driven by the `dry-run` skill. The user creates a
229
+ `dry-run/<parentConfig>/<flowId>-<description>` branch from the
230
+ config branch via
231
+ `foundry_git_branch({ kind: "dry-run", flowId, description })`,
232
+ then runs the flow normally. The dry-run branch has the same
233
+ flow-data write permissions as `work/*` (forge can edit artefacts,
234
+ memory rows can be written), but `foundry_git_finish` on it neither
235
+ merges nor commits — it captures a snapshot and force-deletes the
236
+ branch. Schema-mutation tools refuse on dry-run by design, so
237
+ config changes always land through `config/*` → `main`. Nesting is
238
+ forbidden: `dry-run/<x>/<y>/<z>` is rejected by the same regex that
239
+ gates depth.
240
+
241
+ ## Snapshot
242
+
243
+ The forensic record of a finished dry-run, materialised as a
244
+ plain-files directory at `.snapshots/<runId>/` on the parent
245
+ `config/*` working tree. Each snapshot contains:
246
+
247
+ - `README.md` — rendered metadata (branch, parent, flow, goal,
248
+ timestamps, exit reason, commit log).
249
+ - `work/WORK.md`, `work/WORK.history.yaml`, `work/WORK.feedback.yaml` —
250
+ the workfile triple captured before branch deletion (any of these
251
+ may be absent if the dry-run did not produce them).
252
+ - `diff.patch` — the full `git diff parent...HEAD` from the dry-run.
253
+ - `trace.jsonl` — the full tool-call trace.
254
+
255
+ `runId` is `<branch-slug>-<ulid>`. Snapshots are gitignored
256
+ (`.snapshots/` is added to `.gitignore` by `init-foundry`) and
257
+ accumulate locally; `foundry_snapshot_list` enumerates them,
258
+ `foundry_snapshot_show` returns a structured summary,
259
+ `foundry_snapshot_delete` removes one, and
260
+ `foundry_snapshot_prune` removes those older than a given age.
261
+ Implementation: `src/scripts/lib/snapshot/`.
262
+
263
+ ## Custom tools
264
+
265
+ All deterministic pipeline operations are exposed as custom tools by the Foundry plugin. Skills call these tools to run deterministic pipeline operations. Tools are backed by shared library modules in `src/scripts/lib/` with injectable I/O so they can be unit-tested. This separation ensures state transitions and routing logic are tested code, not LLM interpretation. See [tools.md](./tools.md) for the full catalogue.
266
+
267
+ ## Skill
268
+
269
+ A self-contained workflow written as markdown with YAML frontmatter. Foundry ships pipeline skills (`flow`, `orchestrate`, `forge`, `quench`, `appraise`, `human-appraise`), authoring skills (`add-*`, `init-foundry`), utility skills (`list-agents`, `refresh-agents`, `upgrade-foundry`), and memory skills (`init-memory`, `add-memory-*`, `rename-memory-*`, `drop-memory-*`, `reset-memory`, `change-embedding-model`). Skills are either **atomic** (do one thing) or **composite** (orchestrate other skills).
270
+
271
+ ---
272
+
273
+ ## Extractor
274
+
275
+ A project-authored CLI that emits JSONL describing entities and edges to upsert into flow memory. Defined in `foundry/memory/extractors/<name>.md`:
276
+
277
+ - `command` — path to the executable (or shell command) to run. Stdout is parsed as JSONL.
278
+ - `memory.write` — entity types the extractor is permitted to populate. Edge permissions are derived: an edge is permitted if either endpoint's entity type is in this list (mirroring the cycle-level rule).
279
+ - `timeout` (optional, default 60s) — hard kill if the script exceeds it.
280
+
281
+ The markdown body is a prose brief injected into the `forge` prompt of any cycle that uses this extractor, telling the agent what is in memory and where it came from. Extractors are run by the [Assay](#assay) stage.
282
+
283
+ Create with `add-extractor`.
284
+
285
+ ### JSONL output contract
286
+
287
+ Extractors emit one JSON object per line on stdout, discriminated by a required `kind` field. Two row shapes are recognised:
288
+
289
+ **Entity row:**
290
+
291
+ ```json
292
+ {"kind":"entity","type":"<entity-type>","name":"<id>","value":"<string>"}
293
+ ```
294
+
295
+ | Field | Type | Required | Notes |
296
+ |-------|------|----------|-------|
297
+ | `kind` | `"entity"` | yes | Discriminator. |
298
+ | `type` | string | yes | Must match a declared entity type in the project's vocabulary. |
299
+ | `name` | string | yes | Stable identifier for the entity within its type. |
300
+ | `value` | string | yes | Free text describing the entity's intrinsic characteristics. **Max 4096 bytes** (UTF-8). |
301
+
302
+ No other fields are permitted; unknown keys raise a parse error.
303
+
304
+ **Edge row:**
305
+
306
+ ```json
307
+ {"kind":"edge","from":{"type":"<t1>","name":"<n1>"},"edge":"<edge-type>","to":{"type":"<t2>","name":"<n2>"}}
308
+ ```
309
+
310
+ | Field | Type | Required | Notes |
311
+ |-------|------|----------|-------|
312
+ | `kind` | `"edge"` | yes | Discriminator. |
313
+ | `from` | object `{type,name}` | yes | Source endpoint. Both inner fields required and non-empty. |
314
+ | `edge` | string | yes | Edge type name (must exist in vocabulary). |
315
+ | `to` | object `{type,name}` | yes | Target endpoint. Both inner fields required and non-empty. |
316
+
317
+ No other fields are permitted on edge rows.
318
+
319
+ **Line handling:**
320
+
321
+ - Blank lines and lines starting with `#` (after trimming whitespace) are ignored — useful for header comments.
322
+ - Each remaining line must be a valid JSON object (not an array, not a primitive).
323
+ - Parsing is line-numbered: errors include the line number for easy debugging.
324
+
325
+ **Failure semantics:**
326
+
327
+ Any of the following mark the workfile failed (`status: failed` with a `reason`) and abort the cycle. No feedback item is written — extractor scripts live outside any artefact's `file-patterns`, so forge cannot fix them. The user must fix the root cause, then either clear the failed state with `foundry_stage_retry()` or abandon the cycle and start again:
328
+
329
+ - Extractor exits non-zero.
330
+ - Extractor exceeds the configured `timeout`.
331
+ - Output contains a malformed JSON line, an unknown `kind`, an unknown field, a missing required field, or an entity `value` exceeding 4096 bytes.
332
+ - An entity row references a `type` not in the extractor's `memory.write` set, or an edge row's endpoint types are both outside that set (permission violation).
333
+
334
+ The complete reference parser is `src/scripts/lib/assay/parse-jsonl.js`.
335
+
336
+ ## Flow memory
337
+
338
+ A typed, graph-shaped knowledge store shared across cycles in a project. Strictly opt-in: a project without `foundry/memory/` has no memory and behaves exactly as previous Foundry versions.
339
+
340
+ When present, memory is populated and consulted by cycles that declare read/write permissions in their frontmatter. It can be hand-seeded through committed NDJSON rows, written by memory-enabled stages through the memory tools, or populated at runtime by assay extractors before the first forge. Its vocabulary is injected into the dispatched stage's prompt, and its contents survive across flows as long as the NDJSON relations stay committed.
341
+
342
+ See also: [docs/memory-maintenance.md](memory-maintenance.md) for contributor-facing notes on Cozo 0.7 and session lifecycle.
343
+
344
+ ## Entity / entity type
345
+
346
+ An **entity** is one row in memory: `{ type, name, value }`, where `value` is free text describing the entity's intrinsic characteristics (≤ 4 KB). Relationships belong in edges, not in the value.
347
+
348
+ An **entity type** is declared once per project in `foundry/memory/entities/<type>.md`. Its markdown body is a prose brief — naming convention, what `value` should contain, likely related edges — that becomes part of every cycle's prompt that reads or writes this type. Create types with `add-memory-entity-type`.
349
+
350
+ ## Edge / edge type
351
+
352
+ An **edge** is one row relating two entities: `{ from_type, from_name, edge_type, to_type, to_name }`. Edges are directed.
353
+
354
+ An **edge type** declares allowed endpoints — `sources` and `targets` are either a list of entity types or the literal `any` — and a prose body describing when the edge holds. Declared in `foundry/memory/edges/<name>.md`. Create with `add-memory-edge-type`.
355
+
356
+ ## Memory permissions
357
+
358
+ Per-cycle opt-in, specified in cycle frontmatter:
359
+
360
+ ```yaml
361
+ memory:
362
+ read: [class, method] # types this cycle can read
363
+ write: [method] # types this cycle can upsert into
364
+ ```
365
+
366
+ A cycle with no `memory:` block gets no memory tools in its prompt. Entity reads check the `read` set only — a type listed in `write` only is writable but not readable. List a type in both lists for full access.
367
+
368
+ Edge permissions are derived: an edge is readable if either endpoint type is in `read` or `write`, writable if either endpoint type is in `write`. `foundry_memory_query` restricts `ent_*` and `edge_*` relations to the read set.
369
+
370
+ ## Memory layout
371
+
372
+ Memory uses a two-tree split: configuration lives under `foundry/memory/`
373
+ and is committed alongside the rest of the foundry config; row data
374
+ lives under `foundry-memory/relations/`, a top-level sibling of
375
+ `foundry/`, so it can be tracked or replaced independently.
376
+
377
+ ```
378
+ foundry/memory/ # config (committed)
379
+ ├── config.md # frontmatter: enabled, validation, embeddings
380
+ ├── schema.json # canonical, deterministic, derived from entity/edge files
381
+ ├── entities/<type>.md # prose brief per entity type
382
+ ├── edges/<name>.md # frontmatter (sources/targets) + prose brief
383
+ ├── extractors/<name>.md # extractor definitions (assay stage)
384
+ ├── memory.db # live Cozo store (gitignored)
385
+ ├── memory.db-wal # WAL (gitignored)
386
+ └── memory.db-shm # shared memory (gitignored)
387
+
388
+ foundry-memory/ # row data (committed)
389
+ └── relations/<type>.ndjson # one line per row, source of truth for memory contents
390
+ ```
391
+
392
+ `schema.json` is a **canonicalised** (fully key-sorted) derivation of the entity/edge files plus the active embedding configuration. It is a diff-friendly artefact of the vocabulary, not a source of truth — regenerated by the admin tools.
393
+
394
+ ## Embeddings
395
+
396
+ Optional. When `embeddings.enabled: true` in `config.md`, entity values are embedded against an OpenAI-compatible endpoint (default: local Ollama) and stored in a typed `<F32; N>?` column backed by an HNSW index. The `foundry_memory_search` tool exposes semantic nearest-neighbour search over entity values; `change-embedding-model` re-embeds all entities when the model changes. With embeddings disabled, everything else (graph, query, neighbours) still works.