Module: OKF::Pro::BundleRoot
- Defined in:
- lib/okf/pro/bundle_root.rb
Overview
Where the bundle is, given where the agent is.
The hook event carries a working directory, which is the repository root
— not the bundle. They stopped being the same thing when the knowledge
moved into .okf/ and the repository kept README, CLAUDE.md, the
checker and CI at the top. Every check that reads the corpus has to make
that hop, and making it in one place is the difference between one rule
and six copies of it drifting apart.
Both layouts resolve, nested first. An adopter who prefers the repository root to be the bundle — the shape this template shipped with — is not broken by the change, and neither are the fixtures.
Constant Summary collapse
- DIR =
".okf"- FENCE =
A fence exactly as the okf gem parses one — dashes at column zero, trailing blanks tolerated, nothing else. One rule, not two that drift: strip-based matching briefly accepted indented fences here, which ended the scan at a "---" inside a YAML block scalar (real root unrecognised, guards re-rooted through the cwd fallback) and recognised roots the gem itself refuses to parse. No BOM tolerance for the same reason — the gem raises on one, and a "root" whose corpus every later check crashes on is not a root.
/\A---[[:blank:]]*\z/.freeze
Class Method Summary collapse
- .bundle?(dir) ⇒ Boolean
-
.containing(path) ⇒ Object
The bundle that contains
path, found by walking up from the file itself — or nil. -
.enclosing(start) ⇒ Object
The bundle governing a working directory, found by walking up from it.
-
.level_root(dir, probe_nested: true) ⇒ Object
The strongest proof at one level, ONCE — both walks consume this, so the ordering cannot drift between them (it did, and the doors governed different bundles).
-
.repo_root(dir) ⇒ Object
The first ancestor (inclusive) holding a .git entry — a directory for ordinary repositories, a file for worktrees and submodules.
-
.resolve(start) ⇒ Object
The bundle root, or nil when
startis not a repository holding one. -
.root_index?(dir) ⇒ Boolean
The root index declares
okf_version:in its frontmatter. -
.root_kind(dir) ⇒ Object
One stat ladder per directory: :strong, :weak, or nil.
Class Method Details
.bundle?(dir) ⇒ Boolean
186 187 188 |
# File 'lib/okf/pro/bundle_root.rb', line 186 def bundle?(dir) File.file?(File.join(dir, "index.md")) end |
.containing(path) ⇒ Object
The bundle that contains path, found by walking up from the file
itself — or nil. The discriminator is level_root/root_kind below:
the innermost strong or anchored root wins outright, and plain weak
candidates resolve by fencing, so journal/index.md and friends do
not stop the walk. This exists because scoping trust
guards through the event's cwd was a hole: cwd is wherever the
session happens to sit, resolve() never walks up, and a session
parked in a subdirectory made the guards find no bundle and
silently disarm.
Fenced at the file's own repository, exactly as enclosing() is: a
bundle above the session's repo is somebody else's, and a file
inside a nested repository must not summon the guards of the tree
above it. Outside any repository the walk runs to the filesystem
root — a file physically inside a bundle's tree is governed by it,
which is the walk's whole claim; the directory walk cannot say the
same of a cwd, which is why enclosing() refuses to walk unfenced.
ONE deliberate divergence from enclosing(): probe_nested: false.
A root that does not contain the file is no root of it, so this
walk never adopts a sibling .okf. In the one layout where that
matters — a directory that is BOTH a flat root and the parent of
an .okf root — this door governs a file outside .okf by the
flat root while the stop gate and the audit govern .okf. That
layout is ambiguous at its source (two bundles, one directory),
the alternative was guards that skipped the file in silence, and
Audit.ambiguous_layout reports it rather than leaving the doors to
disagree quietly.
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 |
# File 'lib/okf/pro/bundle_root.rb', line 62 def containing(path) return nil if path.to_s.empty? dir = File.dirname(File.(path)) top = repo_root(dir) weak = nil until dir == File.dirname(dir) kind, found = level_root(dir, probe_nested: false) case kind when :strong, :anchored return found when :weak # Fenced, the outermost weak directory inside the fence is the # bundle — a real root contains its directories. Unfenced there # is no "outermost inside" to speak of, and climbing toward the # filesystem root adopted a stranger's index+log over the # bundle the file actually lives in — so the nearest weak root # wins and the walk stops guessing upward. return found if top.nil? weak = found end break if dir == top dir = File.dirname(dir) end weak end |
.enclosing(start) ⇒ Object
The bundle governing a working directory, found by walking up from it. Same classifier, same tiers as containing(); the only difference is the no-repository rule — a cwd, unlike a file, is not "inside" anything in a way that justifies climbing, so with no fence the look is single-level and cannot adopt anything above the directory it was handed.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/okf/pro/bundle_root.rb', line 97 def enclosing(start) dir = File.(start.to_s) top = repo_root(dir) if top.nil? kind, found = level_root(dir) return kind ? found : nil end weak = nil loop do kind, found = level_root(dir) case kind when :strong, :anchored return found when :weak weak = found end break if dir == top dir = File.dirname(dir) end weak end |
.level_root(dir, probe_nested: true) ⇒ Object
The strongest proof at one level, ONCE — both walks consume this,
so the ordering cannot drift between them (it did, and the doors
governed different bundles). probe_nested: is the walks' one
legitimate difference: enclosing roots a CWD, and the bundle
governing a session may sit beside it at any ancestor level, so
each level's .okf is probed. containing roots a FILE, and a
root that does not contain the file is no root of it — the
sibling probe adopted repo/.okf for repo/reference/x.md, the rel
path nil'd out, and every write-time guard skipped the edit in
silence. A file actually inside .okf meets it as the dir itself
on the way up, so containing loses nothing by not probing.
Three tiers, nested .okf (when probed) before the level itself:
:strong — okf_version in the index frontmatter. Innermost wins,
immediately: only a root carries it.
:anchored — a weak proof in a directory literally named `.okf`.
The name is the convention's own marker, so an inner
nested bundle beats outer coincidence — outer-weak-
wins let a stray index+log at the repo root shadow a
real frontmatterless .okf and silently disarm every
write-time guard.
:weak — index.md plus any core file (board.md or log.md) in
an ordinarily named directory. Resolution differs by
fencing; see the walks.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/okf/pro/bundle_root.rb', line 155 def level_root(dir, probe_nested: true) nested = File.join(dir, DIR) nested_kind = probe_nested ? root_kind(nested) : nil return [ :strong, nested ] if nested_kind == :strong dir_kind = root_kind(dir) return [ :strong, dir ] if dir_kind == :strong return [ :anchored, nested ] if nested_kind == :weak case dir_kind when :weak File.basename(dir) == DIR ? [ :anchored, dir ] : [ :weak, dir ] end end |
.repo_root(dir) ⇒ Object
The first ancestor (inclusive) holding a .git entry — a directory for ordinary repositories, a file for worktrees and submodules.
123 124 125 126 127 128 129 130 |
# File 'lib/okf/pro/bundle_root.rb', line 123 def repo_root(dir) until dir == File.dirname(dir) return dir if File.exist?(File.join(dir, ".git")) dir = File.dirname(dir) end nil end |
.resolve(start) ⇒ Object
The bundle root, or nil when start is not a repository holding one.
Nil is a real answer: a check that cannot find a bundle has nothing to
say about it, which is different from finding one and approving of it.
25 26 27 28 29 30 31 32 33 |
# File 'lib/okf/pro/bundle_root.rb', line 25 def resolve(start) base = File.(start.to_s) nested = File.join(base, DIR) return nested if bundle?(nested) return base if bundle?(base) nil end |
.root_index?(dir) ⇒ Boolean
The root index declares okf_version: in its frontmatter. Matching
the bare token anywhere in the file was a hole: directory indexes are
free-form prose, this bundle is about OKF, and a journal index that
mentioned okf_version in a sentence would have stopped the walk early,
mis-rooted the bundle, and disarmed the journal guard through a rel
path that no longer started with journal/. Only the key, only inside
a COMPLETE leading frontmatter block, counts — the closing fence is
required, because a mangled block that never closes must not let a
column-zero mention in prose adopt the directory. The block is read
once (no re-enumeration racing an agent's write between two opens)
through Pro.read_text, and bounded: frontmatter whose closing fence
sits past line 101 is not recognised, a stated limit rather than a
silent one, and pinned by a test.
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/okf/pro/bundle_root.rb', line 213 def root_index?(dir) index = File.join(dir, "index.md") return false unless File.file?(index) # Streamed, not slurped: this runs per ancestor on every tool-use # event, and read_text pulled the whole file in before the 101-line # bound was applied. The per-line scrub matches read_text's # defense — bytes in, forced UTF-8, invalid sequences replaced. lines = [] File.foreach(index, mode: "rb") do |l| lines << l.force_encoding(Encoding::UTF_8).scrub.chomp break if lines.size >= 101 end return false unless lines.first&.match?(FENCE) body = lines.drop(1) close = body.index { |l| l.match?(FENCE) } return false if close.nil? body.first(close).any? { |l| l.match?(/\Aokf_version[[:blank:]]*:/) } rescue Errno::ENOENT # The file passed File.file? and vanished before the read — an agent # write or checkout mid-hook. Not a root anybody can prove; keep # walking. false end |
.root_kind(dir) ⇒ Object
One stat ladder per directory: :strong, :weak, or nil. The weak proof accepts ONE core file, not the whole skeleton — demanding both left a mid-bootstrap bundle (index and board down, log.md not yet written) unguarded while its first concepts landed. An index.md truly alone remains ambiguous with a directory index and is never adopted: the scaffold writes the skeleton in one move, and a walk that guessed would re-open the bogus-adoption hole.
177 178 179 180 181 182 183 184 |
# File 'lib/okf/pro/bundle_root.rb', line 177 def root_kind(dir) return nil unless File.file?(File.join(dir, "index.md")) return :strong if root_index?(dir) return unless File.file?(File.join(dir, "board.md")) || File.file?(File.join(dir, "log.md")) :weak end |