Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "allium",
"version": "3.12.0",
"version": "3.13.0",
"description": "Velocity through clarity.",
"author": {
"name": "JUXT",
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "allium",
"version": "3.12.0",
"version": "3.13.0",
"description": "Velocity through clarity.",
"author": {
"name": "JUXT",
Expand Down
15 changes: 14 additions & 1 deletion .github/agents/distill.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "Extract an Allium specification from an existing codebase. Use whe

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Scope the distillation from the goal you were given, record unconfirmed judgement calls as `open question` declarations in the distilled spec, and list the parked questions in your final output.

Reading the source code is your job precisely so it stays out of the caller's context: return the distilled spec's path, a short summary of what it covers, and the parked questions — not the code you read.
Reading the source code is your job precisely so it stays out of the caller's context. Return your result as a single JSON object conforming to the distill-result schema (see the skill's "Typed result" section) and nothing else — the spec path, a one-line summary of what it covers, and the parked questions as fields. Not the code you read, and no prose around the object.

# Distillation guide

Expand Down Expand Up @@ -843,6 +843,19 @@ If any remain, ask: "Would a stakeholder include this in a requirements doc?"

The extracted spec is a starting point. If distillation reveals gaps that need structured discovery (unclear requirements, complex entity relationships, unstated business rules), use the `elicit` skill to fill them. For targeted changes as requirements evolve, use the `tend` skill. For checking ongoing alignment between the spec and implementation, use the `weed` skill.

## Typed result (loop hand-off)

When running as the `distill` subagent inside the Allium loop, return your result as a single JSON object conforming to [distill-result.schema.json](../../skills/allium/references/schemas/distill-result.schema.json), and nothing else: the `spec_path`, the parked `open_questions`, and a one-line `summary` of what the spec covers. Emit every field, using `[]` for empty lists. The source you read stays out of the caller's context; only these fields come back. Running interactively, present your findings in prose as usual — the typed record is for the machine hand-off, not the conversation.

```json
{
"phase": "distill",
"spec_path": "giftcard.allium",
"open_questions": ["Is forcing an over-redeemed balance to zero intended or accidental?"],
"summary": "GiftCard redemption and status lifecycle"
}
```

## References

- [Language reference](../../skills/allium/references/language-reference.md), full Allium syntax
Expand Down
18 changes: 17 additions & 1 deletion .github/agents/propagate.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "Generate tests from Allium specifications. Use when the user wants

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Report anything that needs a human decision in your final output and continue with the work that does not depend on it. You have full shell access because obligation reconciliation requires running the project's test command; use it for the allium CLI and test runs, not for modifying implementation code — implementation belongs to the loop's implement phase, not to you.

Return the generated test file paths, the reconciliation summary line (`N obligations, M covered, K uncovered`), and any uncovered obligations with their classification — not the file contents.
Return your result as a single JSON object conforming to the propagate-result schema (see the skill's "Typed result" section) and nothing else — the reconciliation counts, the uncovered obligations with their classification, and the generated tests with their hashes, all as fields. Not the file contents, and no prose around the object.

# Propagation

Expand Down Expand Up @@ -246,6 +246,22 @@ When running inside the Allium loop, record the reconciliation baseline so the l

The hash is the ground truth the `witness` skill re-derives: a generated test whose hash changes with no intervening propagate run is a hand-edited test — the anti-cheat violation the loop must never reach convergence with. Recording the baseline is what makes that check possible; skip it and the witness can confirm the tests pass but not that they were not weakened. This is cheap bookkeeping, not a report — do not narrate it.

### Typed result (loop hand-off)

When running as the `propagate` subagent inside the Allium loop, return your result as a single JSON object conforming to [propagate-result.schema.json](../../skills/allium/references/schemas/propagate-result.schema.json), and nothing else. The loop routes on the structured fields: `obligations` and `uncovered_obligations` decide whether coverage is complete (the loop must not converge while `uncovered_obligations` is non-empty), and `generated_tests` carries the path-and-hash baseline the witness re-derives — the same hashes recorded above, now first-class in the hand-off. Keep the `summary` field to the `N obligations, M covered, K uncovered` line. Emit every field, using `[]` for empty lists — do not omit them. Running interactively, the summary line remains the whole user-facing output as before — the typed record is for the machine hand-off, not the conversation.

```json
{
"phase": "propagate",
"obligations": { "total": 12, "covered": 12, "uncovered": 0 },
"uncovered_obligations": [],
"generated_tests": [{ "path": "order.test.js", "hash": "sha256:9f2c…" }],
"test_paths": ["order.test.js"],
"open_questions": [],
"summary": "12 obligations, 12 covered, 0 uncovered"
}
```

## Interaction with other tools

- **distill** produces specs from code. Those specs feed propagate.
Expand Down
18 changes: 17 additions & 1 deletion .github/agents/tend.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ name: tend
description: "Tend the Allium garden. Use when the user wants to write, edit, update, add to, improve, clarify, refine, restructure, fix or migrate Allium specs. Covers adding entities, rules, triggers, surfaces and contracts, fixing syntax or validation errors, renaming or refactoring within specs, migrating specs to a new language version, and translating requirements into well-formed specifications. Pushes back on vague requirements."
---

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Record anything that needs a human decision as an `open question` declaration in the spec, continue with the work that does not depend on it, and list the parked questions in your final output.
Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Record anything that needs a human decision as an `open question` declaration in the spec and continue with the work that does not depend on it.

Return your result as a single JSON object conforming to the tend-result schema (see the skill's "Typed result" section) and nothing else — the spec path, the changes made, and the parked questions as fields. No prose around the object.

# Tend

Expand Down Expand Up @@ -106,3 +108,17 @@ After edits that change rules, surfaces or transition graphs, run `allium analys
## Output

When proposing spec changes, explain the behavioural intent first, then show the changes. If you have questions or concerns about the request, raise them before writing anything.

### Typed result (loop hand-off)

When running as the `tend` subagent inside the Allium loop, return your result as a single JSON object conforming to [tend-result.schema.json](../../skills/allium/references/schemas/tend-result.schema.json), and nothing else: the `spec_path`, the `changes` you made (each a short string, not an object), the parked `open_questions`, and a one-line `summary`. Emit every field, using `[]` for empty lists. Running interactively, present the intent-then-changes prose above as usual — the typed record is for the machine hand-off, not the conversation.

```json
{
"phase": "tend",
"spec_path": "shop.allium",
"changes": ["Added expiry field to GiftCard", "Added GiftCardExpires temporal rule"],
"open_questions": ["Expiry period undecided"],
"summary": "Added gift card expiry behaviour"
}
```
22 changes: 21 additions & 1 deletion .github/agents/weed.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ name: weed
description: "Weed the Allium garden. Find where Allium specifications and implementation code have diverged, and help resolve the divergences. Use when the user wants to check spec-code alignment, compare specs against implementation, audit for spec drift or violations, sync specs with code or code with specs, or verify whether the implementation matches what the spec says."
---

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Report anything that needs a human decision as an open finding in your output (and, when updating the spec, as an `open question` declaration), then continue with the work that does not depend on it.
Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Report anything that needs a human decision as an open finding (and, when updating the spec, as an `open question` declaration), then continue with the work that does not depend on it.

Return your result as a single JSON object conforming to the weed-result schema (see the skill's "Typed result" section) and nothing else — the loop routes on its fields, not on prose.

# Weed

Expand Down Expand Up @@ -113,3 +115,21 @@ Classification: [proposed classification with reasoning]
```

Group related divergences together. Lead with the most consequential findings.

### Typed result (loop hand-off)

When running as the `weed` subagent inside the Allium loop, return your result as a single JSON object conforming to [weed-result.schema.json](../../skills/allium/references/schemas/weed-result.schema.json), and nothing else. The loop routes on the structured fields (`verdict`, each divergence's `classification`, `open_questions`) rather than parsing prose, so the routing and the convergence check stay deterministic. Keep the `summary` field to the one human-readable line; put the detail in the structured fields. Emit every field, using `[]` for empty lists. Running interactively, present the prose format above as before — the typed record is for the machine hand-off, not the conversation.

```json
{
"phase": "weed",
"mode": "check",
"verdict": "dirty",
"divergences": [
{ "subject": "Order.cancel", "classification": "code-bug", "spec": "cancel allowed from paid (shop.allium:42)", "code": "guarded to pending only (order.py:88)" }
],
"open_questions": [],
"artefacts": [],
"summary": "1 divergence: Order.cancel (code-bug)"
}
```
26 changes: 23 additions & 3 deletions .github/agents/witness.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "Independently witness that an Allium loop's convergence claim is t

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Write the witness record, then report the verdict and every violation with its routing in your final output and continue — the caller acts on them. You have full shell access because independent verification requires re-running the project's test command, hashing the generated tests, and running the allium CLI; use it to re-derive the deterministic checks and read ground truth, never to modify the spec, the tests, or the code. Write only the witness record — everything else you read, hash or re-run.

Return the verdict, the one-line witness summary, every violation with its routing, and the record's path — not the file contents or the code you read.
Return your result as a single JSON object conforming to the witness-result schema (see the skill's "Output format" section) and nothing else — the verdict, each check with the ground truth it read, every violation with its routing, and the record path as fields. Not the file contents or the code you read, and no prose around the object.

# Witness

Expand Down Expand Up @@ -75,13 +75,33 @@ Do not embed file contents or code — the record holds verdicts and the evidenc

## Output format

Close with a single summary line the loop can fold into its report:
When running as the `witness` subagent inside the Allium loop, return your result as a single JSON object conforming to [witness-result.schema.json](../../skills/allium/references/schemas/witness-result.schema.json), and nothing else: the `verdict`, each `check` with the `ground_truth` it read, every `violation` with its `routing`, the `record_path`, and a one-line `summary`. Emit every field, using `[]` for an empty `violations` list on a PASS. The loop gates convergence on `verdict` directly — no prose to parse. The object mirrors the durable record you wrote to `.allium-loop/<slug>.witness.json`.

```json
{
"phase": "witness",
"verdict": "FAIL",
"checks": [
{ "name": "tests-pass", "result": "pass", "ground_truth": "runner exit 0, 12/12" },
{ "name": "no-test-weakened", "result": "fail", "ground_truth": "sha256 mismatch on order.test.js" }
],
"violations": [
{ "violation": "order.test.js edited after propagate", "routing": "revert + propagate" }
],
"record_path": ".allium-loop/gift-cards.witness.json",
"summary": "witness: FAIL · tampering on order.test.js"
}
```

As the loop subagent, return **only** that JSON object — no prose before or after it, even though you also wrote the durable record to disk. The returned object is your result; the file is its durable copy.

Running interactively (not as the loop subagent), skip the JSON and close with a single human-readable summary line instead:

```
witness: PASS · checks 6/6 · tests 12/12 (runner) · tampering none · openQ 0 blocking · record .allium-loop/<slug>.witness.json
```

On failure, lead with the verdict and the violations, each with its routing, then the record path. Keep the body to the verdict and its evidence — the record holds the detail.
On an interactive failure, lead with the verdict and the violations, each with its routing, then the record path. Keep the body to the verdict and its evidence — the record holds the detail.

## Interaction with other tools

Expand Down
2 changes: 1 addition & 1 deletion agents/distill.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ You are the non-interactive entry point for the `distill` skill, whose content i

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Scope the distillation from the goal you were given, record unconfirmed judgement calls as `open question` declarations in the distilled spec, and list the parked questions in your final output.

Reading the source code is your job precisely so it stays out of the caller's context: return the distilled spec's path, a short summary of what it covers, and the parked questions — not the code you read.
Reading the source code is your job precisely so it stays out of the caller's context. Return your result as a single JSON object conforming to the distill-result schema (see the skill's "Typed result" section) and nothing else — the spec path, a one-line summary of what it covers, and the parked questions as fields. Not the code you read, and no prose around the object.
2 changes: 1 addition & 1 deletion agents/propagate.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ You are the non-interactive entry point for the `propagate` skill, whose content

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Report anything that needs a human decision in your final output and continue with the work that does not depend on it. You have full Bash access because obligation reconciliation requires running the project's test command; use it for the allium CLI and test runs, not for modifying implementation code — implementation belongs to the loop's implement phase, not to you.

Return the generated test file paths, the reconciliation summary line (`N obligations, M covered, K uncovered`), and any uncovered obligations with their classification — not the file contents.
Return your result as a single JSON object conforming to the propagate-result schema (see the skill's "Typed result" section) and nothing else — the reconciliation counts, the uncovered obligations with their classification, and the generated tests with their hashes, all as fields. Not the file contents, and no prose around the object.
4 changes: 3 additions & 1 deletion agents/tend.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ skills:

You are the non-interactive entry point for the `tend` skill, whose content is preloaded above. If it is not present, read `${CLAUDE_PLUGIN_ROOT}/skills/tend/SKILL.md` and follow it. Relative file references in the skill resolve from that directory.

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Record anything that needs a human decision as an `open question` declaration in the spec, continue with the work that does not depend on it, and list the parked questions in your final output.
Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Record anything that needs a human decision as an `open question` declaration in the spec and continue with the work that does not depend on it.

Return your result as a single JSON object conforming to the tend-result schema (see the skill's "Typed result" section) and nothing else — the spec path, the changes made, and the parked questions as fields. No prose around the object.
4 changes: 3 additions & 1 deletion agents/weed.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ skills:

You are the non-interactive entry point for the `weed` skill, whose content is preloaded above. If it is not present, read `${CLAUDE_PLUGIN_ROOT}/skills/weed/SKILL.md` and follow it. Relative file references in the skill resolve from that directory.

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Report anything that needs a human decision as an open finding in your output (and, when updating the spec, as an `open question` declaration), then continue with the work that does not depend on it.
Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Report anything that needs a human decision as an open finding (and, when updating the spec, as an `open question` declaration), then continue with the work that does not depend on it.

Return your result as a single JSON object conforming to the weed-result schema (see the skill's "Typed result" section) and nothing else — the loop routes on its fields, not on prose.
2 changes: 1 addition & 1 deletion agents/witness.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ You are the non-interactive entry point for the `witness` skill, whose content i

Operate in the skill's non-interactive mode: no user is reachable, so never wait for an answer. Write the witness record, then report the verdict and every violation with its routing in your final output and continue — the caller acts on them. You have full Bash access because independent verification requires re-running the project's test command, hashing the generated tests, and running the allium CLI; use it to re-derive the deterministic checks and read ground truth, never to modify the spec, the tests, or the code. Write only the witness record — everything else you read, hash or re-run.

Return the verdict, the one-line witness summary, every violation with its routing, and the record's path — not the file contents or the code you read.
Return your result as a single JSON object conforming to the witness-result schema (see the skill's "Output format" section) and nothing else — the verdict, each check with the ground truth it read, every violation with its routing, and the record path as fields. Not the file contents or the code you read, and no prose around the object.
Loading