okf-pro 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/.okf/capabilities/checks.md +52 -0
- data/.okf/capabilities/index.md +12 -0
- data/.okf/capabilities/verbs.md +70 -0
- data/.okf/contract/containment-directions.md +3 -0
- data/.okf/contract/exit-codes.md +5 -2
- data/.okf/contract/gates-only-at-the-hook-door.md +3 -0
- data/.okf/contract/silent-skips.md +3 -0
- data/.okf/contract/telemetry-does-not-lie.md +3 -0
- data/.okf/contract/the-contract.md +3 -0
- data/.okf/design/a-comment-is-not-an-implementation.md +3 -0
- data/.okf/design/a-rule-you-can-walk-past.md +3 -0
- data/.okf/design/derivation-that-writes.md +3 -0
- data/.okf/design/failure-modes.md +3 -0
- data/.okf/design/lineage.md +3 -0
- data/.okf/design/structure-laws.md +3 -0
- data/.okf/design/the-residue.md +3 -0
- data/.okf/design/three-laws.md +3 -0
- data/.okf/design/three-pillars.md +3 -0
- data/.okf/index.md +23 -14
- data/.okf/log.md +20 -0
- data/.okf/scaffold/collisions-and-refusals.md +3 -0
- data/.okf/scaffold/no-date-ships.md +3 -0
- data/.okf/scaffold/ownership-not-subject.md +3 -0
- data/.okf/scaffold/the-adopters-manual.md +3 -0
- data/.okf/seam/bundler-scoping.md +4 -1
- data/.okf/seam/identity-not-existence.md +3 -0
- data/.okf/seam/the-wrapper.md +3 -0
- data/.okf/seam/three-fail-opens.md +3 -0
- data/.okf/structure/doors.md +80 -0
- data/.okf/structure/index.md +18 -0
- data/.okf/structure/reading-the-world.md +65 -0
- data/.okf/structure/the-gates.md +64 -0
- data/.okf/structure/the-guards.md +43 -0
- data/.okf/structure/the-recorder.md +49 -0
- data/.okf/structure/the-scaffold.md +42 -0
- data/.okf/structure/the-writers.md +60 -0
- data/.okf/testing/adding-a-verb.md +63 -0
- data/.okf/testing/drills-over-units.md +3 -0
- data/.okf/testing/fixture-is-a-client.md +3 -0
- data/.okf/testing/index.md +1 -0
- data/.okf/trust/read-owed-rule.md +3 -0
- data/.okf/trust/scalar-verified.md +3 -0
- data/CHANGELOG.md +68 -1
- data/README.md +5 -1
- data/lib/okf/pro/cli.rb +1 -1
- data/lib/okf/pro/version.rb +1 -1
- metadata +19 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a908d8394cc7ea78e25a744f080813d34eb656b77274e3bb8db4397d222c3950
|
|
4
|
+
data.tar.gz: 687919c65db7bf0d42bc48f475cd88f636c01aaf87f49443d92c6de0fd434fb9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0589ad1bd8edb04b767044692d20b271614fe37d60f598fe1b51ba436384142049da27d94d8281df54b0a485b658751e7ef2fe3d4c84a292b4593a61502d6c87'
|
|
7
|
+
data.tar.gz: 7afe1c08143bf0524f9deb5ce47e827276b59aa805f62cb65b2be5e5414bb542b1208a4b2a59826443ab1fde4e5680066fd047e6236007afc8b3e1b0e04e7ae3
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Capability
|
|
3
|
+
title: The checks — nine hook names, and the event each one rides
|
|
4
|
+
description: What `okf pro hook <name>` accepts, which Claude Code event the scaffold wires it to, what it asks, and what it does when it cannot answer.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The catalogue
|
|
11
|
+
|
|
12
|
+
`OKF::Pro::CLI::HOOK_NAMES` is the source of truth, and it is strictly narrower
|
|
13
|
+
than `NAMES`: the hook door accepts these nine and nothing else, because `run`
|
|
14
|
+
dispatches the CI verbs off the same first argv element and a `settings.json`
|
|
15
|
+
typo spelling `hook audit` would otherwise install a gate that reads no stdin,
|
|
16
|
+
never blocks, and reports "clean." That failure is [gates-only-at-the-hook-door](/contract/gates-only-at-the-hook-door.md).
|
|
17
|
+
|
|
18
|
+
| name | event | asks |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `guard-verified` | `PreToolUse` on `Edit\|Write\|MultiEdit` | is this edit setting `verified:` on something the owner has not read? |
|
|
21
|
+
| `journal-guard` | `PreToolUse` on `Edit\|Write\|MultiEdit` | is this rewriting a past journal day? |
|
|
22
|
+
| `shell-guard` | `PreToolUse` on `Bash` | is this command about to write into the bundle behind the other two? |
|
|
23
|
+
| `post-edit` | `PostToolUse` on `Edit\|Write\|MultiEdit` | the three below, in one process, sharing one bundle read |
|
|
24
|
+
| `check-okf` | (composed into `post-edit`) | does the bundle still validate and lint? |
|
|
25
|
+
| `cap-check` | (composed into `post-edit`) | is In flight over the cap? |
|
|
26
|
+
| `reconcile-search` | (composed into `post-edit`) | what already says this? (**Law 1**) |
|
|
27
|
+
| `stop-gate` | `Stop` | is the day's snapshot line written, and does it agree with the board? (**Law 2**) |
|
|
28
|
+
| `session-context` | `SessionStart` | the banner: what is on the board, as the session opens |
|
|
29
|
+
|
|
30
|
+
`check-okf`, `cap-check` and `reconcile-search` remain addressable on their own
|
|
31
|
+
— they are in `CHECKS` and the door accepts them — but the scaffold wires
|
|
32
|
+
`post-edit` instead, because the bundle read is the expensive part and three
|
|
33
|
+
separate invocations paid for it three times over.
|
|
34
|
+
|
|
35
|
+
# What they do when they cannot answer
|
|
36
|
+
|
|
37
|
+
This is the half that matters, and it is the contract rather than a courtesy.
|
|
38
|
+
|
|
39
|
+
`hook` reads **0 as pass and 2 as block, and every other code — including 1 —
|
|
40
|
+
as non-blocking**: the tool call proceeds. So `hook` never returns 1. A gate
|
|
41
|
+
that crashes therefore *passes*, from the protocol's side, which is why the
|
|
42
|
+
reader scrubs rather than raises, why `CLI.run` refuses anything that still
|
|
43
|
+
raises, and why the shell wrapper exists at all — a Ruby checker structurally
|
|
44
|
+
cannot refuse on its own absence.
|
|
45
|
+
|
|
46
|
+
A check that does not apply is not a failure: `Target.for` returns `nil` for an
|
|
47
|
+
edit outside any bundle, and the gate passes. A check that could not run says
|
|
48
|
+
so, in the same channel it would have used to refuse.
|
|
49
|
+
|
|
50
|
+
The full table, and the argument, is [exit-codes](/contract/exit-codes.md); the
|
|
51
|
+
three ways the seam let an unchecked edit through are
|
|
52
|
+
[three-fail-opens](/seam/three-fail-opens.md).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Capabilities
|
|
2
|
+
|
|
3
|
+
What this gem can already answer. Read it before writing a check or a verb —
|
|
4
|
+
the point of a catalogue is that the fifteenth thing is not the eighth thing
|
|
5
|
+
under a new name.
|
|
6
|
+
|
|
7
|
+
Both lists are pinned: `test/unit/bundle_catalog_test.rb` compares them against
|
|
8
|
+
`OKF::Pro::CLI`'s own tables, so a verb added to the code and not to the
|
|
9
|
+
catalogue is a red suite rather than a stale document.
|
|
10
|
+
|
|
11
|
+
* [verbs](verbs.md) - The sixteen `okf pro` verbs: what each answers, which module implements it, and which flags it takes.
|
|
12
|
+
* [checks](checks.md) - The nine hook names, the event each rides, and what it does when it cannot answer.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Capability
|
|
3
|
+
title: The verbs — what `okf pro` already answers
|
|
4
|
+
description: Sixteen verbs in four families — scaffold, readers, writers, and the hook adapter — each with the module that implements it and the flags it declares.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The catalogue
|
|
11
|
+
|
|
12
|
+
`OKF::Pro::CLI::USAGE` is the source of truth for this table, and a test holds
|
|
13
|
+
the two together.
|
|
14
|
+
|
|
15
|
+
| verb | argument | answers | implemented in |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| `setup` | `[DIR]` | create or complete an agent's brain in DIR (default `.`) | `scaffold.rb` |
|
|
18
|
+
| `upgrade` | `[DIR]` | rewrite the gem-owned governance files; stage the rest | `scaffold.rb` |
|
|
19
|
+
| `skill` | `DEST` | (re)install okf-pro's agent skill on its own | `scaffold.rb` |
|
|
20
|
+
| `state` | `[DIR]` | what is on the board, in one call — add `--full` for the corpus | `state.rb` |
|
|
21
|
+
| `board` | `[DIR]` | one row per board line: section, dates, age, links | `board.rb` |
|
|
22
|
+
| `audit` | `[DIR]` | every invariant at once — the CI door | `audit.rb` |
|
|
23
|
+
| `records` | `[DIR]` | does the staged commit rewrite a past journal day? | `records.rb` |
|
|
24
|
+
| `snapshot` | `[DIR]` | compute the day's counter line (prints, never writes) | `snapshot.rb` |
|
|
25
|
+
| `unverified` | `[DIR]` | generated concepts still awaiting the owner's read | `attestation.rb` |
|
|
26
|
+
| `friction` | `[DIR]` | what was done by hand that a verb could do | `friction.rb` |
|
|
27
|
+
| `capture` | `TEXT` | append a dated Inbox line | `writes.rb` |
|
|
28
|
+
| `promote` | `SEL` | Inbox or Backlog to In flight, refusing over the cap | `writes.rb` |
|
|
29
|
+
| `demote` | `SEL` | In flight back to Backlog | `writes.rb` |
|
|
30
|
+
| `journal` | `open` | create today's journal day and index it | `writes.rb` |
|
|
31
|
+
| `close` | `SLUG` | the three mechanical closing moves for a project | `writes.rb` |
|
|
32
|
+
| `hook` | `CHECK` | run one gate against a hook event on stdin | see [checks](checks.md) |
|
|
33
|
+
|
|
34
|
+
# The four families, and why they differ
|
|
35
|
+
|
|
36
|
+
**Scaffold** (`setup`, `upgrade`, `skill`) takes a *destination*, not a bundle.
|
|
37
|
+
It does not go through `dir_argument`: `setup` into an empty directory is the
|
|
38
|
+
whole point, and refusing one that holds no bundle would refuse every first run.
|
|
39
|
+
|
|
40
|
+
**Readers** (`audit`, `records`, `snapshot`, `unverified`, `state`, `board`,
|
|
41
|
+
`friction`) answer questions. Every one of them routes through `parse_flags`,
|
|
42
|
+
listed in `FLAGS` or not — because absence from that table means "accepts none",
|
|
43
|
+
and a verb that skips the parser hands its undeclared flag to `BundleRoot` as a
|
|
44
|
+
directory and reports "holds no OKF bundle" as a *finding*: a pipeline's own
|
|
45
|
+
typo, spelled as a broken bundle. `test/integration/cli_test.rb` pins it over
|
|
46
|
+
`READERS`.
|
|
47
|
+
|
|
48
|
+
**Writers** (`capture`, `promote`, `demote`, `journal`, `close`) are the shapes
|
|
49
|
+
with exactly one correct form. Each is additive, targeted, and refused by
|
|
50
|
+
`Conserve` rather than careful — see [the-writers](/structure/the-writers.md).
|
|
51
|
+
They go through `writer_flags` instead, because their first argument is content
|
|
52
|
+
and a flag there is data.
|
|
53
|
+
|
|
54
|
+
**The hook adapter** is `hook`, and it is the odd one: its exit codes are the
|
|
55
|
+
protocol's, not this repo's.
|
|
56
|
+
|
|
57
|
+
# Flags
|
|
58
|
+
|
|
59
|
+
Declared per verb in `CLI::FLAGS`, and the help text is read *from* that table
|
|
60
|
+
rather than typed beside it:
|
|
61
|
+
|
|
62
|
+
| verb | flags |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `state` | `--json`, `--pretty`, `--full` |
|
|
65
|
+
| `board` | `--json`, `--pretty`, `--section NAME` |
|
|
66
|
+
| `snapshot` | `--json`, `--pretty` |
|
|
67
|
+
| `unverified` | `--json`, `--pretty` |
|
|
68
|
+
| `friction` | `--json`, `--pretty`, `--issue`, `--clear` |
|
|
69
|
+
|
|
70
|
+
Everything else accepts none, and says so.
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: Containment fails in four different directions, on purpose
|
|
4
4
|
description: A path that escapes the bundle is refused, or ignored, or read as "not closed" — and which one is right is decided per call site by what a wrong answer costs, at the write door as well as the read one.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The rule
|
data/.okf/contract/exit-codes.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: Exit codes
|
|
4
4
|
description: The hook protocol reads 0 and 2 and treats every other code as non-blocking, which inverts the repo's usual 0/1/2 convention for `hook` alone.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# What the protocol actually reads
|
|
@@ -17,8 +20,8 @@ that lets the edit through.
|
|
|
17
20
|
|
|
18
21
|
# The deviation, and where it stops
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
error. `okf pro hook` cannot keep it: 1 is a code the protocol ignores, so it
|
|
23
|
+
The kernel's guide states the repo's convention as 0 ok / 1 failing bundle / 2
|
|
24
|
+
usage error (`@okf capabilities/validator` carries it too). `okf pro hook` cannot keep it: 1 is a code the protocol ignores, so it
|
|
22
25
|
is never returned. Every other verb keeps the convention.
|
|
23
26
|
|
|
24
27
|
| Verb | 0 | 1 | 2 |
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: Only gates answer at the hook door
|
|
4
4
|
description: The CI verbs are not checks, and accepting one as a check name would report clean without ever reading the event.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The whitelist
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Finding
|
|
3
3
|
title: The checker broke its own third clause
|
|
4
4
|
description: A default Linter.call skips two clock-gated checks and still reports healthy, so the gate reported clean over checks it never ran.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# What was measured
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Learning
|
|
3
3
|
title: The third clause, applied to something that is not a check
|
|
4
4
|
description: The friction recorder neither refuses nor blocks, and still may not report a zero it did not count — because a measurement that quietly degrades to "nothing happened" retires itself.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# What it is
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: The contract
|
|
4
4
|
description: Blocking checks fail closed, feedback checks fail loud, and no check ever fails silent — the three clauses every other decision in this gem is derived from.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The three clauses
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Learning
|
|
3
3
|
title: A comment is not an implementation
|
|
4
4
|
description: A regex that claimed to anchor `>` to a redirection position never did, and the guard it powered fired on reads for its whole life — which is the shape of noise that retires a gate. The same class reaches comments, docstrings and the bundle's own concepts.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# What happened
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Learning
|
|
3
3
|
title: A rule you can walk past is a preference
|
|
4
4
|
description: Why the gates block rather than warn, why three doors are not redundant, and why the day's ritual must stay silent on a day nobody worked.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The claim
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: Derivation may write, under a conservation guard
|
|
4
4
|
description: Failure mode 07 forbids an LLM regenerating a view; it does not forbid a Ruby function performing a declared, line-conserving edit — and the status quo it was written against was the failure mode itself.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The verdict, read precisely
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Finding
|
|
3
3
|
title: The eight failure modes
|
|
4
4
|
description: The named ways a personal knowledge system dies — three by lying and five by telling the truth badly — and the mechanism that answers each.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The catalogue
|
data/.okf/design/lineage.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: Structure laws
|
|
4
4
|
description: The seven rules that decide whether a room, a field or a file may exist — a directory is a retrieval question, and state lives in exactly one place.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The rules
|
data/.okf/design/the-residue.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Learning
|
|
3
3
|
title: The residue
|
|
4
4
|
description: Trust measures process, never truth — a misheard number, sourced and verified, reaches maximum trust while being false, and no law closes it.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The limit
|
data/.okf/design/three-laws.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: Three laws
|
|
4
4
|
description: Nothing is written without confronting what is already there; every view confesses its blind spots; attention is a budget, renegotiable but never silently exceeded.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The laws
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Overview
|
|
3
3
|
title: Three pillars
|
|
4
4
|
description: Know, Work and State — three questions no one of them can answer alone, and why the zones of the bundle are the pillars themselves.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The three questions
|
data/.okf/index.md
CHANGED
|
@@ -4,25 +4,34 @@ okf_version: "0.2"
|
|
|
4
4
|
|
|
5
5
|
# okf-pro knowledge bundle
|
|
6
6
|
|
|
7
|
-
The
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
The knowledge behind **okf-pro** — the gem that turns an okf bundle (`@okf`)
|
|
8
|
+
into a working memory an agent is held to. It is written to be read *before*
|
|
9
|
+
opening `lib/`, so an agent about to add a check, a verb or a test does not
|
|
10
|
+
re-derive the layering and does not rebuild something this gem already answers.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
`AGENTS.md` beside it carries the contract, the constraints and the commands,
|
|
13
|
+
and routes here for everything else; the `README` is the adopter's. What used
|
|
14
|
+
to be a hand-maintained Map of `lib/**` in `AGENTS.md` now lives in
|
|
15
|
+
[Structure](structure/), pinned by `test/unit/bundle_catalog_test.rb` — the
|
|
16
|
+
code is the truth and this bundle is the claim, so the two cannot drift
|
|
17
|
+
quietly.
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
It also captures the argument the gem serves — three laws, the failures each
|
|
20
|
+
one closes, and the limit each one admits — because the rules are borrowed and
|
|
21
|
+
reasoned rather than obvious, and a rule whose reasoning is lost gets re-argued
|
|
22
|
+
from scratch every time someone questions it.
|
|
23
|
+
|
|
24
|
+
And the thing this gem is unusual for: **every defect here fails in the
|
|
25
|
+
direction of silence.** A gate that cannot run, a check that was skipped, a shim
|
|
26
|
+
on `PATH`, a status code the protocol reads as "proceed" — each of them produces
|
|
27
|
+
an unchecked bundle that is indistinguishable, from the outside, from a clean
|
|
28
|
+
one. So the knowledge worth recording is not what the checks assert; it is where
|
|
29
|
+
the machinery around them can stop asserting anything without saying so.
|
|
23
30
|
|
|
24
31
|
# Areas
|
|
25
32
|
|
|
33
|
+
* [Structure](structure/) - Every file under `lib/`, grouped by the layer that owns it: the doors, the readers, the gates, the guards, the writers, the scaffold, the recorder.
|
|
34
|
+
* [Capabilities](capabilities/) - The catalogue: sixteen verbs and nine checks — what each answers, before you write a seventeenth.
|
|
26
35
|
* [Design](design/) - Why the rules exist: three pillars, three laws, the eight failures they close, and what was borrowed.
|
|
27
36
|
* [Contract](contract/) - Fail closed, fail loud, never fail silent — and the exit codes that make it true.
|
|
28
37
|
* [Seam](seam/) - The plugin entry point, the three ways it fails open, and the wrapper that closes them.
|
data/.okf/log.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Update Log
|
|
2
2
|
|
|
3
|
+
## 2026-08-19
|
|
4
|
+
|
|
5
|
+
* **Every concept declares `generated:`.** The dates are read from git with
|
|
6
|
+
`--follow`, so a rename is not mistaken for authorship — the first attempt
|
|
7
|
+
stamped all 37 with the day the files *moved*, which is provenance invented
|
|
8
|
+
rather than recorded, and was thrown away.
|
|
9
|
+
|
|
10
|
+
* **The structure and the catalogue moved into this bundle, and a test holds
|
|
11
|
+
them to the code.** `AGENTS.md` carried a hand-maintained Map of `lib/**` that
|
|
12
|
+
nothing checked, so a file could arrive, move or leave and the Map would keep
|
|
13
|
+
reading plausibly. [Structure](/structure/) now owns it — one concept per
|
|
14
|
+
layer, naming every file — and [Capabilities](/capabilities/) owns the
|
|
15
|
+
catalogue of what the sixteen verbs and nine checks already answer, which is
|
|
16
|
+
the list an agent has to read before writing a seventeenth.
|
|
17
|
+
`test/unit/bundle_catalog_test.rb` is the pin, and it bites in both
|
|
18
|
+
directions: a file no concept names, a concept naming a file that is gone, or
|
|
19
|
+
a catalogue that disagrees with `CLI::USAGE` or `CLI::HOOK_NAMES`.
|
|
20
|
+
[testing/adding-a-verb](/testing/adding-a-verb.md) is the walk a new surface
|
|
21
|
+
owes, ending at the catalogue entry the pin will demand.
|
|
22
|
+
|
|
3
23
|
## 2026-08-17
|
|
4
24
|
|
|
5
25
|
* **okf-pro 1.0.0.** The prototype checker — carried inside a template
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: Setup writes what it can, and refuses exactly one thing
|
|
4
4
|
description: A collision is staged beside the adopter's file rather than refused; a root-level bundle is refused outright, because writing there manufactures an ambiguous layout.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# Never refuse wholesale
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: No date ships in the generated bundle
|
|
4
4
|
description: A template is cloned an unknowable number of days after it is built, and every shipped date ages into somebody else's calendar.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The rule
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: The scaffold splits by ownership, not by subject
|
|
4
4
|
description: Knowledge-versus-machinery is the obvious cut and the wrong one — it makes `upgrade` destroy exactly the hand-merge that adopting the gem asked for.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The two classes
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Learning
|
|
3
3
|
title: The seeded README is the only document written for the owner
|
|
4
4
|
description: Every other seeded file addresses the agent, so the adopter's README carries the exit, the limits and the price — and a fresh reader can measure whether it does.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# What the seeded README is for
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Finding
|
|
3
3
|
title: Bundler scoping switches the gates off, and the fix locks out the in-bundle adopter
|
|
4
4
|
description: Gem discovery is bundle-scoped once bundler's variables are exported, so `okf pro` is an unknown command inside a bundle that does not name okf-pro.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# Both directions, both measured
|
|
@@ -10,7 +13,7 @@ okf finds its extensions with `Gem.find_latest_files`, which is bundle-scoped
|
|
|
10
13
|
once bundler's variables are in the environment.
|
|
11
14
|
|
|
12
15
|
```
|
|
13
|
-
BUNDLE_GEMFILE=okf/Gemfile bundle exec okf tui --help
|
|
16
|
+
BUNDLE_GEMFILE=gems/okf/Gemfile bundle exec okf tui --help
|
|
14
17
|
→ unknown command, exit 2
|
|
15
18
|
```
|
|
16
19
|
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Learning
|
|
3
3
|
title: Existence is not identity
|
|
4
4
|
description: A stray `okf` on PATH that exits 0 passes every gate, and no exit-code normalisation can tell it from a clean check.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The claim that was wrong
|
data/.okf/seam/the-wrapper.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: Why the wrapper is a separate shell script
|
|
4
4
|
description: A Ruby checker structurally cannot refuse on its own absence, so something already running has to — and it must not exec.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The argument
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Finding
|
|
3
3
|
title: Three fail-opens in the plugin seam
|
|
4
4
|
description: Moving the checker behind okf's plugin seam introduced three paths where a failure exits 1 — which the hook protocol reads as "proceed".
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The measurements
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Component
|
|
3
|
+
title: The doors, and the constants the contract is written in
|
|
4
|
+
description: Three entry points — the plugin verb, the hook adapter, the CI verbs — all through one dispatcher, and the exit codes every one of them answers in.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The files
|
|
11
|
+
|
|
12
|
+
| file | what it owns |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `lib/okf/pro.rb` | the contract's constants, the two reads, `NO_RECONCILE`, and the require list that loads everything |
|
|
15
|
+
| `lib/okf/plugin.rb` | `OKF::CLI::Pro` — the okf extension seam, and the last Ruby-side line of the contract |
|
|
16
|
+
| `lib/okf/pro/cli.rb` | `OKF::Pro::CLI` — dispatch, the verb tables, the flag parser, and the identity marker |
|
|
17
|
+
| `lib/okf/pro/version.rb` | `OKF::Pro::VERSION` |
|
|
18
|
+
|
|
19
|
+
# `pro.rb` is the vocabulary, not a namespace
|
|
20
|
+
|
|
21
|
+
Three constants, and every exit this gem takes is one of them:
|
|
22
|
+
|
|
23
|
+
* `PASS = 0`, `BLOCK = 2` — the only two codes the hook protocol reads as
|
|
24
|
+
meaning anything.
|
|
25
|
+
* `FAIL = 1` — `audit` only. It is *findings*, which is CI's vocabulary and not
|
|
26
|
+
the hook's; the hook door never returns it, because the protocol reads it as
|
|
27
|
+
proceed.
|
|
28
|
+
|
|
29
|
+
Then three things every layer above uses. `read_text` is the raw read, as
|
|
30
|
+
bytes, forced to UTF-8 and scrubbed — it failed open twice before it looked
|
|
31
|
+
like this, once on an unset `LANG` making `Encoding.default_external` US-ASCII
|
|
32
|
+
and once on a single invalid byte raising out of `match?`. `read_contained` is
|
|
33
|
+
the same read through the kernel's `SafeRead.read!`, for the callers that
|
|
34
|
+
already hold a root and must refuse a symlink leaving it. `newline_terminated`
|
|
35
|
+
guarantees the final newline a splice depends on.
|
|
36
|
+
|
|
37
|
+
`require "okf/pro"` loads **everything**, deliberately: this gem is not an
|
|
38
|
+
embedding library, it is a checker, and every module is on the path of some
|
|
39
|
+
gate. It also refuses at the top — a `LoadError` on `okf` prints
|
|
40
|
+
`ENFORCEMENT DEGRADED` and exits 2, rather than letting the edit through.
|
|
41
|
+
|
|
42
|
+
# `plugin.rb` is where the contract runs out of Ruby
|
|
43
|
+
|
|
44
|
+
It registers `pro` with the kernel's command registry, and holds a
|
|
45
|
+
`rescue Exception` that re-raises `SystemExit`, cop disabled, reason beside it.
|
|
46
|
+
The failures it catches are `ScriptError`s, which are not `StandardError`s, and
|
|
47
|
+
it must sit **outside** the `require "okf/pro"` it guards — inside the CLI it
|
|
48
|
+
could not catch the `LoadError` of the require that reaches it.
|
|
49
|
+
|
|
50
|
+
That is the first of [three fail-opens](/seam/three-fail-opens.md) this seam had.
|
|
51
|
+
|
|
52
|
+
A `SyntaxError` here is unreachable from Ruby altogether: discovery rescues
|
|
53
|
+
`LoadError, StandardError`, a `SyntaxError` is neither, and no gem code runs at
|
|
54
|
+
all. Only the scaffold's shell wrapper can catch that, which is why the wrapper
|
|
55
|
+
no longer `exec`s.
|
|
56
|
+
|
|
57
|
+
# `cli.rb` is one dispatcher and four tables
|
|
58
|
+
|
|
59
|
+
The verb tables are the catalogue, and they are read rather than copied
|
|
60
|
+
wherever a second copy would drift:
|
|
61
|
+
|
|
62
|
+
| table | what it holds |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `CHECKS` | the eight hook checks, name to lambda |
|
|
65
|
+
| `HOOK_NAMES` | `CHECKS.keys` plus `session-context` — the **only** names the hook door accepts |
|
|
66
|
+
| `READERS` | the seven question-answering verbs |
|
|
67
|
+
| `WRITERS` | the five verbs that change a file |
|
|
68
|
+
| `SCAFFOLD` | `setup`, `upgrade`, `skill` — they take a destination, not a bundle |
|
|
69
|
+
| `USAGE` | the printed command list, joined to `help_rows` by a test rather than by a second literal |
|
|
70
|
+
| `FLAGS` | what each verb accepts, declared; absence means "accepts none" |
|
|
71
|
+
|
|
72
|
+
`HOOK_NAMES` exists because `run` dispatches the CI verbs off the same first
|
|
73
|
+
argv element a check name arrives in. Without the whitelist a `settings.json`
|
|
74
|
+
typo spelling `hook audit` installs a gate that reads no stdin, cannot block,
|
|
75
|
+
and reports "clean."
|
|
76
|
+
|
|
77
|
+
`MARKER` — `okf-pro-enforcer v1` on stderr immediately before a check runs — is
|
|
78
|
+
the whole of the wrapper's identity proof, and it is emitted *here* rather than
|
|
79
|
+
at the door so that its presence means the check was reached, not merely that
|
|
80
|
+
the plugin loaded.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Structure
|
|
2
|
+
|
|
3
|
+
Every file under `lib/`, grouped by the layer that owns it. One concept owns
|
|
4
|
+
each file, and `test/unit/bundle_catalog_test.rb` fails if that stops being true
|
|
5
|
+
in either direction — a file no concept names, or a concept naming a file that
|
|
6
|
+
is gone.
|
|
7
|
+
|
|
8
|
+
Read it as a pipeline. An event arrives at a door, the world is read into data,
|
|
9
|
+
a gate or a guard asks one question of that data, and — only for the write
|
|
10
|
+
verbs — a transform is planned, conserved and committed.
|
|
11
|
+
|
|
12
|
+
* [doors](doors.md) - `lib/okf/pro.rb`, `lib/okf/plugin.rb`, `lib/okf/pro/cli.rb`, `lib/okf/pro/version.rb` — the seam, the contract's constants, and the one dispatcher.
|
|
13
|
+
* [reading-the-world](reading-the-world.md) - `bundle_root.rb`, `event.rb`, `target.rb`, `board.rb`, `log.rb`, `records.rb` — where the bundle is, what arrived, and the two documents as data.
|
|
14
|
+
* [the-gates](the-gates.md) - `reconcile.rb`, `budget.rb`, `closing.rb`, `snapshot.rb`, `attestation.rb`, `pairing.rb`, `conformance.rb`, `state.rb`, `audit.rb` — one question each, and the two doors that ask them all.
|
|
15
|
+
* [the-guards](the-guards.md) - `guards.rb`, `shell_guard.rb` — the trust rules, and the same rules at the door a shell command comes through.
|
|
16
|
+
* [the-writers](the-writers.md) - `conserve.rb`, `board/edit.rb`, `log/edit.rb`, `writes.rb` — pure transforms, a declared delta, and the refusal that enforces it.
|
|
17
|
+
* [the-scaffold](the-scaffold.md) - `scaffold.rb` and the template tree it writes.
|
|
18
|
+
* [the-recorder](the-recorder.md) - `friction.rb` — telemetry about the tooling, which refuses nothing and blocks nothing.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Component
|
|
3
|
+
title: Reading the world — the root, the event, and the two documents as data
|
|
4
|
+
description: Six files that answer where the bundle is, what arrived on stdin, and what `board.md` and `log.md` say — before any gate has an opinion.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The files
|
|
11
|
+
|
|
12
|
+
| file | pure? | what it owns |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `lib/okf/pro/bundle_root.rb` | pure | where the bundle is, given where the agent is |
|
|
15
|
+
| `lib/okf/pro/event.rb` | pure | the hook event — the only place untrusted input is parsed |
|
|
16
|
+
| `lib/okf/pro/target.rb` | shell | bundle root + edited path, or `nil` when a check cannot apply |
|
|
17
|
+
| `lib/okf/pro/board.rb` | pure | `board.md` as data: sections, budget header, links, dates |
|
|
18
|
+
| `lib/okf/pro/log.rb` | pure | `log.md` as data: the snapshot line, the newest day |
|
|
19
|
+
| `lib/okf/pro/records.rb` | shell | the append-only record, asked of git at the commit door |
|
|
20
|
+
|
|
21
|
+
# Where the root is
|
|
22
|
+
|
|
23
|
+
`BundleRoot.resolve` walks from a starting directory to the bundle it belongs
|
|
24
|
+
to. `DIR` is `.okf`, and `root_kind` distinguishes the repository root from a
|
|
25
|
+
level root from a plain directory, because "the bundle" is a different question
|
|
26
|
+
in a monorepo than in a repository holding one. It is the one reader that does
|
|
27
|
+
**not** go through `read_contained`: it is deciding where the root *is*, so it
|
|
28
|
+
has none to contain against — a raise there would be a permanent lockout and a
|
|
29
|
+
rescue would mis-root the bundle and disarm the journal guard.
|
|
30
|
+
|
|
31
|
+
# What arrived
|
|
32
|
+
|
|
33
|
+
`Event` is a class rather than a module, and it is the only place untrusted
|
|
34
|
+
input is parsed. It answers `tool_name`, `tool_input`, `file_path`, `command`,
|
|
35
|
+
`cwd`, `added_text`, `stop_hook_active?` — and `parse_error?`, which is the
|
|
36
|
+
honest answer to malformed JSON rather than an exception thrown at a gate that
|
|
37
|
+
would then not run.
|
|
38
|
+
|
|
39
|
+
`Target.for(event)` turns an event into a root plus a relative path, or `nil`
|
|
40
|
+
when the check simply does not apply — an edit outside any bundle is not a
|
|
41
|
+
failure, and a gate that treated it as one would refuse every edit in the
|
|
42
|
+
repository. `Target#read` and `#exist?` are how a gate reaches a file inside
|
|
43
|
+
its own root.
|
|
44
|
+
|
|
45
|
+
# The two documents
|
|
46
|
+
|
|
47
|
+
`Board` and `Log` are the parsers, and they are pure — no disk, no stdio.
|
|
48
|
+
|
|
49
|
+
`Board` strips HTML comments first (`strip_comments`, then `visible`), because
|
|
50
|
+
a commented-out row is not on the board and every count downstream depends on
|
|
51
|
+
that being true. It answers `rows`, `count`, `budget`, `targets`, `line_date`,
|
|
52
|
+
`chase_date`, and the three grammar checks — `missing_sections`,
|
|
53
|
+
`date_findings`, `stray_bullets` — against the six `SECTIONS` it knows.
|
|
54
|
+
|
|
55
|
+
`Log` answers the questions the closing gate asks of `log.md`:
|
|
56
|
+
`snapshot_under?` for a given day, `days`, `newest_day`, `latest_snapshot_entry`,
|
|
57
|
+
and `malformed_days`. `DAY_HEADING` and the deliberately looser `DAYISH_HEADING`
|
|
58
|
+
exist as a pair — the second is how a heading that *looks* like a day but is not
|
|
59
|
+
one gets reported instead of silently skipped.
|
|
60
|
+
|
|
61
|
+
The gates that consume all six are [the-gates](/structure/the-gates.md).
|
|
62
|
+
|
|
63
|
+
`Records` is the git question: `staged_violations` asks the index, not the
|
|
64
|
+
working tree, whether a commit rewrites or deletes a past journal day. It is at
|
|
65
|
+
the commit door because that is the only door where the staged tree is the truth.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Component
|
|
3
|
+
title: The gates — one question each, and the two doors that ask them all
|
|
4
|
+
description: Nine files, each answering one invariant, plus `audit` (the CI door) and `state` (the readers' payload) which ask the same questions from a different side.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The files
|
|
11
|
+
|
|
12
|
+
| file | the law or the question |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `lib/okf/pro/reconcile.rb` | **Law 1** — what already says this, while the write is still hot |
|
|
15
|
+
| `lib/okf/pro/budget.rb` | **Law 3** — the cap, and the dormancy question |
|
|
16
|
+
| `lib/okf/pro/closing.rb` | **Law 2** — the stop gate, and the session banner |
|
|
17
|
+
| `lib/okf/pro/snapshot.rb` | Law 2's counters, derived — a checker, never a generator |
|
|
18
|
+
| `lib/okf/pro/attestation.rb` | what still awaits the owner's read |
|
|
19
|
+
| `lib/okf/pro/pairing.rb` | the board↔work invariants, both directions |
|
|
20
|
+
| `lib/okf/pro/conformance.rb` | `okf validate` + `okf lint`, in process |
|
|
21
|
+
| `lib/okf/pro/audit.rb` | the CI door: the same invariants minus the tool event |
|
|
22
|
+
| `lib/okf/pro/state.rb` | the readers' payload — cheap by contract |
|
|
23
|
+
|
|
24
|
+
The laws themselves are [design/three-laws](/design/three-laws.md); this concept
|
|
25
|
+
is where each one is implemented.
|
|
26
|
+
|
|
27
|
+
# Snapshot is a checker, not a generator
|
|
28
|
+
|
|
29
|
+
`Snapshot.counters` recomputes every number from the board and the bundle;
|
|
30
|
+
`Snapshot.line` renders it; `Snapshot.parse` reads a line already written; and
|
|
31
|
+
`Snapshot.verify` compares the two and reports the difference. There is no
|
|
32
|
+
`--write`, and there deliberately never will be: a writer and a checker sharing
|
|
33
|
+
a code path agree trivially, which is exactly what Law 2's confession must not
|
|
34
|
+
be able to do.
|
|
35
|
+
|
|
36
|
+
# Conformance is the kernel's answer, not a second one
|
|
37
|
+
|
|
38
|
+
`Conformance.check` runs the kernel's validator and linter in process against
|
|
39
|
+
the target's bundle. Every conformance and curation question is okf's to answer
|
|
40
|
+
— this gem owns the policy on top. The one rule it adds is the contract's third
|
|
41
|
+
clause: `Linter.call` with no options skips `expired` and `stale` and still
|
|
42
|
+
reports `healthy?`, so `confession` surfaces what was not run rather than
|
|
43
|
+
reporting clean over a silent skip.
|
|
44
|
+
|
|
45
|
+
# Two doors, one set of invariants
|
|
46
|
+
|
|
47
|
+
`Audit.call` is the CI door. It asks `structure`, `conformance`, `curation`,
|
|
48
|
+
`snapshot` and `pairing` of a tree with no tool event in sight, and it reserves
|
|
49
|
+
exit 1 for *findings* — spelling "the checker broke" as 2, because a pipeline
|
|
50
|
+
that cannot tell those apart learns to ignore both. `ambiguous_layout` is its
|
|
51
|
+
refusal when it cannot tell which bundle it was pointed at.
|
|
52
|
+
|
|
53
|
+
`State.call` is the other side: what is on the board, in one call, cheap by
|
|
54
|
+
contract. `full: true` is the one parse it will pay for — the readers exist
|
|
55
|
+
because an agent working in a seeded bundle rediscovered the board by reading
|
|
56
|
+
raw markdown while the stop gate was already computing it.
|
|
57
|
+
|
|
58
|
+
# Pairing holds the one git shell-out
|
|
59
|
+
|
|
60
|
+
`Pairing.failures` asks both directions at once: a project with no board line,
|
|
61
|
+
a board line with no project, a target that does not resolve, a link to a
|
|
62
|
+
closed project, a read line gone stale. `MARKER` and `NEGATED` are a pair — the
|
|
63
|
+
second is why "never closed" is not read as a closure marker. `dirty_markdown?`
|
|
64
|
+
is the git shell-out, and it is the only one outside `records.rb`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Component
|
|
3
|
+
title: The guards — the trust rules, and the same rules at the shell door
|
|
4
|
+
description: Two files asking the same questions at two doors, because a `cat > concept.md` is seen by neither the Edit tool nor the file-path checks.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The files
|
|
11
|
+
|
|
12
|
+
| file | the door |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `lib/okf/pro/guards.rb` | an `Edit` or `Write` tool event: the trust rules on the text about to land |
|
|
15
|
+
| `lib/okf/pro/shell_guard.rb` | a `Bash` tool event: the same rules, read out of a command line |
|
|
16
|
+
|
|
17
|
+
# What they ask
|
|
18
|
+
|
|
19
|
+
`Guards.attests?` is the trust question — does this text set `verified:`?
|
|
20
|
+
`ATTESTATION` matches the block form, `FLOW_ATTESTATION` the inline-mapping
|
|
21
|
+
form, and both exist because a rule that only knew one spelling waved the other
|
|
22
|
+
through. `guard_verified` is the gate that uses them; `journal_guard` is the
|
|
23
|
+
append-only rule for `journal/YYYY-MM-DD.md`, which `JOURNAL_ENTRY` recognises.
|
|
24
|
+
|
|
25
|
+
`ShellGuard.check` reads the same rules out of a command. `MUTATORS` is the
|
|
26
|
+
list of commands that can write, `SEPARATORS` splits a compound command so a
|
|
27
|
+
mutator after `&&` is not missed, `MARKDOWN` finds the paths, and `own_write?`
|
|
28
|
+
is what keeps `okf pro` verbs from being refused by the guard that exists to
|
|
29
|
+
make people use them.
|
|
30
|
+
|
|
31
|
+
# Why there are two
|
|
32
|
+
|
|
33
|
+
A gate reads a tool event. A shell redirect is not one — a `cat > board.md`
|
|
34
|
+
carries no `file_path`, so `guard_verified` never sees it and `Target.for`
|
|
35
|
+
returns nothing to check. Without the shell door the whole trust layer is one
|
|
36
|
+
`bash -c` away from being off, which is the shape every defect in this gem has
|
|
37
|
+
had: not a wrong answer, a *no* answer wearing the costume of a clean one.
|
|
38
|
+
|
|
39
|
+
The write verbs the guards are protecting are [the-writers](/structure/the-writers.md).
|
|
40
|
+
|
|
41
|
+
That is also why the write verbs refuse text spanning lines rather than
|
|
42
|
+
escaping it — a verb invoked through Bash is seen by neither guard, so the
|
|
43
|
+
safety there is by construction rather than by inspection.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Component
|
|
3
|
+
title: The recorder — telemetry that refuses nothing and still may not lie
|
|
4
|
+
description: "`friction` counts what was done by hand that a verb could have done, at paths that already run; it blocks nothing, and it may not report a zero it did not count."
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The file
|
|
11
|
+
|
|
12
|
+
| file | what it owns |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `lib/okf/pro/friction.rb` | the friction log: record, report, classify, clear |
|
|
15
|
+
|
|
16
|
+
# What it is, and what it is not
|
|
17
|
+
|
|
18
|
+
It is telemetry about the *tooling*, not about the knowledge base. It refuses
|
|
19
|
+
nothing and blocks nothing, and it is **not** one of `okf pro state`'s sources —
|
|
20
|
+
mixing it in would make a reader's payload depend on how the last session was
|
|
21
|
+
typed.
|
|
22
|
+
|
|
23
|
+
Nor does it register a new hook event. `.claude/settings.json` is a seeded file,
|
|
24
|
+
so a registration added there would never reach an existing adopter through
|
|
25
|
+
`upgrade`. The recorder rides the paths that already run.
|
|
26
|
+
|
|
27
|
+
# It still answers to the third clause
|
|
28
|
+
|
|
29
|
+
A recorder that fails quietly reports a clean session it never observed, which
|
|
30
|
+
is the contract's third clause broken by something that is not even a check.
|
|
31
|
+
So:
|
|
32
|
+
|
|
33
|
+
* a failed write leaves `MARKER` (`okf-pro-friction.unavailable`) behind, and
|
|
34
|
+
`available?` is asked directly rather than inferred from an empty log;
|
|
35
|
+
* an unwritable `.tmp/` is confessed, not shrugged at;
|
|
36
|
+
* an unparseable line is reported rather than skipped.
|
|
37
|
+
|
|
38
|
+
The argument is [telemetry-does-not-lie](/contract/telemetry-does-not-lie.md).
|
|
39
|
+
|
|
40
|
+
# And it may not ask for a verb that is not missing
|
|
41
|
+
|
|
42
|
+
`own_command?` recognises this gem's own invocations — including through
|
|
43
|
+
`WRAPPERS` (`bundle exec`, `env`, `sudo` and the rest) — so `okf pro capture`
|
|
44
|
+
is not recorded as friction against `okf pro capture`. `covered_by` and
|
|
45
|
+
`verb_covered?` decide whether a row names something a verb would actually
|
|
46
|
+
answer: a shell redirect's honest answer is `SHELL_ANSWER` — Edit or Write,
|
|
47
|
+
because the trust guards read a tool event and a redirect is none. The session
|
|
48
|
+
banner counts only the rows a verb would answer, and `--issue` prints nothing
|
|
49
|
+
when nothing was recorded.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Component
|
|
3
|
+
title: The scaffold — what `setup` writes, and who owns it afterwards
|
|
4
|
+
description: "One generator, two template trees, and a split by ownership rather than by subject: `upgrade` rewrites the gem's files and never touches a seeded one."
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The file
|
|
11
|
+
|
|
12
|
+
| file | what it owns |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `lib/okf/pro/scaffold.rb` | `setup`, `upgrade`, `skill` — the generator and its refusals |
|
|
15
|
+
|
|
16
|
+
Plus `lib/okf/pro/template/`, which is not code: it is the tree `setup` writes.
|
|
17
|
+
It ships, and the gemspec says why — a gem without it installs a verb that
|
|
18
|
+
cannot do its job.
|
|
19
|
+
|
|
20
|
+
# Two trees, and the split is ownership
|
|
21
|
+
|
|
22
|
+
`GEM_DIR` (`template/gem/`) holds the machinery: `upgrade` rewrites those files
|
|
23
|
+
in place, every time, without asking. `SEED_DIR` (`template/seed/`) holds the
|
|
24
|
+
adopter's: written once by `setup`, never touched again.
|
|
25
|
+
|
|
26
|
+
Reclassifying `CLAUDE.md`, `.gitignore` or `settings.json` as machinery would
|
|
27
|
+
make `upgrade` destroy exactly the hand-merge `setup` told the adopter to
|
|
28
|
+
perform. The argument is [ownership-not-subject](/scaffold/ownership-not-subject.md).
|
|
29
|
+
|
|
30
|
+
`DOTFILES` maps `gitignore` to `.gitignore` — stored without the dot, because a
|
|
31
|
+
dotfile inside the template is a file `git ls-files` and every glob treat
|
|
32
|
+
differently. `EXECUTABLE` is the two files that must land with the bit set.
|
|
33
|
+
`SUFFIX` (`.okf-pro-new`) is what a collision is written as instead of an
|
|
34
|
+
overwrite; `flat_layout_refusal` is what happens when the destination is not a
|
|
35
|
+
shape this gem can complete.
|
|
36
|
+
|
|
37
|
+
# What must not appear
|
|
38
|
+
|
|
39
|
+
No date ships in the generated bundle, outside a code span — and the exemption
|
|
40
|
+
*is* required rather than cosmetic, because the closure marker has to be taught
|
|
41
|
+
and `Pairing::MARKER` requires a date. Both directions of that coupling are
|
|
42
|
+
pinned by `test/unit/closure_grammar_test.rb`. See [no-date-ships](/scaffold/no-date-ships.md).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Component
|
|
3
|
+
title: The writers — pure transforms, a declared delta, and the refusal that enforces it
|
|
4
|
+
description: Every write verb computes its new text without touching the disk, declares the lines it means to add, remove and move, and is refused if the actual delta differs.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# The files
|
|
11
|
+
|
|
12
|
+
| file | pure? | what it owns |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `lib/okf/pro/conserve.rb` | pure | the write contract, enforced: line multisets in, refusals out |
|
|
15
|
+
| `lib/okf/pro/board/edit.rb` | pure | the board's text transforms, and the keyed selectors |
|
|
16
|
+
| `lib/okf/pro/log/edit.rb` | pure | a dated line, under its day, newest-first |
|
|
17
|
+
| `lib/okf/pro/writes.rb` | shell | the mechanical writers: read, transform, guard, rename |
|
|
18
|
+
|
|
19
|
+
# The shape every verb has
|
|
20
|
+
|
|
21
|
+
Read the file. Compute the new text with a **pure** transform — `Board::Edit` and
|
|
22
|
+
`Log::Edit` cannot touch the disk, which is what makes the transform testable
|
|
23
|
+
without a fixture and unable to half-write. Declare the delta: which lines were
|
|
24
|
+
added, removed, moved. Then `Conserve.check` compares the declared delta against
|
|
25
|
+
the actual one and refuses with exit 2 — **nothing written** — if they differ in
|
|
26
|
+
either direction.
|
|
27
|
+
|
|
28
|
+
That is the enforcement of [derivation-that-writes](/design/derivation-that-writes.md):
|
|
29
|
+
a write verb is additive and targeted, never regenerative. `Conserve.normalize`
|
|
30
|
+
chomps, so a missing final newline is not a delta; `counts` and `subtract` do the
|
|
31
|
+
multiset arithmetic, so a line moved between sections is a move rather than a
|
|
32
|
+
delete plus an add.
|
|
33
|
+
|
|
34
|
+
# What the verbs are
|
|
35
|
+
|
|
36
|
+
`Writes` holds `capture`, `promote`, `demote`, `journal_open` and `close`, and
|
|
37
|
+
each one is a plan-then-commit pair: `plan` computes and conserves, `commit`
|
|
38
|
+
writes atomically. `PROMOTE_FROM` is the two sections a promotion may come from.
|
|
39
|
+
`dormancy_note` is what a demotion says.
|
|
40
|
+
|
|
41
|
+
`Board::Edit`'s selectors are the part worth reading before touching:
|
|
42
|
+
`select` tries `by_target` then `by_substring`, and reports `no_match` or
|
|
43
|
+
`ambiguous` rather than guessing. `BUDGET_LINE` is the header regex
|
|
44
|
+
`set_declared` rewrites, and `NAMES_NOTHING` is the pair of selectors that
|
|
45
|
+
address nothing at all — an empty string and a bare `/projects`.
|
|
46
|
+
|
|
47
|
+
# Three refusals that are not politeness
|
|
48
|
+
|
|
49
|
+
* **A missing file is refused, never created.** `capture` will not create
|
|
50
|
+
`board.md`; `journal open` will not create `journal/index.md`. An index
|
|
51
|
+
rebuilt from the one line a verb knows is regeneration wearing an append's
|
|
52
|
+
clothes.
|
|
53
|
+
* **Text spanning lines is refused, not escaped.** Agent text reaches a board
|
|
54
|
+
line body or a journal entry body and nowhere else.
|
|
55
|
+
* **A caller-supplied name is contained twice** — one directory segment, *and*
|
|
56
|
+
resolving inside the bundle, because `projects/<slug>` can be a symlink out.
|
|
57
|
+
`contained?`, `escaping` and `escape_refusal` are that check; the directions
|
|
58
|
+
are argued in [containment-directions](/contract/containment-directions.md).
|
|
59
|
+
|
|
60
|
+
No verb writes a concept body or sets `verified:`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Playbook
|
|
3
|
+
title: Adding a verb, or a check
|
|
4
|
+
description: The nine steps a new `okf pro` surface owes, in order — where the logic goes, which table it joins, which test file it earns, and which catalogue entry the pin will demand.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-19
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Decide the family first
|
|
11
|
+
|
|
12
|
+
Four families, and they differ in how they are dispatched, what they may accept
|
|
13
|
+
and what they may do. [capabilities/verbs](/capabilities/verbs.md) has the table.
|
|
14
|
+
|
|
15
|
+
* a **check** rides a hook event, reads stdin, and answers in the protocol's
|
|
16
|
+
exit codes — 0 or 2, never 1;
|
|
17
|
+
* a **reader** answers a question about a bundle and takes flags;
|
|
18
|
+
* a **writer** changes a file, and is refused by `Conserve` unless its declared
|
|
19
|
+
delta is exactly what happened;
|
|
20
|
+
* a **scaffold** verb takes a destination, not a bundle.
|
|
21
|
+
|
|
22
|
+
Getting this wrong is not stylistic. A reader that skips `parse_flags` reports
|
|
23
|
+
its caller's typo as a broken bundle; a check that returns 1 is read as
|
|
24
|
+
*proceed*.
|
|
25
|
+
|
|
26
|
+
# The walk
|
|
27
|
+
|
|
28
|
+
1. **Write the failing test first**, in `test/integration/`, in a file named for
|
|
29
|
+
the verb — `cli_<verb>_test.rb`, one file per verb and subcommand. Run it.
|
|
30
|
+
It must fail for the reason you predicted, not because a fixture is missing.
|
|
31
|
+
2. **Put the logic in a module, not in the CLI.** `cli.rb` dispatches, parses
|
|
32
|
+
flags and prints; the question itself belongs in the module that owns that
|
|
33
|
+
part of the world. [structure/](/structure/) says which one, and a genuinely
|
|
34
|
+
new layer earns a new file *and* a line in the concept that owns it.
|
|
35
|
+
3. **Join the right table** in `cli.rb`: `CHECKS` (which puts it in
|
|
36
|
+
`HOOK_NAMES` automatically), or `READERS`, `WRITERS`, `SCAFFOLD`.
|
|
37
|
+
4. **Add its `USAGE` row**, and the matching row in `OKF::CLI::Pro.help_rows`.
|
|
38
|
+
The two are separate literals on purpose — reading `USAGE` from the plugin
|
|
39
|
+
would make every `okf help` load the whole library — and they are held
|
|
40
|
+
together by a test rather than by discipline.
|
|
41
|
+
5. **Declare its flags** in `FLAGS` if it takes any. Absence means "accepts
|
|
42
|
+
none", not "is exempt": every reader routes through `parse_flags` either way.
|
|
43
|
+
6. **A writer owes a pure transform.** Compute the new text in `board/edit.rb`
|
|
44
|
+
or `log/edit.rb` — they cannot touch the disk — declare the added, removed
|
|
45
|
+
and moved lines, and let `Conserve` refuse the mismatch. See
|
|
46
|
+
[the-writers](/structure/the-writers.md).
|
|
47
|
+
7. **A check owes a drill** in `test/integration/wrapper_test.rb` if it adds a
|
|
48
|
+
way for enforcement to be absent. Half the drills there assert a *pass*, and
|
|
49
|
+
yours should too: a wrapper that refused everything would satisfy every
|
|
50
|
+
refusal drill in the file. See [drills-over-units](drills-over-units.md).
|
|
51
|
+
8. **Update the catalogue** — [capabilities/verbs](/capabilities/verbs.md) or
|
|
52
|
+
[capabilities/checks](/capabilities/checks.md). This is not documentation
|
|
53
|
+
etiquette: `test/unit/bundle_catalog_test.rb` compares those tables against
|
|
54
|
+
`CLI::USAGE` and `CLI::HOOK_NAMES`, so the suite is red until you do.
|
|
55
|
+
9. **Run the same test unedited.** If it needed editing to pass, it was written
|
|
56
|
+
after the code and certifies only what the code happens to do.
|
|
57
|
+
|
|
58
|
+
# What the fixture will do to you
|
|
59
|
+
|
|
60
|
+
`BundleFixture` is a **client of the code under test** — `write_log` computes
|
|
61
|
+
its snapshot line by calling `Snapshot.line` — so "the suite is green, therefore
|
|
62
|
+
nothing changed" holds only for changes that do not touch what the fixture
|
|
63
|
+
calls. [fixture-is-a-client](fixture-is-a-client.md) is the full argument.
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Learning
|
|
3
3
|
title: Drills, not unit tests, at the enforcement seam
|
|
4
4
|
description: Every defect found in the entry point was a gate that passed while unable to check, which only a real process with a real exit status can catch.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# What a drill is here
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Learning
|
|
3
3
|
title: The bundle fixture is a client of the code under test
|
|
4
4
|
description: The bundle fixture computes its own snapshot line by calling the code under test, so a change to the counters reaches every fixture carrying a log day — which bounds what a green ported suite proves.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# Why it calls the code
|
data/.okf/testing/index.md
CHANGED
|
@@ -2,3 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
* [drills-over-units](drills-over-units.md) - Every bug in this layer was a gate that passed when it could not do its job.
|
|
4
4
|
* [fixture-is-a-client](fixture-is-a-client.md) - The bundle fixture calls the code under test, so "the ported suite is unchanged proof" has a limit.
|
|
5
|
+
* [adding-a-verb](adding-a-verb.md) - The nine steps a new verb or check owes, in order.
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Decision
|
|
3
3
|
title: A briefing is owed a read until a human has verified it
|
|
4
4
|
description: §5.3's trust tiers replaced a truthiness test on `verified`, and the four call sites that read the rule have to move together or a briefing falls through all of them.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The rule
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
type: Finding
|
|
3
3
|
title: A scalar `verified:` fails in the worst available direction
|
|
4
4
|
description: The guard fires on the word, the owner approves, and then the reader drops the malformed value — so the concept stays unverified and the To-read line is demanded forever.
|
|
5
|
+
generated:
|
|
6
|
+
by: human:maintainer
|
|
7
|
+
at: 2026-08-17
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
# The sequence
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,72 @@ All notable changes to okf-pro are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this gem uses
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [1.1.0] - 2026-08-22
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **The `okf` floor moves to 2.2.0**, with no change to this gem's own code. The
|
|
12
|
+
floor tracks the kernel each sibling develops against and may never lag it, so
|
|
13
|
+
the PR that bumps okf moves every sibling's line — `test/unit/gemspec_test.rb`
|
|
14
|
+
fails until it does. Nothing here reads a 2.2.0 surface yet; the rule is
|
|
15
|
+
defensive, and this is the gem where it matters most, since a missing okf
|
|
16
|
+
method raises inside a check whose crash the hook protocol reads as
|
|
17
|
+
*non-blocking* — a floor that lags does not error here, it stops gating.
|
|
18
|
+
|
|
19
|
+
A floor is part of the published contract, so moving it is a release even when
|
|
20
|
+
no code changed. Hence a version of its own rather than a line riding along
|
|
21
|
+
with the kernel's.
|
|
22
|
+
|
|
23
|
+
## [1.0.1] - 2026-08-20
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **The package metadata follows the `gems/` move and the repository rename.**
|
|
28
|
+
Two URLs on the package page were about to be wrong at once. `changelog_uri`
|
|
29
|
+
named `blob/main/okf-pro/CHANGELOG.md`, a path that stopped existing when the
|
|
30
|
+
four gems moved under `gems/`; `homepage` named `serradura/okf-gem`, and the
|
|
31
|
+
repository is now **`serradura/okf`** — the name the command has had all
|
|
32
|
+
along. rubygems.org serves whatever the last release published, so neither
|
|
33
|
+
corrects itself: only a release republishes metadata, and this is that
|
|
34
|
+
release. Both land in one round rather than two, which is the whole reason
|
|
35
|
+
the rename waited for the paths to stop moving.
|
|
36
|
+
|
|
37
|
+
`homepage_uri`, `source_code_uri` and `changelog_uri` are all derived from
|
|
38
|
+
`homepage`, so one line moves four pieces of this gem's metadata. GitHub
|
|
39
|
+
redirects the old URLs permanently, so anything already published keeps
|
|
40
|
+
resolving.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- **Every concept in the gem's bundle now declares `generated:`.** The actor is
|
|
45
|
+
the maintainer and the date is read from git with `--follow`, so a rename is
|
|
46
|
+
not mistaken for authorship — 27 concepts date to the 1.0.0 release and 10 to
|
|
47
|
+
the structural work after it. `okf lint` on the bundle is clean.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- **The bundle carries the structure and the catalogue, and a test pins them.**
|
|
52
|
+
`.okf/structure/` names every file under `lib/`, grouped by the layer that
|
|
53
|
+
owns it; `.okf/capabilities/` is the catalogue of the sixteen verbs and nine
|
|
54
|
+
checks this gem already answers; `.okf/testing/adding-a-verb.md` is the walk a
|
|
55
|
+
new one owes. `test/unit/bundle_catalog_test.rb` fails when a file under
|
|
56
|
+
`lib/` is named by no concept, when a concept names a file that is gone, or
|
|
57
|
+
when either catalogue disagrees with `CLI::USAGE` or `CLI::HOOK_NAMES`.
|
|
58
|
+
`AGENTS.md` kept the contract and the commands and now routes here for the
|
|
59
|
+
rest — its hand-maintained Map of `lib/**` was the thing nothing checked.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **`AGENTS.md`'s 2.4 floor command pointed at a directory that no longer
|
|
64
|
+
exists** — `cd /build/okf-pro`, from before the gems moved under `gems/`.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- **The okf floor moves to `>= 2.1.1, < 3`** — the kernel this gem develops
|
|
69
|
+
against released 2.1.1, and the floor tracks what the suite proves against
|
|
70
|
+
(the gemspec drill enforces equality as the normal state). Nothing here calls
|
|
71
|
+
a 2.1-only surface; the ceiling is unchanged.
|
|
72
|
+
|
|
7
73
|
## [1.0.0] - 2026-08-17
|
|
8
74
|
|
|
9
75
|
First release. okf-pro begins as a working prototype — a checker carried inside
|
|
@@ -184,4 +250,5 @@ every use. This surface answers both.
|
|
|
184
250
|
- **The `.bin/okf_pro` binary is gone.** `okf pro` is the only door, which is
|
|
185
251
|
what lets the wrapper refuse anything that is not it.
|
|
186
252
|
|
|
187
|
-
[1.0.
|
|
253
|
+
[1.0.1]: https://github.com/serradura/okf/compare/okf-pro/v1.0.0...okf-pro/v1.0.1
|
|
254
|
+
[1.0.0]: https://github.com/serradura/okf/releases/tag/okf-pro%2Fv1.0.0
|
data/README.md
CHANGED
|
@@ -376,7 +376,11 @@ your day; two people sharing one board is not a supported arrangement.
|
|
|
376
376
|
Its own knowledge bundle ships inside the gem, at `.okf/` — the non-obvious
|
|
377
377
|
parts, written down: the three ways the plugin seam let an unchecked edit
|
|
378
378
|
through, the check the gate skipped in silence, and why a stray binary on
|
|
379
|
-
`PATH` cannot be caught by reading an exit code.
|
|
379
|
+
`PATH` cannot be caught by reading an exit code. It is also this gem's
|
|
380
|
+
structural documentation and its catalogue of what the verbs and checks already
|
|
381
|
+
answer, so `okf server .okf` inside an installed copy is a working map of the
|
|
382
|
+
code. Which is the format arguing for itself: the bundle you are being handed
|
|
383
|
+
is one the gem reads with its own tools.
|
|
380
384
|
|
|
381
385
|
## Licence
|
|
382
386
|
|
data/lib/okf/pro/cli.rb
CHANGED
|
@@ -129,7 +129,7 @@ module OKF
|
|
|
129
129
|
# Where a friction report goes. It is about okf-pro, not about the
|
|
130
130
|
# adopter's knowledge base, so it is this repository regardless of whose
|
|
131
131
|
# tree the bundle lives in. Nothing is ever filed automatically.
|
|
132
|
-
ISSUE_REPO = "serradura/okf
|
|
132
|
+
ISSUE_REPO = "serradura/okf"
|
|
133
133
|
|
|
134
134
|
HELP = %w[--help -h help].freeze
|
|
135
135
|
|
data/lib/okf/pro/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: okf-pro
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rodrigo Serradura
|
|
@@ -15,7 +15,7 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 2.2.0
|
|
19
19
|
- - "<"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
21
|
version: '3'
|
|
@@ -25,7 +25,7 @@ dependencies:
|
|
|
25
25
|
requirements:
|
|
26
26
|
- - ">="
|
|
27
27
|
- !ruby/object:Gem::Version
|
|
28
|
-
version:
|
|
28
|
+
version: 2.2.0
|
|
29
29
|
- - "<"
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
31
|
version: '3'
|
|
@@ -48,6 +48,9 @@ executables: []
|
|
|
48
48
|
extensions: []
|
|
49
49
|
extra_rdoc_files: []
|
|
50
50
|
files:
|
|
51
|
+
- ".okf/capabilities/checks.md"
|
|
52
|
+
- ".okf/capabilities/index.md"
|
|
53
|
+
- ".okf/capabilities/verbs.md"
|
|
51
54
|
- ".okf/contract/containment-directions.md"
|
|
52
55
|
- ".okf/contract/exit-codes.md"
|
|
53
56
|
- ".okf/contract/gates-only-at-the-hook-door.md"
|
|
@@ -77,6 +80,15 @@ files:
|
|
|
77
80
|
- ".okf/seam/index.md"
|
|
78
81
|
- ".okf/seam/the-wrapper.md"
|
|
79
82
|
- ".okf/seam/three-fail-opens.md"
|
|
83
|
+
- ".okf/structure/doors.md"
|
|
84
|
+
- ".okf/structure/index.md"
|
|
85
|
+
- ".okf/structure/reading-the-world.md"
|
|
86
|
+
- ".okf/structure/the-gates.md"
|
|
87
|
+
- ".okf/structure/the-guards.md"
|
|
88
|
+
- ".okf/structure/the-recorder.md"
|
|
89
|
+
- ".okf/structure/the-scaffold.md"
|
|
90
|
+
- ".okf/structure/the-writers.md"
|
|
91
|
+
- ".okf/testing/adding-a-verb.md"
|
|
80
92
|
- ".okf/testing/drills-over-units.md"
|
|
81
93
|
- ".okf/testing/fixture-is-a-client.md"
|
|
82
94
|
- ".okf/testing/index.md"
|
|
@@ -139,14 +151,14 @@ files:
|
|
|
139
151
|
- lib/okf/pro/template/seed/gitignore
|
|
140
152
|
- lib/okf/pro/version.rb
|
|
141
153
|
- lib/okf/pro/writes.rb
|
|
142
|
-
homepage: https://github.com/serradura/okf
|
|
154
|
+
homepage: https://github.com/serradura/okf
|
|
143
155
|
licenses:
|
|
144
156
|
- Apache-2.0
|
|
145
157
|
metadata:
|
|
146
158
|
allowed_push_host: https://rubygems.org
|
|
147
|
-
homepage_uri: https://github.com/serradura/okf
|
|
148
|
-
source_code_uri: https://github.com/serradura/okf
|
|
149
|
-
changelog_uri: https://github.com/serradura/okf
|
|
159
|
+
homepage_uri: https://github.com/serradura/okf
|
|
160
|
+
source_code_uri: https://github.com/serradura/okf
|
|
161
|
+
changelog_uri: https://github.com/serradura/okf/blob/main/gems/okf-pro/CHANGELOG.md
|
|
150
162
|
rubygems_mfa_required: 'true'
|
|
151
163
|
rdoc_options: []
|
|
152
164
|
require_paths:
|