Fanning a large goal out into one distill (or one loop) per service is the map.
The reduce -- assembling the slices and driving the seams between them to
convergence -- was only a hand-wave in driving-the-loop §6: "run a whole-spec
integration pass". Slices distilled in isolation don't agree at their seams: the
same entity defined twice, a rule that depends on a trigger another slice was
meant to emit, a contract demanded with nothing to fulfil it.
Turn §6 into a concrete reduce: assemble and wire the slices (canonical owner
for a shared entity, use imports), cross-check the whole set with a single
`allium analyse` call (now cross-module aware, so it reasons across use seams and
returns the seams that don't line up), route each finding through the existing
taxonomy to tend/weed, then cross-service propagate and a final witness. The CLI
does the seeing and the existing phase agents do the edits, so the orchestrator
holds slice paths and JSON, never slice bodies -- and no new agent is added.
- New reference integrating-slices.md: the seam detail (canonical entity
ownership, use wiring, contract matching, which findings signal a broken seam).
- driving-the-loop §6 rewritten as the four-step reduce, linking it.
- actioning-findings gains the cross-seam framing and documents
allium.reference.unknownName as the seam signal to read from diagnostics.
- test-skills: a reduce group pinning the procedure's constructs and links.
## Why
When the loop drives a big goal it can run for many ticks. If it stops converging, with tests not improving and weed still dirty, there is no signal until it hits an iteration cap. The loop now records each tick and warns as soon as it stalls, instead of at the cap.
## What it does
Each tick it appends a line to a trace file with the test counts, the weed verdict, the open questions, and which subagent ran and why. If a tick improves none of those, it flags a stall rather than waiting for the cap. A hook records how long each subagent call took. The final report shows the trace.
## Implementation
- One trace entry per tick in `.allium-loop/<slug>.trace.jsonl`, built from the phases' typed records.
- The stall rule is simple counting over the trace, so the model applies it directly. The threshold is a config value.
- A hook times each subagent call into `.allium-loop/timings.jsonl`. It runs where hooks are supported and does nothing elsewhere.
- Each entry records which phase ran and why, so a wasted call is visible.
## Tests
- Offline: trace entries validate, malformed ones are caught, and the stall rule is proven over trajectories.
- The hook has unit tests in CI, plus a live check that spawns a real subagent and confirms a timing line lands.
## Potential next steps
The model applies the stall rule today, with the deterministic version pinned in the tests. It could move into a script or the CLI later, which would also be the natural home for the timing.
## Why
The loop runs each phase in its own subagent. Each phase hands back a result. Until now that result was prose, so the loop had to read it and work out what it meant. This makes the result typed. The loop reads a field instead of reading a paragraph. The routing and the done check get simpler and easier to trust, with less room to misread a result. It also lays the groundwork for moving these checks into the CLI later, where code can run them instead of the model.
## What it does
Every phase now returns a small JSON record instead of prose. weed returns a verdict and its divergences. propagate returns the coverage counts. The loop reads those fields to decide what to do next. A clean verdict moves on, a code bug goes back to the code, and whether the loop is done becomes a plain check over the fields.
## Implementation
- Six schemas: one per phase result, plus the ledger.
- Each skill returns its record, with a worked example to follow.
- The routing table and the convergence check read the typed fields.
## Tests
- The test suite has a small dependency-free validator: valid records pass, malformed ones are caught, and convergence is proven as a function of the fields.
- A live test spawns each real agent and confirms its output matches its schema.
## Potential next steps
Right now the loop reads the records itself, and the validator that checks them lives in the tests, not the live loop. A possible next step is to run that check inside the loop too, first as a small script the loop calls on each hand-off, and later as part of the CLI, alongside the routing and the done check. That would be the point where these checks stop being the model's judgement and become code.
## Why
The loop keeps getting stronger. 3.10 moved each phase into its own subagent. 3.11 fanned distill out across services. Each phase runs isolated and hands back a short result. This is the next step. The loop should be able to stand behind that result and prove it. Witness gives it that. It confirms the run's own answer against ground truth, and it leaves a record the loop can show for it.
## What it does
This adds a new phase called witness. It runs at the convergence gate in its own subagent. It rebuilds the result from ground truth the run cannot fake. It reads the test runner's own exit status. It hashes each generated test and compares it to the hash propagate recorded. It reads the reconciliation line and the weed verdict. It checks the open questions. If any of that was faked, the witness fails and the loop keeps working. The loop converges only when the witness passes.
It stays cheap. It re-runs the fast deterministic tools and reads output the phases already produced. It never re-runs propagate, distill, or weed. So it costs one light pass per run.
## Implementation
- New witness skill and agent. Same dual entry as the other phases.
- Propagate records the generated test hashes in the ledger. That is the baseline the witness checks.
- The loop docs gate convergence on the witness verdict.
- Witness writes a record to `.allium-loop/<slug>.witness.json`.
- Tests cover it, including a live probe that weakens a test and confirms the witness catches it.
- Version bump to 3.12.0.
* distil: fan-out orchestration to keep source out of the main context
For a large codebase, map it into bounded contexts and dispatch one subagent
per context that reads only its slice and returns distilled fragments, never
raw source. The orchestrator assembles the fragments and never holds the whole
codebase in one window. Small codebases still run inline.
* Bump plugin version to 3.11.0
Make per-phase sub-agent delegation the default when /allium drives the loop
(driving-the-loop.md §7). The orchestrator stays thin -- it holds only the spec
path, the divergence ledger and short phase summaries, reads no source itself,
delegates each phase (distill/weed/tend) to its agent and forwards only on-disk
artefacts -- so its context stays flat while each phase reads in a disposable
context. Adds an inline-for-small heuristic (a cold sub-agent's overhead isn't
worth it on tiny scopes) and by-name invocation for deterministic routing.
Builds on the dual-entry agents change (#63), which shipped the agents but left
nothing driving them by default. On a large codebase the delegated loop is both
bounded and cheaper than the inline loop.
Claude-Session: https://claude.ai/code/session_01R5VhSn12Pk4CXo2YT986Jm
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make each skill the single source of truth; Claude Code agents become thin preload shells. Add distill and propagate agents for per-phase loop isolation. Generate VS Code agents from the canonical skills. Pin the interaction-mode contract offline and add a live parking probe.
The canonical site moved from juxt.github.io/allium to
allium-lang.org. Update the README link and the homepage/websiteURL
fields in both plugin manifests. Metadata only, no version bump.
Claude-Session: https://claude.ai/code/session_01D6FTVnDrei6nNse624pG3w
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Add an explicit reconciliation step to the propagate process: after
generating tests, walk the full obligation list from `allium plan` and
map each obligation to a covering test. Uncovered obligations are
auto-covered in a guarded mini-loop (2 attempts per obligation plus a
no-progress cap, mirroring the outer loop's convergence rules).
Reconciliation is silent on the happy path — the user sees only a
one-line summary and any obligations that could not be covered, each
classified as an infrastructure gap or unmappable construct. Missing
implementation is explicitly not a residue category: in spec-first
flows a written-but-failing test covers its obligation.
Claude-Session: https://claude.ai/code/session_01D6FTVnDrei6nNse624pG3w
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
/allium now drives the whole spec→tests→code loop to convergence. The separate /allium:loop command and standalone loop skill are removed — folded into the allium entry-point skill, with the driver procedure in skills/allium/references/driving-the-loop.md. Minor release 3.6.0 → 3.7.0.
Frame Allium's skills as an agentic loop — gather context → take action →
verify → repeat — and position the spec+tests+weed signal as the
verification phase that makes a self-driving loop trustworthy.
- New reference skills/allium/references/recommended-loops.md with the
loop framing, the convergence invariant (whole-loop, two feedback arcs),
spec-first (Loop A) and code-first (Loop B) walkthroughs, the red-step
before implementation, autonomous-run guardrails, and the implement prompt.
- README: prominent "The Allium loop" section after "How it works".
- allium router SKILL.md: recommended-sequencing note so the LLM suggests
the next phase, plus the reference in the References list.
- Bump both plugin manifests 3.5.0 -> 3.6.0 (minor: additive docs/guidance).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Minor bump for the CLI install notice added in #51: new backward-compatible
behaviour (first-run install prompt, install offer, marker files), no breaking
changes.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The post-write hook previously exited silently when the `allium` binary
wasn't on PATH, so a user could edit specs indefinitely with no formal
checking and no hint the CLI existed. On the first .allium edit with no
CLI present, the hook now surfaces a one-time notice explaining what the
CLI adds and instructing the model to offer to install it (with the
platform-appropriate command and a single confirmation).
A "shown once" marker suppresses repeats: the per-machine cache dir
first, falling back to a `.allium-cli-notice-shown` file in the project
root when the cache isn't writable. Only when neither is writable does
the notice recur, in which case it says so and hands off to manual
installation. The notice never fires for non-spec or out-of-root edits,
and self-disables once the binary is on PATH.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Minor bump for new backwards-compatible features added since v3.3.0:
LSP server wiring via lspServers (#48) and Codex support / .codex-plugin
manifest (#41). Updates both the Claude and Codex plugin manifests.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Wire allium-lsp in plugin.json for live .allium diagnostics (#39)
Add an lspServers entry so the Claude Code plugin connects the
allium-lsp language server to .allium files, giving Claude checker
diagnostics, go-to-definition and hover immediately after each edit
without a separate `allium check` invocation.
Note in the README that allium-lsp is not bundled and must be
installed separately (and be on PATH), consistent with official
marketplace LSP plugins.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix allium-lsp invocation and install note
The language server calls createConnection(ProposedFeatures.all), which
requires a transport flag on argv, so it must be launched as
`allium-lsp --stdio` (per allium-tools CONTRIBUTING). Add args: ["--stdio"]
to the lspServers entry; without it the server errors on startup.
allium-lsp is not published to npm (the bare and @juxt-scoped names both
404), so drop the `npm install -g allium-lsp` instruction and point to the
allium-tools repo for installing the server onto PATH instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Document zero-argument contract signatures
Note that contract operations can take an empty parameter list,
`name: () -> ReturnType`, for parameterless operations (list-all, fetch
global state, health checks). Mirrors the v3 language reference in
allium-tools and matches the parser fix in juxt/allium-tools#55.
Refs #37
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Wire allium-lsp in plugin.json for live .allium diagnostics (#39)
Add an lspServers entry so the Claude Code plugin connects the
allium-lsp language server to .allium files, giving Claude checker
diagnostics, go-to-definition and hover immediately after each edit
without a separate `allium check` invocation.
Note in the README that allium-lsp is not bundled and must be
installed separately (and be on PATH), consistent with official
marketplace LSP plugins.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix allium-lsp invocation and install note
The language server calls createConnection(ProposedFeatures.all), which
requires a transport flag on argv, so it must be launched as
`allium-lsp --stdio` (per allium-tools CONTRIBUTING). Add args: ["--stdio"]
to the lspServers entry; without it the server errors on startup.
allium-lsp is not published to npm (the bare and @juxt-scoped names both
404), so drop the `npm install -g allium-lsp` instruction and point to the
allium-tools repo for installing the server onto PATH instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce the spec-as-primary-artefact mental model and the
elicit/distill -> tend/weed -> propagate loop, with an ASCII diagram,
before the install instructions. Links to the existing skills table
rather than duplicating it.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a worked example under Pattern 9 showing the common "my spec is a
client that calls an external typed API" case: a module-level contract
referenced from the caller's surface via `contracts: demands`. Includes
a direction-marker note contrasting `demands` (call out / counterpart
implements), `fulfils` (supply the API), and `provides` (user fires a
domain rule).
Also add a redirect note on Pattern 6's APIAccess surface — where readers
get misdirected toward `provides:` — and surface the keyword in the
summary table for discoverability.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds validation rule 24b: every field in a `default` object literal must be
declared on the named entity/value type, recursing into nested object literals,
so field renames/removals surface as drift errors at check time. Complements the
existing 24a (qualified-type schema resolution) and 14c (empty-list element
type). Implemented in juxt/allium-tools#46.
Refs #43
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Specifies the two language features tracked in #43:
- `[...]` list literals: a general expression-position literal producing
`List<T>` (distinct from `Set` braces and inferred `Sequence`). Elements must
share a type; an empty `[]` takes its element type from the target field;
duplicates are retained. List literals are the only way to populate a
`List<T>` field from the spec layer.
- Qualified type names in `default` declarations (`default alias/Type x = ...`),
resolved against the imported module's entity so drift is caught at check time.
Adds validation rules 14b–14d and 24a.
Refs #43
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>