okf 1.12.0 → 2.0.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/CHANGELOG.md +296 -0
- data/README.md +94 -466
- data/lib/okf/bundle/folder.rb +48 -3
- data/lib/okf/bundle/graph.rb +12 -3
- data/lib/okf/bundle/linter.rb +470 -47
- data/lib/okf/bundle/reader.rb +47 -18
- data/lib/okf/bundle/references.rb +111 -0
- data/lib/okf/bundle/row_filter.rb +53 -0
- data/lib/okf/bundle/search.rb +20 -2
- data/lib/okf/bundle/validator/result.rb +6 -3
- data/lib/okf/bundle/validator.rb +267 -26
- data/lib/okf/bundle/writer.rb +1 -1
- data/lib/okf/bundle.rb +124 -8
- data/lib/okf/cli/catalog.rb +2 -2
- data/lib/okf/cli/command.rb +93 -19
- data/lib/okf/cli/dirs.rb +1 -1
- data/lib/okf/cli/files.rb +2 -2
- data/lib/okf/cli/index.rb +3 -3
- data/lib/okf/cli/lint.rb +70 -12
- data/lib/okf/cli/references.rb +97 -0
- data/lib/okf/cli/search.rb +23 -8
- data/lib/okf/cli/stats.rb +3 -39
- data/lib/okf/cli/tags.rb +6 -43
- data/lib/okf/cli/types.rb +1 -1
- data/lib/okf/cli/validate.rb +3 -3
- data/lib/okf/cli.rb +4 -1
- data/lib/okf/concept/file.rb +17 -2
- data/lib/okf/concept.rb +362 -10
- data/lib/okf/markdown/citations.rb +41 -4
- data/lib/okf/markdown/frontmatter.rb +1 -1
- data/lib/okf/markdown/links.rb +67 -7
- data/lib/okf/path.rb +17 -3
- data/lib/okf/render/graph/template.html.erb +173 -41
- data/lib/okf/render/graph.rb +11 -3
- data/lib/okf/safe_read.rb +50 -0
- data/lib/okf/server/app.rb +47 -15
- data/lib/okf/server/hub.rb +1 -1
- data/lib/okf/skill/SKILL.md +14 -12
- data/lib/okf/skill/playbooks/curate.md +8 -3
- data/lib/okf/skill/playbooks/doctor.md +3 -1
- data/lib/okf/skill/playbooks/maintain.md +7 -6
- data/lib/okf/skill/playbooks/menu.md +5 -4
- data/lib/okf/skill/playbooks/migrate.md +31 -8
- data/lib/okf/skill/playbooks/produce.md +16 -9
- data/lib/okf/skill/playbooks/search.md +2 -2
- data/lib/okf/skill/reference/SPEC.md +739 -187
- data/lib/okf/skill/reference/authoring.md +154 -35
- data/lib/okf/skill/reference/cli.md +160 -44
- data/lib/okf/skill/templates/attested-computation.md +41 -0
- data/lib/okf/skill/templates/concept.md +13 -6
- data/lib/okf/skill/templates/root-index.md +1 -1
- data/lib/okf/version.rb +1 -1
- data/lib/okf.rb +23 -2
- metadata +7 -3
- data/CODE_OF_CONDUCT.md +0 -10
data/lib/okf/bundle/folder.rb
CHANGED
|
@@ -54,10 +54,30 @@ module OKF
|
|
|
54
54
|
@bundle.hubs
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
def directories
|
|
58
|
+
@bundle.directories
|
|
59
|
+
end
|
|
60
|
+
|
|
57
61
|
def directory_index
|
|
58
62
|
@bundle.directory_index
|
|
59
63
|
end
|
|
60
64
|
|
|
65
|
+
def stats
|
|
66
|
+
@bundle.stats
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def tag_groups(by:, entries: nil)
|
|
70
|
+
@bundle.tag_groups(by: by, entries: entries)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The §6.3 inventory — this handle's half is the disk: the reader models
|
|
74
|
+
# only markdown, so listing `references/` here is how a .py attester or a
|
|
75
|
+
# .sql computation becomes visible at all. The pure model gets the
|
|
76
|
+
# manifest and answers the rest (citers, dangling pointers).
|
|
77
|
+
def references
|
|
78
|
+
Bundle::References.build(@bundle, files: reference_files)
|
|
79
|
+
end
|
|
80
|
+
|
|
61
81
|
# Every log.md with its content, root scope first — read live from disk so a
|
|
62
82
|
# just-appended entry shows without a reload; the reserved snapshot is the
|
|
63
83
|
# fallback if the file has since vanished. Shared by `okf render`'s bake
|
|
@@ -104,7 +124,18 @@ module OKF
|
|
|
104
124
|
Concept::File.read(root: @root, path: path)
|
|
105
125
|
end
|
|
106
126
|
|
|
107
|
-
#
|
|
127
|
+
# The raw markdown bytes for one concept id — read once through the same
|
|
128
|
+
# containment guard as #concept, but without the parse #concept pays for,
|
|
129
|
+
# so a caller that wants the file verbatim (never a re-serialized copy)
|
|
130
|
+
# does one read, not a read plus a discarded frontmatter parse. nil when no
|
|
131
|
+
# concept has that id; raises Path::Error if the file has become a symlink
|
|
132
|
+
# escaping the root, and the reader's own SystemCallError if it has gone.
|
|
133
|
+
def concept_source(id)
|
|
134
|
+
path = @bundle.paths_by_id[id] or return nil
|
|
135
|
+
Concept::File.new(root: @root, path: path).read
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Materialize the in-memory bundle to disk (Writer validates §11 before
|
|
108
139
|
# publishing, so a malformed bundle is never written).
|
|
109
140
|
def save(overwrite: false)
|
|
110
141
|
Writer.call(
|
|
@@ -124,6 +155,16 @@ module OKF
|
|
|
124
155
|
|
|
125
156
|
private
|
|
126
157
|
|
|
158
|
+
# Bundle-relative paths of every file under references/, sorted. A glob
|
|
159
|
+
# on a directory that does not exist is simply empty — no references/ and
|
|
160
|
+
# an empty references/ are the same inventory.
|
|
161
|
+
def reference_files
|
|
162
|
+
Dir.glob(File.join(@root, "references", "**", "*"))
|
|
163
|
+
.select { |path| File.file?(path) }
|
|
164
|
+
.map { |path| Pathname.new(path).relative_path_from(Pathname.new(@root)).to_s }
|
|
165
|
+
.sort
|
|
166
|
+
end
|
|
167
|
+
|
|
127
168
|
def reserved_hash(basename)
|
|
128
169
|
@bundle.reserved
|
|
129
170
|
.select { |entry| File.basename(entry.path) == basename }
|
|
@@ -131,8 +172,12 @@ module OKF
|
|
|
131
172
|
end
|
|
132
173
|
|
|
133
174
|
def log_content(path)
|
|
134
|
-
|
|
135
|
-
|
|
175
|
+
# Live, but through the same containment as every other read: a log.md
|
|
176
|
+
# that was a real file at boot and is a symlink out of the root now falls
|
|
177
|
+
# back to the boot snapshot rather than serving the target — the same
|
|
178
|
+
# answer a vanished file gets, since an escape is a file we must not read.
|
|
179
|
+
SafeRead.read!(@root, File.join(@root, path))
|
|
180
|
+
rescue SystemCallError, Path::Error
|
|
136
181
|
@bundle.reserved_content(path)
|
|
137
182
|
end
|
|
138
183
|
end
|
data/lib/okf/bundle/graph.rb
CHANGED
|
@@ -20,7 +20,7 @@ module OKF
|
|
|
20
20
|
attr_reader :nodes, :edges, :type_index, :tag_index
|
|
21
21
|
|
|
22
22
|
def self.build(bundle, minimal: false, body: true)
|
|
23
|
-
# Best-effort (§
|
|
23
|
+
# Best-effort (§11): a malformed concept never reaches here — the reader keeps
|
|
24
24
|
# it in bundle.unparseable — so the rest of the bundle still renders. Inspect
|
|
25
25
|
# bundle.unparseable to detect skips.
|
|
26
26
|
concepts = bundle.concepts
|
|
@@ -51,10 +51,19 @@ module OKF
|
|
|
51
51
|
# Edges resolve by *path* — a markdown link is a file path — then map that path
|
|
52
52
|
# to the concept living there and use its id, so a frontmatter `id` that differs
|
|
53
53
|
# from the path still lands the edge on the right node.
|
|
54
|
+
# Body links and sources[].resource entries feed the same resolver: §5.1
|
|
55
|
+
# says a `resource` naming another concept is a derivation edge that
|
|
56
|
+
# "already exists in the bundle graph", and this is what makes that true —
|
|
57
|
+
# it is also what keeps a migrated bundle's graph equal to its v0.1 twin's,
|
|
58
|
+
# since a `# Citations` in-bundle link stops being a body link the moment
|
|
59
|
+
# it is lifted into frontmatter. URLs and scope descriptors resolve to
|
|
60
|
+
# nothing; an unresolvable path is broken_source's to report.
|
|
54
61
|
def self.edges_for(concepts, id_by_path, root)
|
|
55
62
|
seen = Set.new
|
|
56
63
|
concepts.each_with_object([]) do |concept, edges|
|
|
57
|
-
Markdown::Links.extract(concept.body)
|
|
64
|
+
targets = Markdown::Links.extract(concept.body) +
|
|
65
|
+
concept.sources.map { |source| source["resource"] }.compact
|
|
66
|
+
targets.each do |raw|
|
|
58
67
|
resolved = Markdown::Links.resolve(raw, from: concept.path, bundle: root)
|
|
59
68
|
next if resolved.nil?
|
|
60
69
|
|
|
@@ -86,7 +95,7 @@ module OKF
|
|
|
86
95
|
concept.tags.is_a?(Array) ? concept.tags : []
|
|
87
96
|
end
|
|
88
97
|
|
|
89
|
-
# Blank, not just nil: §
|
|
98
|
+
# Blank, not just nil: §11 condition 2 makes a whitespace-only `type` as non-conformant
|
|
90
99
|
# as a missing one (the validator says so with the same OKF.blank?), so the
|
|
91
100
|
# index must not sort them into different buckets. Otherwise `type: " "`
|
|
92
101
|
# earns its own row, labelled with spaces, next to Untyped.
|