Module: OKF::Pro::Conformance
- Defined in:
- lib/okf/pro/conformance.rb
Overview
okf validate and okf lint --fail-on warn, in process. The CLI would
answer the same questions at the cost of two interpreter boots per edit;
the analyzers are pure and take the bundle directly.
Class Method Summary collapse
- .check(target) ⇒ Object
-
.confession(report) ⇒ Object
What the linter did not run, said out loud.
- .format_warnings(warnings) ⇒ Object
-
.same_file?(path, rel) ⇒ Boolean
Lint reports a concept id, a bundle-absolute link or a path; the target carries a bundle-relative one.
-
.warn_lines(warnings) ⇒ Object
The validator's soft findings, which are advice rather than a verdict.
Class Method Details
.check(target) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/okf/pro/conformance.rb', line 11 def check(target) return [] if target.nil? result = ::OKF::Bundle::Validator.call(target.bundle) unless result.valid? lines = result.errors.map { |e| " #{e[:path]}: #{e[:message]}" } return [ "okf validate failed after your edit:\n#{lines.join("\n")}" ] end # §9 forbids the validator from REJECTING a soft problem, so these are # warnings rather than errors — and this gate used to read `errors` only # and drop every one of them on the floor. # # That is not a cosmetic loss. It is where the trust family goes wrong # in the worst direction: `verified: human:rod` written as a scalar # fires the attestation guard (the word is there), so the owner is asked # and approves — and then the reader drops the malformed value, the tier # stays `unverified`, and the To-read line is demanded forever. The # validator says exactly what is wrong — *verified should be a mapping # or a list of mappings* — and surfacing it fixes the whole class in one # line, where an okf-pro-side re-implementation of the grammar would be a # second parser to keep in step with okf's. # # They warn rather than refuse: §9's separation is the kernel's, and a # gate that turned the validator's warnings into errors would be # overruling it from outside. soft = warn_lines(result.warnings) # WHAT THIS GATE RUNS, AND WHY IT SAYS SO. # # Two of the linter's checks are clock-gated, and a default # `Linter.call(bundle)` runs neither. Measured on this gate's own path # before this line existed: `skipped_checks: [:expired, :stale]`, # `healthy?: true`. The gate reported clean over two checks it never # ran, and nothing said so — the contract's third clause broken by the # checker itself, in the one place nobody can notice. # # okf 2.0 confesses it in `stats[:skipped_checks]`, a field added for a # reader exactly like this one. The answer is not to relay the # confession as a refusal — a skipped check is not a finding, and a gate # that blocked every edit until someone supplied a cutoff would be off # within a day. It is to leave nothing skipped: # # `today:` the clock `expired` needs. It is `:info`, so this # adds no refusal — it removes a silence. # `except: stale` a decision, stated. `stale` asks "untouched since # when?", and this gem has no cutoff policy and no # business inventing one — that is curation backlog, # which `okf lint` answers when a person runs it and # picks a date. An excluded check is excluded here in # source, which is the opposite of silent. # # `skipped_checks` is then empty, and `confession` below is a live guard # rather than a formality: the day okf adds a third clock-gated check, # this gate reports it instead of quietly not running it. report = ::OKF::Bundle::Linter.call(target.bundle, today: Date.today, except: [ :stale ]) notes = confession(report) + soft return notes if report.healthy? && notes.empty? # Warnings only. Lint's `:info` findings are observations, and a gate # that refuses on an observation stops being read as a refusal. # # And the lint is bundle-WIDE while this gate fires on one edit, so # the report says which is which. Everything is still reported — # dropping the other files' warnings would hide the edit that # breaks a NEIGHBOUR (delete a concept its index still links, and # the warning lands on the index, not on the file you touched) — # but only the edited file's warnings are called yours. A single # pre-existing warning elsewhere used to arrive under the heading # "your edit" on every edit in the bundle, and a gate that blames # you for what you did not do is a gate people switch off. mine, theirs = report.warnings.partition { |w| same_file?(w[:path], target.rel) } parts = notes parts << "okf lint flagged your edit:\n#{format_warnings(mine)}" unless mine.empty? unless theirs.empty? parts << "okf lint findings elsewhere in the bundle — pre-existing, or a neighbour " \ "your edit affected:\n#{format_warnings(theirs)}" end [ parts.join("\n") ] end |
.confession(report) ⇒ Object
What the linter did not run, said out loud. Empty in normal operation (see the call above); non-empty means okf grew a check this gate does not know how to supply, and the right answer then IS to stop — an unchecked bundle reported as clean is the failure this whole file exists to prevent, and a loud unknown beats a quiet one.
97 98 99 100 101 102 103 |
# File 'lib/okf/pro/conformance.rb', line 97 def confession(report) skipped = Array(report.stats[:skipped_checks]) return [] if skipped.empty? [ "okf lint could not run #{skipped.join(", ")} on this bundle (no cutoff supplied), " \ "so those are unchecked rather than clean." ] end |
.format_warnings(warnings) ⇒ Object
114 115 116 |
# File 'lib/okf/pro/conformance.rb', line 114 def format_warnings(warnings) warnings.map { |w| " #{w[:path]}: #{w[:message]} (#{w[:check]})" }.join("\n") end |
.same_file?(path, rel) ⇒ Boolean
Lint reports a concept id, a bundle-absolute link or a path; the target carries a bundle-relative one. Compared on the stem so the three spellings of the same file agree.
121 122 123 124 |
# File 'lib/okf/pro/conformance.rb', line 121 def same_file?(path, rel) stem = ->(s) { s.to_s.downcase.sub(/\A\//, "").sub(/\.md\z/, "") } stem.call(path) == stem.call(rel) end |
.warn_lines(warnings) ⇒ Object
The validator's soft findings, which are advice rather than a verdict.
106 107 108 109 110 111 112 |
# File 'lib/okf/pro/conformance.rb', line 106 def warn_lines(warnings) return [] if warnings.empty? [ "okf validate accepted the bundle with #{warnings.size} warning(s) — conformant, but not " \ "read the way you meant:\n" + warnings.map { |w| " #{w[:path]}: #{w[:message]}" }.join("\n") ] end |