@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.
- package/README.md +200 -198
- 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 +490 -0
- package/dist/LICENSE +21 -0
- package/dist/README.md +278 -0
- package/dist/docs/README.md +59 -0
- package/dist/docs/architecture.md +434 -0
- package/dist/docs/concepts.md +396 -0
- package/dist/docs/getting-started.md +345 -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 +393 -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 +42 -6
- package/{skills → dist/skills}/add-artefact-type/SKILL.md +49 -21
- package/{skills → dist/skills}/add-cycle/SKILL.md +60 -14
- package/dist/skills/add-extractor/SKILL.md +133 -0
- package/{skills → dist/skills}/add-flow/SKILL.md +39 -7
- 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 +192 -0
- package/package.json +34 -17
- package/.opencode/plugins/foundry.js +0 -761
- package/CHANGELOG.md +0 -90
- package/docs/concepts.md +0 -59
- package/docs/getting-started.md +0 -78
- package/docs/work-spec.md +0 -193
- 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 -105
- 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,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.
|