docs(project): the server is the standard way in, the skill the supplement

`okf project`'s closing text and the README's first screen now say it in
that order: register `okf mcp --root` once, and it answers from every
project and reaches subagents; the skill beside the bundle is for someone
who would rather register nothing; neither is made again when a bundle is
rebuilt. Two tests hold the order in both places. The README's stale
`<id>-consume` skill path is corrected to `okf-consume-any`.

v1.1 order F, part F3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-21 10:36:52 +02:00
commit 570496470b
5 changed files with 56 additions and 19 deletions

View file

@ -1540,6 +1540,12 @@ R761 **8** (S1-S6 + KP + KN), vegnormal **32** questions / **43**
of them, so over a folder it is REFUSED by name with exit 2
(`consume.FOLDER_FLAGS` is the allowlist), never dropped; `--bundle-id` on a
bundle path is refused the same way. A bundle path reads exactly as before.
- **THE SERVER IS THE STANDARD WAY IN AND THE SKILL THE SUPPLEMENT (v1.1 F3,
operator 2026-09-21).** `okf project`'s closing text and README's first
screen say it in that order: register `okf mcp --root` once (every project,
subagents too); the skill beside the bundle is for someone who would rather
register nothing; neither is made again when a bundle is rebuilt. Two tests
hold the ORDER, not just the presence, in both places.
- **`okf card <bundle>` and the generic skill are the one-to-many form.** The card is one bundle's identity, concept count,
conditional-field counts and whole-bundle cost as JSON, **DERIVED on every run
and never written into the bundle** -- storing it would move the bytes of all

View file

@ -16,22 +16,23 @@ uv tool install "llm-ingestion-okf[extract] @ git+https://git.fromaitochitta.com
## Use it
```sh
okf project ~/my-documents # folder in: bundle + Claude Code skill, in this directory
claude # start Claude Code here
okf project ~/my-documents # folder in: a bundle under ./.okf, and a skill beside it
```
**Or connect every bundle at once.** Register the server once, on user scope,
and every project you open can ask any bundle under that directory — no skill
to install per project, and nothing to regenerate when a bundle is rebuilt.
You run this line; `okf` never starts Claude Code:
**The standard way in is the server.** Register it once, on user scope,
pointing at the directory that holds your projects. Every project you open can
then ask any bundle under it, subagents included — they inherit MCP tools and
not skills — and a bundle added or rebuilt is seen by the next call. You run
this line; `okf` never starts Claude Code:
```sh
claude mcp add --scope user okf -- okf mcp --root ~/okf
claude mcp add --scope user okf -- okf mcp --root ~/projects
```
Subagents inherit MCP tools and do not inherit skills, so the server is also
the only way the same working method reaches an arm running below the main
thread.
**The skill is the supplement**, for when you would rather register nothing:
`okf project` writes it beside the bundle, so starting `claude` in that
directory is enough. It reads every bundle under `./.okf` with the same code the
server runs. Neither has to be made again when a bundle is rebuilt.
Then ask in plain language. Three shapes of request work, and the skill states
the rules for each:
@ -1236,7 +1237,7 @@ claude
```
`okf project` writes the bundle to `.okf/<id>/` and a skill to
`.claude/skills/<id>-consume/` in the **current directory**, then prints what it
`.claude/skills/okf-consume-any/` in the **current directory**, then prints what it
read, what it wrote, and which documents a question cannot reach. Start `claude`
in that directory and ask in plain language; the generated skill runs the
pre-pass and the contract check itself and marks every claim with its source.

View file

@ -178,16 +178,19 @@ def summarise(
lines.extend(
[
"",
f"NEXT: start claude again in {out} and ask your question.",
"",
"To reach this bundle -- and every other bundle under one directory --",
"from ANY project, register the server once instead. You run this line;",
f"{CLI_ID} never starts claude:",
"NEXT -- the standard way in is the server. Register it ONCE; you run",
f"this line, {CLI_ID} never starts claude:",
"",
f" claude mcp add --scope user okf -- okf mcp --root {out.parent}",
"",
"Then no skill has to be installed per project, and nothing has to be",
"regenerated when a bundle is rebuilt.",
"It then answers from every project, reaches subagents too, and sees",
"every bundle under that directory -- one added or rebuilt later included.",
"",
"The skill written here is the supplement, for when you would rather",
f"register nothing: start claude in {out} and ask. It reads every",
f"bundle under {out / BUNDLE_DIR} with the same code.",
"",
"Neither has to be made again when a bundle is rebuilt.",
]
)
return "\n".join(lines)

View file

@ -134,6 +134,33 @@ def test_the_project_summary_says_what_to_do_next_with_the_server(tmp_path: Path
assert "claude mcp add --scope user okf -- okf mcp --root" in summary
def test_the_project_summary_puts_the_server_first_and_the_skill_second(tmp_path: Path) -> None:
"""v1.1 F3: the server is the standard way in, the skill the supplement."""
folder = tmp_path / "Dokumenter"
folder.mkdir()
(folder / "krav.md").write_text(
"## 4 Grunnforhold\n\nGrunnen er morene over berg.\n", encoding="utf-8", newline=""
)
out = tmp_path / "project"
_, _, summary = project.create(folder, out=out)
server = summary.index("claude mcp add --scope user okf -- okf mcp --root")
skill = summary.index(f"start claude in {out}")
assert server < skill
assert "standard" in summary[:server]
assert "supplement" in summary[server:]
assert "Neither has to be made again when a bundle is rebuilt" in summary
def test_the_readme_first_screen_puts_the_server_first_and_the_skill_second() -> None:
readme = (PROJECT_ROOT / "README.md").read_text(encoding="utf-8")
first_screen = readme.split("## Known limitations", 1)[0]
server = first_screen.index("claude mcp add --scope user okf -- okf mcp --root")
skill = first_screen.index("The skill is the supplement")
assert server < skill
assert "standard way in" in first_screen[:server]
assert "Neither has to be made again" in first_screen
def test_a_project_bundle_is_where_a_root_server_finds_it(tmp_path: Path) -> None:
"""`--root <the directory holding projects>` must reach `<project>/.okf/<id>`.

View file

@ -127,7 +127,7 @@ def test_the_summary_names_the_documents_that_landed_whole(folder: Path, tmp_pat
assert "notat.md" in summary
assert "krav.md" not in summary
assert "[sourced-not-sufficient]" in summary
assert f"NEXT: start claude again in {out}" in summary
assert f"start claude in {out}" in summary
def test_a_document_that_is_in_the_bundle_is_not_reported_as_missing(