@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,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.
|