@really-knows-ai/foundry 2.3.1 → 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 +200 -198
  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 +42 -6
  122. package/{skills → dist/skills}/add-artefact-type/SKILL.md +49 -21
  123. package/{skills → dist/skills}/add-cycle/SKILL.md +60 -14
  124. package/dist/skills/add-extractor/SKILL.md +133 -0
  125. package/{skills → dist/skills}/add-flow/SKILL.md +39 -7
  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 -90
  150. package/docs/concepts.md +0 -59
  151. package/docs/getting-started.md +0 -78
  152. package/docs/work-spec.md +0 -193
  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 -105
  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,345 @@
1
+ # Getting Started
2
+
3
+ End-to-end walkthrough for setting up Foundry and running your first flow.
4
+
5
+ ---
6
+
7
+ ## Prerequisites
8
+
9
+ - A git repository initialised with a clean working tree.
10
+ - Node.js ≥ 18.3.0 (for the plugin and validation scripts).
11
+ - [OpenCode](https://opencode.ai) (primary target — multi-model routing relies on OpenCode's agent files).
12
+
13
+ ## Install
14
+
15
+ Add Foundry to `opencode.json`:
16
+
17
+ ```json
18
+ {
19
+ "$schema": "https://opencode.ai/config.json",
20
+ "plugin": ["@really-knows-ai/foundry"]
21
+ }
22
+ ```
23
+
24
+ OpenCode resolves the package itself — `npm install` is **not** required. Restart OpenCode (or reload plugins) so the plugin registers its tools and skills.
25
+
26
+ Optionally, if you want the package available to your project's local node_modules (for editor tooling or scripts), run:
27
+
28
+ ```sh
29
+ npm install --save-dev @really-knows-ai/foundry
30
+ ```
31
+
32
+ ## Initialise
33
+
34
+ In your project, invoke the `init-foundry` skill. It:
35
+
36
+ 1. Creates the `foundry/` directory structure:
37
+ ```
38
+ foundry/
39
+ artefacts/.gitkeep
40
+ flows/.gitkeep
41
+ cycles/.gitkeep
42
+ laws/.gitkeep
43
+ appraisers/.gitkeep
44
+ ```
45
+ 2. Runs `refresh-agents` to generate `.opencode/agents/foundry-*.md` — one per available model — so cycles can dispatch to specific models later.
46
+ 3. Commits the scaffolding.
47
+
48
+ The `.foundry/` runtime directory (holding `.secret` for stage tokens) is created automatically on first plugin boot and added to `.gitignore`.
49
+
50
+ ---
51
+
52
+ ## Author the configuration
53
+
54
+ Foundry's configuration is five things: artefact types, laws, appraisers, cycles, and flows. You can write the files by hand, but the authoring skills do conflict checking, scaffolding, and validation — use them.
55
+
56
+ Before using any schema-writing skill, open a config branch. All schema-mutation tools (`foundry_config_create_*`, `foundry_memory_create_*`, `foundry_extractor_create`, the memory admin family) refuse off `main` and off flow branches:
57
+
58
+ ```text
59
+ foundry_git_branch({ kind: "config", description: "<short-name>" })
60
+ ```
61
+
62
+ This typically puts you on `config/<short-name>` from `main`. Make all the
63
+ edits below on this branch, then `foundry_git_finish({ message: "...",
64
+ baseBranch: "main", confirm: true })` squashes the work back to
65
+ `main` in one commit. To trial the in-progress edits against a real
66
+ flow before merging, see "Trial config edits with dry-run" below.
67
+
68
+ ### 1. Define an artefact type
69
+
70
+ Run `add-artefact-type`. It walks you through:
71
+
72
+ - `id` (lowercase, hyphenated), `name`, prose description.
73
+ - `file-patterns` — glob patterns describing which files this type owns. Forge's write scope is exactly these patterns; anything written outside them violates the cycle. The skill refuses patterns that overlap with existing types.
74
+ - Appraiser config — how many appraisers evaluate this type and which personalities are allowed.
75
+ - Optional `laws.md` — type-specific criteria.
76
+ - Optional `validation.md` — CLI commands for quench (non-zero exit = failure).
77
+
78
+ Produces `foundry/artefacts/<id>/definition.md` (+ optional `laws.md`, `validation.md`).
79
+
80
+ ### 2. Write laws
81
+
82
+ Laws are subjective pass/fail criteria evaluated by appraisers. Two scopes:
83
+
84
+ - **Global** — `foundry/laws/*.md`. All files are concatenated and apply to every artefact.
85
+ - **Type-specific** — `foundry/artefacts/<type>/laws.md`.
86
+
87
+ Run `add-law` to create one with conflict detection. Each law is a `## heading` (its identifier, referenced as `law:<id>` in feedback) with a description, passing criteria, and failing criteria.
88
+
89
+ ### 3. Create appraisers
90
+
91
+ Appraisers are independent evaluators with named personalities. Run `add-appraiser`. Each appraiser may override the cycle-level appraise model via a `model` field. Artefact types pick which appraisers may evaluate them (`appraisers.allowed`).
92
+
93
+ ### 4. Define a cycle
94
+
95
+ Run `add-cycle`. A cycle produces one artefact type and declares:
96
+
97
+ - `output-type` — the artefact type (must already exist).
98
+ - `inputs` — a contract (`any-of` or `all-of`) over other types. Empty for starting cycles.
99
+ - `targets` — the cycle(s) that may run after this one. Empty for terminal cycles.
100
+ - `human-appraise` / `deadlock-appraise` / `deadlock-iterations` — human-gate config.
101
+ - `models` — optional per-stage model overrides.
102
+
103
+ Example:
104
+
105
+ ```markdown
106
+ ---
107
+ id: haiku-creation
108
+ name: Haiku Creation
109
+ output-type: haiku
110
+ inputs:
111
+ type: any-of
112
+ artefacts:
113
+ - petition
114
+ targets: []
115
+ human-appraise: false
116
+ deadlock-appraise: true
117
+ deadlock-iterations: 5
118
+ models:
119
+ appraise: openai/gpt-5
120
+ ---
121
+
122
+ # Haiku Creation
123
+
124
+ Writes a haiku satisfying the petition produced by haiku-ideation.
125
+ ```
126
+
127
+ The skill validates that every input type can be produced by some cycle in the flow and that targets are reachable.
128
+
129
+ ### 5. Define a flow
130
+
131
+ Run `add-flow`. A flow groups cycles and declares starting points:
132
+
133
+ ```markdown
134
+ ---
135
+ id: make-haiku
136
+ name: Make a Haiku
137
+ starting-cycles:
138
+ - haiku-ideation
139
+ ---
140
+
141
+ # Make a Haiku
142
+
143
+ End-to-end flow: petition → haiku, with a human quality gate.
144
+
145
+ ## Cycles
146
+
147
+ - haiku-ideation
148
+ - haiku-creation
149
+ ```
150
+
151
+ Routing between cycles is owned by individual cycles via their `targets`, not by the flow.
152
+
153
+ ### 6. Validate before writing (optional)
154
+
155
+ Each `add-*` skill writes and commits in one step. When you want to validate a draft body without committing it, call the matching validator first:
156
+
157
+ ```text
158
+ foundry_config_validate_artefact_type({ name, body })
159
+ foundry_config_validate_law({ name, body })
160
+ foundry_config_validate_appraiser({ name, body })
161
+ foundry_config_validate_cycle({ name, body })
162
+ foundry_config_validate_flow({ name, body })
163
+ ```
164
+
165
+ Validators return `{ok: true}` on success or
166
+ `{ok: false, errors: [...]}` on a parse / schema / overlap problem; nothing
167
+ is written either way. Once the validator is happy, call the
168
+ matching `_create_*` tool to commit it.
169
+
170
+ ---
171
+
172
+ ## Run the flow
173
+
174
+ Tell OpenCode something like:
175
+
176
+ > Run the `make-haiku` flow to write a haiku about autumn rain.
177
+
178
+ The `flow` skill will:
179
+
180
+ 1. Check prerequisites and pick a starting cycle — matching your prose to a cycle's output type. If the request is ambiguous, it prompts (defaulting to `starting-cycles`). If a cycle's input contract can't be satisfied from files on disk, it won't be chosen.
181
+ 2. Create a work branch and scaffold `WORK.md` with the goal.
182
+ 3. Hand off to `orchestrate`, which drives the cycle:
183
+ - **forge** writes the artefact.
184
+ - **quench** runs CLI validators (if configured).
185
+ - **appraise** dispatches parallel appraiser sub-agents and consolidates their `law:<id>` feedback.
186
+ - **human-appraise** (if configured, or on deadlock) asks you for input.
187
+ - If any unresolved feedback remains, another forge iteration begins.
188
+ 4. When the cycle completes, the flow skill checks the cycle's `targets`. If a target's input contract is satisfied, it asks whether to proceed.
189
+ 5. When all desired cycles are done, the flow skill summarises the output and asks how to finish — squash-merge, PR, or leave the branch.
190
+
191
+ Every stage ends with a micro-commit. Violations of the write invariant (writing to disallowed files) hard-stop the cycle.
192
+
193
+ ---
194
+
195
+ ## Trial config edits with dry-run
196
+
197
+ When you've changed a law, an appraiser, or a cycle on a `config/*`
198
+ branch and want to see how the change behaves end-to-end before
199
+ merging, use dry-run mode.
200
+
201
+ ```text
202
+ # starting on a config/* branch with the in-progress edit
203
+ foundry_git_branch({ kind: "dry-run", flowId: "make-haiku",
204
+ description: "stricter-imagery-law" })
205
+ # now on dry-run/<parent>/make-haiku-stricter-imagery-law
206
+
207
+ # run the flow as you normally would
208
+ # every foundry_* call is traced to .foundry/trace/<branch>.jsonl
209
+
210
+ foundry_git_finish({ message: "trial: stricter imagery law", confirm: true })
211
+ # writes .snapshots/<run-id>/{README.md, work/WORK*, diff.patch, trace.jsonl}
212
+ # on the parent config/* working tree and force-deletes the dry-run branch
213
+ ```
214
+
215
+ Inspect the snapshot at `.snapshots/<run-id>/`, decide whether to keep
216
+ the config edit, and either commit/merge from the parent `config/*`
217
+ branch or revise and trial again. Snapshots are local artefacts and
218
+ never committed by foundry. See the `dry-run` skill for the full loop.
219
+
220
+ ---
221
+
222
+ ## Inspecting progress
223
+
224
+ While a flow is running, the state of the world is in four places:
225
+
226
+ - `WORK.md` — current cycle, goal, and artefact table.
227
+ - `WORK.feedback.yaml` — feedback items and their lifecycle history.
228
+ - `WORK.history.yaml` — append-only stage execution log.
229
+ - `git log` — one commit per stage.
230
+
231
+ You can pause and resume: if the flow skill sees an existing `WORK.md` when you start, it asks whether to resume, discard, or abort. Resume is only offered if the existing flow and cycle match the current request.
232
+
233
+ ### Recovering a failed flow
234
+
235
+ A guard violation, a broken extractor in `assay`, or any other
236
+ unrecoverable error marks the workfile failed (`status: failed` in
237
+ `WORK.md` frontmatter, with a `reason`). Ordinary mutating tools refuse
238
+ once that flag is set, but recovery and cleanup tools such as
239
+ `foundry_stage_end`, `foundry_stage_retry()`, and
240
+ `foundry_workfile_delete({ confirm: true })` remain available. Read-only
241
+ diagnostics (`foundry_workfile_get`, `foundry_history_list`,
242
+ `foundry_memory_*` read-side, read-only `foundry_config_*`, and
243
+ `foundry_config_validate_*`) keep working so you can figure out what
244
+ went wrong.
245
+
246
+ Recovery has two paths:
247
+
248
+ - `foundry_stage_retry()` clears the failed state, discards uncommitted in-memory changes, clears `.foundry/last-stage.json`, and lets you re-run the blocked stage. It requires a failed flow, no active stage, and a clean git working tree.
249
+ - `foundry_workfile_delete({ confirm: true })` abandons the cycle entirely by removing `WORK.md`, `WORK.feedback.yaml`, and `WORK.history.yaml` from the work branch.
250
+
251
+ Use `foundry_stage_retry()` when the underlying problem is fixed and you want to continue the current cycle. Use `foundry_workfile_delete({ confirm: true })` when you want to abandon the run and start again.
252
+
253
+ ---
254
+
255
+ ## Cleaning up
256
+
257
+ When a flow completes, `foundry_git_finish` handles integration with audit guarantees. On `work/*` branches, it commits `WORK.*` cleanup, preserves the branch as `archive/work/<flow>-<desc>-<hash>` for immutable forensic history, squash-merges to the base branch, and creates a signed commit whose message embeds the canonical Foundry attestation block. See [`docs/tools.md`](./tools.md#foundry_git_finish) for the full contract.
258
+
259
+ ---
260
+
261
+ ## Optional: flow memory
262
+
263
+ Foundry ships a typed, graph-shaped memory store that persists across cycles. Use it when your flows are codebase-aware, require multi-cycle discovery, reuse project facts across runs, or perform semantic search when embeddings are enabled. Memory is strictly opt-in — skip this section if your project doesn't need shared state across flows.
264
+
265
+ ### Initialise
266
+
267
+ Memory init and vocabulary edits are schema mutations, so they run
268
+ on a config branch — open one first if you are not already on it
269
+ (`foundry_git_branch({ kind: "config", description: "memory-setup" })`).
270
+
271
+ Run the `init-memory` skill. It asks whether to enable embeddings (default: yes, targeting local Ollama `nomic-embed-text` on `http://localhost:11434/v1`) and then invokes `foundry_memory_init`, which deterministically:
272
+
273
+ - creates `foundry/memory/entities/` and `edges/` (each with `.gitkeep`) plus the top-level sibling `foundry-memory/relations/` for committed row data,
274
+ - writes `foundry/memory/config.md` (frontmatter driven by your embeddings choice) and `foundry/memory/schema.json`,
275
+ - appends `foundry/memory/memory.db*` entries to `.gitignore` (idempotent),
276
+ - probes the embedding provider if enabled; if the probe fails, the skill offers three remedies (install/start Ollama, point at a different OpenAI-compatible endpoint, or disable embeddings).
277
+
278
+ ### Declare vocabulary
279
+
280
+ Two concepts: **entity types** (things memory knows about, e.g. `class`, `method`) and **edge types** (directed relationships, e.g. `calls`, `references`).
281
+
282
+ - `add-memory-entity-type` — name + prose body (naming convention, what `value` should contain, likely related edges). The body is injected into the prompt of every cycle that reads/writes this type, so write it for an LLM reader.
283
+ - `add-memory-edge-type` — name, `sources` (list of entity types or `any`), `targets` (list or `any`), and a prose body that describes **when** the edge holds and **what it does not cover**.
284
+
285
+ Both skills commit their work. The vocabulary lives in `foundry/memory/entities/` and `foundry/memory/edges/`; committed row data lives in the top-level sibling `foundry-memory/relations/<name>.ndjson`.
286
+
287
+ ### Give cycles memory permissions
288
+
289
+ Memory is per-cycle opt-in. Add a `memory:` block to any cycle that should see it:
290
+
291
+ ```yaml
292
+ ---
293
+ id: extract-methods
294
+ output-type: method-notes
295
+ memory:
296
+ read: [class]
297
+ write: [method]
298
+ ---
299
+ ```
300
+
301
+ - Types in `read` become visible (the cycle's dispatched prompt lists them along with `foundry_memory_get`, `foundry_memory_list`, `foundry_memory_neighbours`, `foundry_memory_query`, and — if embeddings are on — `foundry_memory_search`).
302
+ - Types in `write` additionally expose `foundry_memory_put`, `foundry_memory_relate`, `foundry_memory_unrelate`.
303
+ - **`read` and `write` are independent.** Entity reads check only the `read` set — a type listed in `write` only is writable but not readable. If the cycle needs to read entities of a type before writing them (the common case), list it in **both** `read` and `write`.
304
+ - Edges are visible when either endpoint type is in `read` *or* `write`, writable when either endpoint type is in `write`.
305
+ - A cycle with no `memory:` block sees no memory tools — same as before.
306
+
307
+ During a flow, forge stages write into memory and later cycles can read what earlier cycles learned. Out-of-stage memory writes flush to `relations/*.ndjson` immediately, assay flushes during the assay stage, and ordinary in-stage writes become durable at `foundry_stage_end`.
308
+
309
+ ### Runtime population with assay
310
+
311
+ Use assay when memory should reflect what is actually present in the codebase before forge starts. An assay-enabled cycle declares memory permissions and the extractor names to run at iteration 0:
312
+
313
+ ```yaml
314
+ memory:
315
+ read: [class, method]
316
+ write: [class, method]
317
+ assay:
318
+ extractors: [java-symbols]
319
+ ```
320
+
321
+ Create the extractor definition with `add-extractor`. The definition lives at `foundry/memory/extractors/<name>.md`; its `command` runs from the project root and emits one JSON object per line. `foundry_assay_run` parses that JSONL, validates the rows against the extractor and cycle write scopes, and upserts the accepted entities and edges into flow memory.
322
+
323
+ Assay runs once before the first forge of the cycle. Successful extractor rows are flushed to `foundry-memory/relations/*.ndjson`, so later forge stages and downstream cycles can query the same committed facts. Extractor failures mark `WORK.md` failed because forge cannot fix instrumentation scripts inside an artefact revision. See [Extractor](concepts.md#extractor) for the JSONL contract and failure semantics.
324
+
325
+ ### Maintenance
326
+
327
+ - **Destructive operations** (`drop-memory-entity-type`, `drop-memory-edge-type`) call their tool first with `confirm: false` (the default) to get a preview (`entityRows`, affected edges with `cascadeDrop` vs `prune`), ask for explicit confirmation, then call again with `confirm: true`.
328
+ - **Renames** (`rename-memory-entity-type`, `rename-memory-edge-type`) cascade through entity/edge files, relations, and schema.
329
+ - **`reset-memory`** purges all row data but preserves type definitions.
330
+ - **`change-embedding-model`** probes the new provider, re-embeds every entity, rewrites `schema.json` and `config.md`. Nothing is written on failure.
331
+ - The live `memory.db` is gitignored and always rebuildable from `relations/*.ndjson` on store open. Orphan relations from interrupted drops/renames are reconciled automatically.
332
+
333
+ ### Further reading
334
+
335
+ - [docs/concepts.md](concepts.md) — the glossary entries for flow memory, entity/edge, permissions, embeddings.
336
+ - [docs/memory-maintenance.md](memory-maintenance.md) — Cozo 0.7 adaptations and session lifecycle constraints (contributor-facing).
337
+
338
+ ---
339
+
340
+ ## Next steps
341
+
342
+ - [docs/concepts.md](concepts.md) — concise glossary.
343
+ - [docs/work-spec.md](work-spec.md) — full WORK.md spec.
344
+ - [README.md](../README.md) — architecture, enforcement, design decisions.
345
+ - [CHANGELOG.md](../CHANGELOG.md) — version history.
@@ -0,0 +1,176 @@
1
+ # Flow memory — maintenance notes
2
+
3
+ Contributor-facing notes. This file records the derived details from the
4
+ Cozo docs / plugin surface that cost us time to establish. Add entries when a
5
+ fix required non-trivial spelunking, so the next maintainer can reuse the result.
6
+
7
+ ## Backend status (as of 3.0.0)
8
+
9
+ The memory subsystem persists to `cozo-node@0.7.6`, which wraps the Rust
10
+ `cozodb` engine. Both are effectively unmaintained:
11
+
12
+ - `cozo-node` has not been published since December 2023.
13
+ - `cozodb/cozo` has not received a commit since December 2024.
14
+
15
+ There are no known correctness or security issues in our usage — `pnpm
16
+ audit` reports clean. The only visible symptom is six `deprecated
17
+ subdependency` install warnings (all from `cozo-node →
18
+ @mapbox/node-pre-gyp@1`).
19
+
20
+ Foundry will migrate to a maintained graph + vector backend in a future
21
+ release. Candidates under evaluation include **Kùzu** (embedded
22
+ property-graph with HNSW, Cypher dialect), **SurrealDB embedded**,
23
+ **DuckDB + vss + PGQ**, and **SQLite + `sqlite-vec`** with hand-rolled
24
+ graph traversal. The migration is structured so the public memory tool
25
+ surface (`foundry_memory_*`) and the on-disk durable artefacts
26
+ (`foundry-memory/entities/*.md`, `foundry-memory/edges/*.md`,
27
+ `foundry-memory/relations/*.ndjson`) remain stable; only the live
28
+ in-process store changes.
29
+
30
+ When contributing to the memory module in the meantime, keep the cozo
31
+ coupling routed through `src/scripts/lib/memory/cozo.js` and
32
+ `src/scripts/lib/memory/store.js`. New consumers should depend on the
33
+ `store` interface, not on `cozo-node` directly.
34
+
35
+ ## Cozo 0.7 adaptations
36
+
37
+ ### `::compact` instead of `::checkpoint`
38
+
39
+ Older Cozo docs and the original spec reference `::checkpoint` for WAL
40
+ consolidation. In 0.7 the operation is spelled `::compact`. The
41
+ `foundry_memory_vacuum` admin tool and `openStore` reconciliation both use
42
+ `::compact`. See `src/scripts/lib/memory/admin/vacuum.js`.
43
+
44
+ ### Typed `<F32; N>?` vector columns
45
+
46
+ HNSW indices in Cozo 0.7 require the indexed column to be declared as a
47
+ typed, nullable vector: `vec: <F32; 768>?` (the trailing `?` makes the field
48
+ nullable so rows without an embedding do not block `:put`). An untyped column
49
+ produces a less helpful "expected vector" error when building the index.
50
+ `store.js:createEntityRelation` encodes the column shape from
51
+ `schema.embeddings.dimensions`.
52
+
53
+ ### `?[...] <- [[...]]` inline-put syntax
54
+
55
+ Cozo 0.7 dropped implicit positional binding in `:put`. The canonical spelling
56
+ is:
57
+
58
+ ```cozo
59
+ ?[name, value, vec] <- [["a", "v", null]]
60
+ :put ent_class { name, value, vec }
61
+ ```
62
+
63
+ i.e. bind a named tuple via `?[...] <- [[...]]` and then `:put` with an
64
+ explicit column map. See `src/scripts/lib/memory/writes.js` for the generator.
65
+
66
+ ### String literal syntax: single-quoted vs double-quoted
67
+
68
+ **This is a footgun.** Cozo 0.7 treats the two forms differently:
69
+
70
+ - `"..."` — **raw**. Does NOT honour backslash escapes. Embedding `"` inside
71
+ (even as `\"`) is a parse error.
72
+ - `'...'` — honours standard escapes (`\n`, `\r`, `\t`, `\\`, `\'`).
73
+
74
+ Any user-supplied value containing `"` would crash a raw-string literal.
75
+ Values with `\n` would round-trip as the literal two characters `\` and `n`.
76
+
77
+ **Always** use the single-quoted form for user data. `src/scripts/lib/memory/cozo.js`
78
+ exports `cozoStringLit(s)` as the canonical helper — it emits `'...'` with
79
+ escapes for `\`, `'`, `\n`, `\r`, `\t`. Do not introduce ad-hoc escape
80
+ helpers.
81
+
82
+ ### `::relations` lists HNSW index pseudo-relations
83
+
84
+ `::relations` returns not just the base relations Foundry created
85
+ (`ent_class`, `edge_calls`) but also their index entries
86
+ (`ent_class:vec`, `ent_class:vec:vec`, …). Any code that iterates relations
87
+ to reconcile against the expected set must filter:
88
+
89
+ ```js
90
+ const baseRelation = /^(ent|edge)_[^:]+$/;
91
+ ```
92
+
93
+ Dropping an HNSW-indexed relation also requires `::hnsw drop foo:vec` first
94
+ — `::remove foo` alone will leave the index metadata behind. See
95
+ `openStore`'s reconciliation loop.
96
+
97
+ ## Plugin / session lifecycle
98
+
99
+ ### Tools that may be first-call-of-session load config from disk
100
+
101
+ A tool that can be invoked before any memory read/write must not rely on
102
+ `context.store` or `context.config` being populated. The store singleton is
103
+ only constructed on first store-touching call, and plugin-level `context` is
104
+ only partially populated for tools that never needed a store before.
105
+
106
+ Canonical example: `foundry_memory_change_embedding_model`. If the user
107
+ invokes it as the first memory op of the session (common in the
108
+ `change-embedding-model` skill), `context.config` is `null` and any
109
+ `context.config.embeddings.*` access throws. The fix (commit `3147409`) loads
110
+ config fresh:
111
+
112
+ ```js
113
+ const io = makeMemoryIO(context.worktree);
114
+ const currentConfig = await loadMemoryConfig('foundry', io);
115
+ ```
116
+
117
+ Any new admin tool that (a) may be the first call of a session and (b) needs
118
+ config should follow the same pattern. Opening a store inside the tool
119
+ handler is fine; *reading through a possibly-uninitialised singleton* is not.
120
+
121
+ ## Runtime population via extractors
122
+
123
+ Beyond hand-authored `foundry-memory/relations/<type>.ndjson` seed data, flow memory can be populated at runtime by **extractors** — project-authored CLI scripts that emit JSONL describing entities and edges. An extractor runs inside the `assay` stage of a cycle that opts in via its frontmatter.
124
+
125
+ Extractors are defined at `foundry/memory/extractors/<name>.md` with a `command`, a `memory.write` scope, and a prose brief. Create them with the `add-extractor` skill; reference them from a cycle via `assay: { extractors: [name, ...] }`. This path is runtime population: extractor definitions live in config, while successful rows are flushed to the top-level `foundry-memory/relations/` data tree. See [docs/concepts.md](concepts.md#extractor) for the full spec.
126
+
127
+ ## Memory layout: two trees
128
+
129
+ Since Phase 2 (3.0.0), memory is split across two top-level trees:
130
+
131
+ - `foundry/memory/` — *config*. Holds `config.md`, `schema.json`,
132
+ `entities/<name>.md`, `edges/<name>.md`, `extractors/<name>.md`,
133
+ and the `memory.db*` runtime files (gitignored). Authored on
134
+ `config/*` branches via the schema-mutation tools.
135
+ - `foundry-memory/relations/` — *row data*. Top-level sibling of
136
+ `foundry/`, holding `<name>.ndjson` files. Tracked in git (the
137
+ source of truth for memory rows). Written by `foundry_stage_end`
138
+ flushing in-cycle puts, by `foundry_assay_run` flushing extractor
139
+ output, by direct out-of-cycle `foundry_memory_put` /
140
+ `foundry_memory_relate` / `foundry_memory_unrelate` calls, or by
141
+ hand-authored seed data.
142
+
143
+ Source of truth: `src/scripts/lib/memory/paths.js`, which threads
144
+ `foundryDir` through the `foundry/memory/` config tree but pins
145
+ `relationsDir` at the literal `'foundry-memory/relations'`.
146
+
147
+ When sweeping memory paths in maintenance scripts, treat the two
148
+ trees as separate: `foundry/memory/` for config, `foundry-memory/`
149
+ for data. Operations that touch both (init, drop, rename, reset,
150
+ embedding-model swap) stage paths under both prefixes in the same
151
+ commit.
152
+
153
+ ## Failed-flow guard on memory admin tools
154
+
155
+ Every mutating memory tool — both data writes (`foundry_memory_put`,
156
+ `foundry_memory_relate`, `foundry_memory_unrelate`) and admin ops
157
+ (`foundry_memory_init`, `foundry_memory_reset`, `foundry_memory_vacuum`,
158
+ `foundry_memory_change_embedding_model`,
159
+ `foundry_memory_create_entity_type` / `_create_edge_type`,
160
+ `foundry_memory_rename_entity_type` / `_rename_edge_type`,
161
+ `foundry_memory_drop_entity_type` / `_drop_edge_type`,
162
+ `foundry_extractor_create`) — refuses to run when `WORK.md`
163
+ frontmatter has `status: failed`. Each tool returns a tool-name-prefixed
164
+ error referencing the failure reason.
165
+
166
+ This is by design: the failed-flow state locks mutating tools until the
167
+ failure is handled, and admin operations on memory while a flow is in an
168
+ unrecoverable state risk compounding the damage. Read-only diagnostics
169
+ (`foundry_memory_get`, `_list`, `_neighbours`, `_query`, `_search`,
170
+ `_dump`, `_validate`) remain callable so the operator can investigate.
171
+
172
+ The supported recovery paths: read the failure reason via
173
+ `foundry_workfile_get`, fix the root cause, then either call
174
+ `foundry_stage_retry()` to clear the failed state and re-run the blocked
175
+ stage, or abandon the cycle with `foundry_workfile_delete({ confirm: true })`.
176
+ See `src/scripts/lib/failed-flow.js` and [architecture.md](architecture.md#failed-flow-state) for the full contract.