Skip to content
Open
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ md/*
# okf output folder
okf/*
!okf/
!okf/.okflintrc.json
!okf/.okflintrc.json
# consumption-test transcripts (generated)
tests/house-style/out/
138 changes: 138 additions & 0 deletions docs/superpowers/specs/2026-07-31-okf-consumption-test-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# OKF consumption test — design

**Date:** 2026-07-31
**Status:** approved, for implementation

## Problem

`md2okf` is verified end to end as a *producer*: a 382 KB book compiles into a
38-page OKF wiki that passes `okf-lint`. Nothing verifies the wiki is any
*use*. Completeness and well-formedness are not usefulness, and OKF's own goals
name consumption explicitly:

> Inform how **consumption agents** should read and traverse it.
> — `SPEC.md`, Goals §2

So: can an agent holding only the compiled wiki apply the knowledge in it, and
can we tell the difference between it having done so and it having guessed?

## The prior-knowledge problem

This is the crux. A capable model already knows roughly what Economist house
style is. Hand it a badly written paragraph and a wiki, and a good rewrite
proves nothing — it may never have opened a page.

Two mitigations, both required:

1. **Arbitrary rulings.** Cases are built on house rulings a model cannot
derive from general knowledge, several of which invert the common
convention. The strongest is `%`: most style guides prefer spelling out
"per cent", and this one requires the sign. A model working from priors gets
it backwards.
2. **Verified citations.** Every change must cite the page it came from, and
grading checks the cited page *actually says so*. A plausible citation to a
page that does not support the claim fails.

There is deliberately **no A/B control arm** (running each case again with the
wiki absent). It would be the cleanest evidence of contribution, but it doubles
the runs, and the counterintuitive rulings already give the test the ability to
fail when the wiki is unread. Recorded as a known limitation, not an oversight.

## Isolation

A second Compose service, `pi-consume`, reusing the existing image:

| Mount | Compile run | Consumption run |
| --- | --- | --- |
| `md/` | read-only | **absent** |
| `okf/` | read-write | **read-only** |

`md/` is unmounted so the agent cannot reach the source book — only the compiled
wiki. `okf/` is read-only because consumption must not mutate the artefact under
test; it also means the run needs no writable mount at all.

The driver passes `--provider` and `--model` explicitly rather than editing
`settings.json`, so the test states which model it exercised and leaves repo
config untouched.

## Output contract

The agent writes nothing. Its final message is the result, captured from
`pi -p` stdout, and must end with a fenced `json` block:

```json
{
"rewrite": "the corrected paragraph",
"changes": [
{
"before": "per cent",
"after": "%",
"ruling": "Use the sign % instead of per cent.",
"citation": "/part-2/7-sweating-the-small-stuff-punctuation-mechanics-and-conventions.md"
}
]
}
```

The grader reads the **last** fenced `json` block, so surrounding prose is
harmless. A missing or unparseable block fails the case explicitly rather than
scoring zero silently.

## Grading

Three mechanical assertions per case. No LLM judge — every check is a string or
filesystem operation, so the result is deterministic and reproducible.

1. **applied** — every `expect_present` string appears in `rewrite`, and every
`expect_absent` string does not.
2. **cited** — at least one `changes[].citation` resolves to a file that exists
in the bundle.
3. **grounded** — that cited file contains the case's `grounding` text.

A case passes only when all three hold. Assertion 3 is what makes hallucinated
citations fail: getting the edit right by luck and inventing a source still
fails the case.

## Cases

All eight are verified present in the compiled wiki. `ch6` is
`/part-2/6-confusables-and-cuttables-individual-rulings.md`.

| # | Ruling | Page | Discriminates because |
| --- | --- | --- | --- |
| 1 | `%` not "per cent" | ch7 | inverts the usual convention |
| 2 | do not verb "impact" | ch9 | house prohibition, not grammar |
| 3 | "due to" modifies nouns only | ch6 | prefers *because of* / *owing to* |
| 4 | "decimate" = a significant proportion | ch6 | not total destruction |
| 5 | "fewer" for countables | ch6 | common error, explicit ruling |
| 6 | no sentence-initial "Hopefully," | ch6 | explicitly *not* a grammar rule |
| 7 | "alibi" ≠ excuse | ch6 | semantic ruling |
| 8 | "compared with" for evaluation | ch6 | fine distinction against *compared to* |

## Failure taxonomy

Infrastructure failure must never read as "the wiki is bad". The driver exits:

- `0` — all cases passed
- `1` — at least one case failed on its assertions (a real result)
- `2` — infrastructure trouble: missing key, gateway error, no wiki, no cases

## Components

| Path | Purpose |
| --- | --- |
| `pi/{container,sandbox}/…/skills/apply-house-style/SKILL.md` | the consumption task, one copy per runtime, aligned by hand |
| `tests/house-style/cases.yaml` | fixtures: input, assertions, expected page, grounding |
| `tests/house-style/grade.py` | the three assertions, table output, exit code |
| `scripts/test-house-style.sh` | driver: one Pi run per case, then grade |
| `pi/container/compose.yaml` | adds the `pi-consume` service |
Comment on lines +120 to +128

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix stale fixture-file extension in the Components table.

Line 125 lists tests/house-style/cases.yaml. The actual fixtures file added in this PR is tests/house-style/cases.json. A reader following this table to find or edit fixtures looks for the wrong file.

📝 Proposed fix
-| `tests/house-style/cases.yaml` | fixtures: input, assertions, expected page, grounding |
+| `tests/house-style/cases.json` | fixtures: input, assertions, expected page, grounding |

As per path instructions, "flag commands, paths, options, and examples that this change has made wrong or stale."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## Components
| Path | Purpose |
| --- | --- |
| `pi/{container,sandbox}/…/skills/apply-house-style/SKILL.md` | the consumption task, one copy per runtime, aligned by hand |
| `tests/house-style/cases.yaml` | fixtures: input, assertions, expected page, grounding |
| `tests/house-style/grade.py` | the three assertions, table output, exit code |
| `scripts/test-house-style.sh` | driver: one Pi run per case, then grade |
| `pi/container/compose.yaml` | adds the `pi-consume` service |
## Components
| Path | Purpose |
| --- | --- |
| `pi/{container,sandbox}/…/skills/apply-house-style/SKILL.md` | the consumption task, one copy per runtime, aligned by hand |
| `tests/house-style/cases.json` | fixtures: input, assertions, expected page, grounding |
| `tests/house-style/grade.py` | the three assertions, table output, exit code |
| `scripts/test-house-style.sh` | driver: one Pi run per case, then grade |
| `pi/container/compose.yaml` | adds the `pi-consume` service |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-07-31-okf-consumption-test-design.md` around
lines 120 - 128, Update the Components table entry for the house-style fixtures
to reference cases.json instead of cases.yaml, leaving the surrounding component
paths and descriptions unchanged.

Source: Path instructions


## Non-goals

- **Navigability.** The container ships `ripgrep` and `fd`, so the agent will
likely grep rather than traverse `index.md` files. This test says nothing
about whether the index structure works.
- **Prose quality.** Only the specific rulings are graded, not whether the
rewrite reads well.
- **The sandbox runtime.** Implemented for parity, exercised on the container
runtime only, matching how every other result in this repo was obtained.
57 changes: 57 additions & 0 deletions pi/container/agent/skills/apply-house-style/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
name: apply-house-style
description: Rewrite a paragraph to follow the house style recorded in the OKF wiki under okf/, citing the page each change came from. Use when a run asks you to apply house style to a passage.
---

# Apply house style from the wiki

You are given one paragraph. Rewrite it so it follows the house style recorded
in the OKF wiki under `okf/`, and say which page each change came from.

The wiki is **read-only** in this run and there is no source document to fall
back on — `okf/` is all you have, and it is the point. Answer from what the
pages actually say, not from what you already believe about house style. The
two often disagree, and here the wiki wins.

## Procedure

1. **Find the rulings.** Search `okf/` for the words and constructions used in

@lars20070 lars20070 Aug 3, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much of style guides are rules for specific words. In these cases, the CLI tools vale (active development, 5.7k stars) or qmd (active development, 28.5k stars) in addition to okf might help the Pi agent.

The harder problem are soft, less specific rules. Even if you can load thousands of lines of md into context, it will most likely lead to context rot i.e. the rules are overlooked and not applied.

the paragraph. A ruling is usually a short section named after the word it
governs. Read the page before relying on it.
2. **Apply only what you found.** Make a change when a page supports it. Do not
make stylistic improvements the wiki does not ask for — an unsupported change
cannot be cited, and uncited changes are the failure this task looks for.
3. **Leave the rest alone.** Preserve the paragraph's meaning, facts and
figures. You are correcting style, not rewriting the argument.
4. **Report** in the format below.

## Output format

End your reply with a fenced `json` block, and put nothing after it:

````text
```json
{
"rewrite": "the full corrected paragraph",
"changes": [
{
"before": "the original wording",
"after": "the corrected wording",
"ruling": "a short quote of what the page says",
"citation": "/part-2/6-confusables-and-cuttables-individual-rulings.md"
}
]
}
```
````

- `rewrite` — the whole paragraph, corrected. Not a diff, not a fragment.
- `changes` — one entry per change. An empty list is a valid answer if the
wiki genuinely supports no change.
- `ruling` — quote the page, briefly. Do not paraphrase it into something the
page does not say.
- `citation` — a **bundle-absolute** path to a page that exists, rooted at the
wiki root: `/part-2/….md`, not `okf/part-2/….md` and not a relative path.

Cite the page you actually read. A citation to a page that does not contain the
ruling is worse than making no change at all: it presents a guess as sourced.
48 changes: 48 additions & 0 deletions pi/container/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,53 @@ services:
- ../../md:/workspace/md:ro
- ../../okf:/workspace/okf:rw

# Consumption runtime: reads the compiled wiki, never writes it. Used by
# scripts/test-house-style.sh to check the wiki is usable, not merely
# well-formed. Two differences from `pi` above carry the whole point:
#
# - md/ is NOT mounted, so the agent cannot reach the source documents and
# must answer from okf/ alone. Mount it and the test proves nothing.
# - okf/ is read-only, because the artefact under test must survive the test
# unchanged. Nothing is written, so no writable mount is needed; the
# agent's answer is its stdout.
#
# Neither key is required here — the driver selects a provider with
# --provider/--model, and only that provider's key has to be set.
pi-consume:
build:
context: ../..
dockerfile: pi/container/Dockerfile
image: pi-container

environment:
HOME: /home/node
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
LITELLM_API_KEY: ${LITELLM_API_KEY:-}

network_mode: bridge
cap_drop:
- ALL
security_opt:
- no-new-privileges
read_only: true
user: "1000:1000"

pids_limit: 512
mem_limit: 4g
cpus: 2

tmpfs:
- /tmp
- /run
- /home/node

volumes:
- pi-agent-home:/home/node/.pi/agent
- ./agent/settings.json:/home/node/.pi/agent/settings.json:ro
- ./agent/models.json:/home/node/.pi/agent/models.json:ro
- ./agent/AGENTS.md:/home/node/.pi/agent/AGENTS.md:ro
- ./agent/skills:/home/node/.pi/agent/skills:ro
- ../../okf:/workspace/okf:ro

volumes:
pi-agent-home:
57 changes: 57 additions & 0 deletions pi/sandbox/files/home/.pi/agent/skills/apply-house-style/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
name: apply-house-style
description: Rewrite a paragraph to follow the house style recorded in the OKF wiki under okf/, citing the page each change came from. Use when a run asks you to apply house style to a passage.
---

# Apply house style from the wiki

You are given one paragraph. Rewrite it so it follows the house style recorded
in the OKF wiki under `okf/`, and say which page each change came from.

The wiki is **read-only** in this run and there is no source document to fall
back on — `okf/` is all you have, and it is the point. Answer from what the
pages actually say, not from what you already believe about house style. The
two often disagree, and here the wiki wins.

## Procedure

1. **Find the rulings.** Search `okf/` for the words and constructions used in
the paragraph. A ruling is usually a short section named after the word it
governs. Read the page before relying on it.
2. **Apply only what you found.** Make a change when a page supports it. Do not
make stylistic improvements the wiki does not ask for — an unsupported change
cannot be cited, and uncited changes are the failure this task looks for.
3. **Leave the rest alone.** Preserve the paragraph's meaning, facts and
figures. You are correcting style, not rewriting the argument.
4. **Report** in the format below.

## Output format

End your reply with a fenced `json` block, and put nothing after it:

````text
```json
{
"rewrite": "the full corrected paragraph",
"changes": [
{
"before": "the original wording",
"after": "the corrected wording",
"ruling": "a short quote of what the page says",
"citation": "/part-2/6-confusables-and-cuttables-individual-rulings.md"
}
]
}
```
````

- `rewrite` — the whole paragraph, corrected. Not a diff, not a fragment.
- `changes` — one entry per change. An empty list is a valid answer if the
wiki genuinely supports no change.
- `ruling` — quote the page, briefly. Do not paraphrase it into something the
page does not say.
- `citation` — a **bundle-absolute** path to a page that exists, rooted at the
wiki root: `/part-2/….md`, not `okf/part-2/….md` and not a relative path.

Cite the page you actually read. A citation to a page that does not contain the
ruling is worse than making no change at all: it presents a guess as sourced.
Loading
Loading