okf-pro 1.1.1 → 1.2.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 +7 -1
- data/.okf/log.md +11 -0
- data/.okf/structure/the-gates.md +18 -0
- data/CHANGELOG.md +32 -0
- data/lib/okf/pro/cli.rb +2 -2
- data/lib/okf/pro/closing.rb +7 -1
- data/lib/okf/pro/conformance.rb +69 -11
- data/lib/okf/pro/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f4b14821d80f4db8b76a6634a53b03fa87d8f04a279c197ed70bcfecb4e5ce1
|
|
4
|
+
data.tar.gz: 0ccde1c91b1e1156265e3a380c3c3ed6484cd66a70426bb3ff02bc761ad0ee1c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90264f22dd742779c0600a9215e2e47c2269dd131e4de7f71d066974044e0dd0d23ba2eb966318b23f4703076dbda8e1a3e91d18d8a1c5c7d5bae08a2fbde4a5
|
|
7
|
+
data.tar.gz: daf911ed01988929bba82c7f056e94ae73c55f5c4979dbcf91c0c09c34012677b95a6547a1400c775611d02c4eb13ed42696a70cf0abede7377b71ebca7b28d9
|
data/.okf/capabilities/checks.md
CHANGED
|
@@ -21,7 +21,7 @@ never blocks, and reports "clean." That failure is [gates-only-at-the-hook-door]
|
|
|
21
21
|
| `journal-guard` | `PreToolUse` on `Edit\|Write\|MultiEdit` | is this rewriting a past journal day? |
|
|
22
22
|
| `shell-guard` | `PreToolUse` on `Bash` | is this command about to write into the bundle behind the other two? |
|
|
23
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? |
|
|
24
|
+
| `check-okf` | (composed into `post-edit`) | does the bundle still validate and lint — minus the four findings one edit cannot decide? |
|
|
25
25
|
| `cap-check` | (composed into `post-edit`) | is In flight over the cap? |
|
|
26
26
|
| `reconcile-search` | (composed into `post-edit`) | what already says this? (**Law 1**) |
|
|
27
27
|
| `stop-gate` | `Stop` | is the day's snapshot line written, and does it agree with the board? (**Law 2**) |
|
|
@@ -32,6 +32,12 @@ never blocks, and reports "clean." That failure is [gates-only-at-the-hook-door]
|
|
|
32
32
|
`post-edit` instead, because the bundle read is the expensive part and three
|
|
33
33
|
separate invocations paid for it three times over.
|
|
34
34
|
|
|
35
|
+
Both of the doors that reach `check-okf` ask it for `scope: :edit`, so the four
|
|
36
|
+
set-scoped findings are withheld there and confessed rather than dropped —
|
|
37
|
+
[the-gates](/structure/the-gates.md) has the argument. `stop-gate` and `audit`
|
|
38
|
+
ask the same questions with the write set complete, which is the only place
|
|
39
|
+
they can be answered.
|
|
40
|
+
|
|
35
41
|
# What they do when they cannot answer
|
|
36
42
|
|
|
37
43
|
This is the half that matters, and it is the contract rather than a courtesy.
|
data/.okf/log.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## 2026-08-30
|
|
4
4
|
|
|
5
|
+
* **A per-edit check now only asks what one edit can answer.** Four of
|
|
6
|
+
`Conformance`'s findings — `broken_link`, `broken_index_entry`, `orphan`,
|
|
7
|
+
`not_in_index` — are questions about a set of files, and no write order
|
|
8
|
+
avoids them at the per-edit door: write the index first and its entry is
|
|
9
|
+
broken, write the concept first and it is the orphan. They are withheld
|
|
10
|
+
under `scope: :edit`, confessed by count rather than dropped, and asked at
|
|
11
|
+
the Stop gate and in `audit` where the write set is complete
|
|
12
|
+
([structure/the-gates](/structure/the-gates.md)). The Stop gate ran no
|
|
13
|
+
conformance at all before this, so half the change is an addition rather
|
|
14
|
+
than a move.
|
|
15
|
+
|
|
5
16
|
* **The scaffold verbs go through `parse_flags` too, and the catalogue says so.**
|
|
6
17
|
[capabilities/verbs](/capabilities/verbs.md) described the family by what it
|
|
7
18
|
does *not* do: no `dir_argument`, because `setup` into an empty directory is
|
data/.okf/structure/the-gates.md
CHANGED
|
@@ -42,6 +42,24 @@ clause: `Linter.call` with no options skips `expired` and `stale` and still
|
|
|
42
42
|
reports `healthy?`, so `confession` surfaces what was not run rather than
|
|
43
43
|
reporting clean over a silent skip.
|
|
44
44
|
|
|
45
|
+
**The policy on top is a scope.** Four findings — `broken_link`,
|
|
46
|
+
`broken_index_entry`, `orphan`, `not_in_index`, held in `SET_SCOPED` — are
|
|
47
|
+
questions about a *set* of files asked after one of them. An index entry is
|
|
48
|
+
decided by the concept it names; an orphan by whether anything else links it.
|
|
49
|
+
At the per-edit door the write set is incomplete by definition, and no write
|
|
50
|
+
order avoids that: write the index first and the entry is broken, write the
|
|
51
|
+
concept first and it is the orphan. So `check(target, scope: :edit)` withholds
|
|
52
|
+
those four, and the per-edit door is the only caller that asks for it.
|
|
53
|
+
|
|
54
|
+
Withholding is not dropping, which is the same clause again. `deferral` says
|
|
55
|
+
how many were withheld and names the two doors that ask them with the write set
|
|
56
|
+
complete — `Closing.stop_gate`, through `Conformance.findings` on the bundle it
|
|
57
|
+
has already parsed, and `Audit`, which never scoped anything and needed no
|
|
58
|
+
change. `findings` takes a bundle and an optional `rel:` rather than a Target
|
|
59
|
+
because neither of those doors has an edited file to be the subject: without a
|
|
60
|
+
`rel` there is no "your edit" and no "elsewhere", and inventing the distinction
|
|
61
|
+
would file every finding under a heading saying somebody else did it.
|
|
62
|
+
|
|
45
63
|
# Two doors, one set of invariants
|
|
46
64
|
|
|
47
65
|
`Audit.call` is the CI door. It asks `structure`, `conformance`, `curation`,
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ 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.2.0] - 2026-08-30
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **The per-edit checks only ask what one edit can decide.** Four of the
|
|
12
|
+
findings `check-okf` reported are questions about a *set* of files, asked
|
|
13
|
+
after one of them: `broken_index_entry` and its validator twin `broken_link`
|
|
14
|
+
are decided by the concept an index names, and `orphan` and `not_in_index` by
|
|
15
|
+
whether anything else in the bundle links or lists the file. No write order
|
|
16
|
+
avoids them at that door — write the index first and its entry is broken,
|
|
17
|
+
write the concept first and it is the orphan — so every one of them there was
|
|
18
|
+
noise by construction, on the hook that fires on every edit.
|
|
19
|
+
|
|
20
|
+
`Conformance.check` takes a `scope:` now. The per-edit door asks for `:edit`
|
|
21
|
+
and withholds those four; everything else asks for `:all` and is unchanged.
|
|
22
|
+
|
|
23
|
+
- **The Stop gate asks the four instead**, which is the half of this that is an
|
|
24
|
+
addition rather than a move: `Closing.stop_gate` ran the snapshot, the board
|
|
25
|
+
grammar and the pairing invariants and **no conformance at all**. It now runs
|
|
26
|
+
it over the bundle it has already parsed, so the one-parse-per-Stop pin still
|
|
27
|
+
holds at one. `okf pro audit` needed no change — it never scoped anything.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **A withheld finding is confessed, not dropped.** The edit door reports how
|
|
32
|
+
many findings needed the whole write set and names the doors that ask them,
|
|
33
|
+
because a gate that quietly stops reporting converts "unchecked" into
|
|
34
|
+
"checked and fine" — the failure the contract's third clause exists to name.
|
|
35
|
+
Nothing is said when nothing was withheld: a warning that is always present
|
|
36
|
+
carries no information.
|
|
37
|
+
|
|
7
38
|
## [1.1.1] - 2026-08-30
|
|
8
39
|
|
|
9
40
|
### Fixed
|
|
@@ -276,6 +307,7 @@ every use. This surface answers both.
|
|
|
276
307
|
- **The `.bin/okf_pro` binary is gone.** `okf pro` is the only door, which is
|
|
277
308
|
what lets the wrapper refuse anything that is not it.
|
|
278
309
|
|
|
310
|
+
[1.2.0]: https://github.com/serradura/okf/compare/okf-pro/v1.1.1...okf-pro/v1.2.0
|
|
279
311
|
[1.1.1]: https://github.com/serradura/okf/compare/okf-pro/v1.1.0...okf-pro/v1.1.1
|
|
280
312
|
[1.1.0]: https://github.com/serradura/okf/compare/okf-pro/v1.0.1...okf-pro/v1.1.0
|
|
281
313
|
[1.0.1]: https://github.com/serradura/okf/compare/okf-pro/v1.0.0...okf-pro/v1.0.1
|
data/lib/okf/pro/cli.rb
CHANGED
|
@@ -21,7 +21,7 @@ module OKF
|
|
|
21
21
|
"guard-verified" => ->(event) { Guards.guard_verified(event) },
|
|
22
22
|
"journal-guard" => ->(event) { Guards.journal_guard(event) },
|
|
23
23
|
"shell-guard" => ->(event) { ShellGuard.check(event) },
|
|
24
|
-
"check-okf" => ->(event) { Conformance.check(Target.for(event)) },
|
|
24
|
+
"check-okf" => ->(event) { Conformance.check(Target.for(event), scope: :edit) },
|
|
25
25
|
"cap-check" => ->(event) { Budget.cap_check(Target.for(event)) },
|
|
26
26
|
"reconcile-search" => ->(event) { Reconcile.search(Target.for(event), event) },
|
|
27
27
|
"post-edit" => ->(event) { CLI.post_edit(event) },
|
|
@@ -154,7 +154,7 @@ module OKF
|
|
|
154
154
|
# covers count — an Edit to a concept body is judgment and always will
|
|
155
155
|
# be, and counting it would report the system working as friction.
|
|
156
156
|
Friction.record(target.root, "edit", target.rel) if target && Friction.covered_path?(target.rel)
|
|
157
|
-
Conformance.check(target) + Budget.cap_check(target) + Reconcile.search(target, event)
|
|
157
|
+
Conformance.check(target, scope: :edit) + Budget.cap_check(target) + Reconcile.search(target, event)
|
|
158
158
|
end
|
|
159
159
|
|
|
160
160
|
def run(argv, stdin: $stdin, stdout: $stdout, stderr: $stderr)
|
data/lib/okf/pro/closing.rb
CHANGED
|
@@ -39,7 +39,8 @@ module OKF
|
|
|
39
39
|
# check below — this gate runs on every Stop, and it used to pay for
|
|
40
40
|
# both twice.
|
|
41
41
|
board = Pro.read_text(File.join(root, "board.md"))
|
|
42
|
-
|
|
42
|
+
bundle = ::OKF::Bundle::Reader.read(root)
|
|
43
|
+
concepts = bundle.concepts
|
|
43
44
|
|
|
44
45
|
msgs = []
|
|
45
46
|
snap = Log.snapshot_line(Pro.read_text(File.join(root, "log.md")), today.to_s)
|
|
@@ -57,6 +58,11 @@ module OKF
|
|
|
57
58
|
# just verified would agree with itself about a deadline it cannot
|
|
58
59
|
# see. The unreadable line is a refusal, not a rounding error.
|
|
59
60
|
msgs.concat(Board.grammar(board).map { |m| "— board: #{m}" })
|
|
61
|
+
# The four findings the per-edit door withheld, asked where the write
|
|
62
|
+
# set is finally complete. `Conformance` takes the bundle this gate has
|
|
63
|
+
# already parsed rather than a Target: there is no edited file here to
|
|
64
|
+
# be the subject, and a second read would break the one-parse pin.
|
|
65
|
+
msgs.concat(Conformance.findings(bundle).map { |m| "— #{m}" })
|
|
60
66
|
msgs.concat(Pairing.failures(root, board: board, concepts: concepts))
|
|
61
67
|
return [] if msgs.empty?
|
|
62
68
|
|
data/lib/okf/pro/conformance.rb
CHANGED
|
@@ -6,12 +6,36 @@ module OKF
|
|
|
6
6
|
# answer the same questions at the cost of two interpreter boots per edit;
|
|
7
7
|
# the analyzers are pure and take the bundle directly.
|
|
8
8
|
module Conformance
|
|
9
|
+
# The four findings that are questions about a SET of files, not about
|
|
10
|
+
# the one just written. An index entry is decided by the concept it
|
|
11
|
+
# names; an orphan by whether anything else in the bundle links it. At
|
|
12
|
+
# the per-edit door they are therefore answered against a write set that
|
|
13
|
+
# is incomplete by definition — the concept an index will list does not
|
|
14
|
+
# exist yet while the index is being written, and no write order avoids
|
|
15
|
+
# that, because the reverse order makes the concept the orphan.
|
|
16
|
+
#
|
|
17
|
+
# Three are the linter's and one is the validator's. They are one list
|
|
18
|
+
# because they share a property, not a producer.
|
|
19
|
+
SET_SCOPED = %i[broken_link broken_index_entry orphan not_in_index].freeze
|
|
20
|
+
|
|
9
21
|
module_function
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
# `scope:` is `:all` — every question, for the doors that see the whole
|
|
24
|
+
# write set — or `:edit`, which withholds the four above and says how
|
|
25
|
+
# many it withheld.
|
|
26
|
+
def check(target, scope: :all)
|
|
12
27
|
return [] if target.nil?
|
|
13
28
|
|
|
14
|
-
|
|
29
|
+
findings(target.bundle, rel: target.rel, scope: scope)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The same questions asked of a bundle that is already parsed, and of no
|
|
33
|
+
# file in particular. The Stop gate holds one parse and must not pay for
|
|
34
|
+
# a second — `closing_test.rb` pins the count at one — and it is asking
|
|
35
|
+
# about the write set rather than about an edit, which is why `rel:` is
|
|
36
|
+
# optional here and required nowhere.
|
|
37
|
+
def findings(bundle, rel: nil, scope: :all)
|
|
38
|
+
result = ::OKF::Bundle::Validator.call(bundle)
|
|
15
39
|
unless result.valid?
|
|
16
40
|
lines = result.errors.map { |e| " #{e[:path]}: #{e[:message]}" }
|
|
17
41
|
return [ "okf validate failed after your edit:\n#{lines.join("\n")}" ]
|
|
@@ -34,7 +58,8 @@ module OKF
|
|
|
34
58
|
# They warn rather than refuse: §9's separation is the kernel's, and a
|
|
35
59
|
# gate that turned the validator's warnings into errors would be
|
|
36
60
|
# overruling it from outside.
|
|
37
|
-
|
|
61
|
+
soft_warnings, deferred = sift(result.warnings, scope)
|
|
62
|
+
soft = warn_lines(soft_warnings)
|
|
38
63
|
|
|
39
64
|
# WHAT THIS GATE RUNS, AND WHY IT SAYS SO.
|
|
40
65
|
#
|
|
@@ -63,9 +88,12 @@ module OKF
|
|
|
63
88
|
# `skipped_checks` is then empty, and `confession` below is a live guard
|
|
64
89
|
# rather than a formality: the day okf adds a third clock-gated check,
|
|
65
90
|
# this gate reports it instead of quietly not running it.
|
|
66
|
-
report = ::OKF::Bundle::Linter.call(
|
|
67
|
-
|
|
68
|
-
|
|
91
|
+
report = ::OKF::Bundle::Linter.call(bundle, today: Date.today, except: [ :stale ])
|
|
92
|
+
lint, held = sift(report.warnings, scope)
|
|
93
|
+
deferred += held
|
|
94
|
+
|
|
95
|
+
notes = confession(report) + deferral(deferred) + soft
|
|
96
|
+
return notes if lint.empty? && notes.empty?
|
|
69
97
|
|
|
70
98
|
# Warnings only. Lint's `:info` findings are observations, and a gate
|
|
71
99
|
# that refuses on an observation stops being read as a refusal.
|
|
@@ -79,16 +107,46 @@ module OKF
|
|
|
79
107
|
# pre-existing warning elsewhere used to arrive under the heading
|
|
80
108
|
# "your edit" on every edit in the bundle, and a gate that blames
|
|
81
109
|
# you for what you did not do is a gate people switch off.
|
|
82
|
-
mine, theirs = report.warnings.partition { |w| same_file?(w[:path], target.rel) }
|
|
83
110
|
parts = notes
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
111
|
+
if rel.nil?
|
|
112
|
+
# No edit to be blamed for or exonerated from. The Stop gate asks
|
|
113
|
+
# about the write set, so "yours" and "elsewhere" are not a
|
|
114
|
+
# distinction it can draw, and pretending otherwise would file every
|
|
115
|
+
# finding under a heading that says someone else did it.
|
|
116
|
+
parts << "okf lint findings across the bundle:\n#{format_warnings(lint)}"
|
|
117
|
+
else
|
|
118
|
+
mine, theirs = lint.partition { |w| same_file?(w[:path], rel) }
|
|
119
|
+
parts << "okf lint flagged your edit:\n#{format_warnings(mine)}" unless mine.empty?
|
|
120
|
+
unless theirs.empty?
|
|
121
|
+
parts << "okf lint findings elsewhere in the bundle — pre-existing, or a neighbour " \
|
|
122
|
+
"your edit affected:\n#{format_warnings(theirs)}"
|
|
123
|
+
end
|
|
88
124
|
end
|
|
89
125
|
[ parts.join("\n") ]
|
|
90
126
|
end
|
|
91
127
|
|
|
128
|
+
# Splits a warning list into what this scope answers and a count of what
|
|
129
|
+
# it withheld. The count is what `deferral` confesses; the findings
|
|
130
|
+
# themselves are not carried, because the door that can answer them
|
|
131
|
+
# recomputes them rather than trusting a hand-off.
|
|
132
|
+
def sift(warnings, scope)
|
|
133
|
+
return [ warnings, 0 ] unless scope == :edit
|
|
134
|
+
|
|
135
|
+
kept, held = warnings.partition { |w| !SET_SCOPED.include?(w[:check]) }
|
|
136
|
+
[ kept, held.size ]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Withholding is not dropping, and the difference has to be audible. A
|
|
140
|
+
# gate that silently stopped asking four questions would have converted
|
|
141
|
+
# "unchecked" into "checked and fine" — the one failure this file's
|
|
142
|
+
# whole design is against.
|
|
143
|
+
def deferral(count)
|
|
144
|
+
return [] if count.zero?
|
|
145
|
+
|
|
146
|
+
[ "#{count} finding(s) need the whole write set and were deferred to the Stop gate " \
|
|
147
|
+
"and `okf pro audit`, which see every file this edit will be part of." ]
|
|
148
|
+
end
|
|
149
|
+
|
|
92
150
|
# What the linter did not run, said out loud. Empty in normal operation
|
|
93
151
|
# (see the call above); non-empty means okf grew a check this gate does
|
|
94
152
|
# not know how to supply, and the right answer then IS to stop — an
|
data/lib/okf/pro/version.rb
CHANGED