Module: Hecks::Bluebook::MetaValidator::SyntaxBoot
- Defined in:
- lib/hecks/bluebook/meta_validator/syntax_boot.rb
Overview
S14, ADR 0026 — DISPATCHES THE LANGUAGE'S OWN GRAMMAR TABLE INTO
ITSELF, once, so Keyword/Argument's own status genuinely IS a
lifecycle — checked at real dispatch time (the same admission/
coercion/lifecycle-guard door every other command goes through),
not merely declared and never exercised.
THE SOURCE STAYS STATIC. Aggregate-local KeywordSeed/
ArgumentSeed value objects (still hand-written member rows —
now beside the concepts they spell) are what gets WRITTEN ; this
is what turns them into what gets READ. Judge/Reconstruction
already draw exactly this line everywhere else in the meta-domain
(a chapter's own DECLARATIONS versus what gets DISPATCHED from
them) — this runs the same distinction one level further out, for
the language's own grammar table.
A DEDICATED RUNTIME, not MetaValidator.fresh_runtime. That one
is reserved for Judge's own bootstrap (dispatching a CHAPTER's
declarations INTO the meta-domain's grammar) — a different act
from this one (dispatching the meta-domain's OWN grammar table
data into a live "Bluebook" domain instance). Sharing the runtime
would let one boot's own repository state leak into the other's.
Usage:
MetaValidator.syntax_table # => { keywords: [...], arguments: [...] }
each row a plain Hash, string values, status included — the
exact shape ParserTable/syntax_conformance_spec already read
off the OLD closed-set members, so neither consumer had to change
what it does with a row, only where the row comes from.
Class Method Summary collapse
- .admit_arguments(runtime, bluebook) ⇒ Object
-
.admit_keywords(runtime, bluebook) ⇒ Object
to: "Syntax"NAMES THE RECORD ALREADY OPENED BYdeclare_syntaxABOVE — an append onto an existing aggregate, not a second creation of it. -
.all_rows(bluebook, name) ⇒ Object
EVERY AGGREGATE-LOCAL TABLE IN EVERY LOADED LANGUAGE CHAPTER.
- .boot ⇒ Object
-
.call ⇒ Object
Memoized the same way
MetaValidator.grammar_registryitself is — the ~284-command dispatch sequence below is real work, not something three separate callers (ParserTable, syntax_ conformance_spec, bin/reference) should each pay for on every call. -
.declare_syntax(runtime, bluebook) ⇒ Object
Syntax.Declare's ownreference_to Bluebooknames the CHAPTER it belongs to — the same fact every other top-level aggregate's own creating command carries (ProcessManager.Declare,Policy.Declare, ...). -
.optional(text) ⇒ Object
ABSENT STAYS ABSENT.
-
.read_back(runtime, bluebook) ⇒ Object
Reads the dispatched result back into the SAME shape
rowsabove hands the seed data in as — plain hashes, string values,statusincluded — soParserTable/syntax_conformance_specneed not know or care that a real dispatch happened in between. -
.rows(bluebook, name) ⇒ Object
The seed rows themselves — plain hashes, string values, read from every aggregate that owns a same-named local value object.
- .same_chapters?(cached, current) ⇒ Boolean
- .scalar(cell) ⇒ Object
-
.seed_chapters(bluebook) ⇒ Object
Keep the core chapter first, then registry insertion order for every sibling/extension.
- .stringify(row) ⇒ Object
-
.v(text) ⇒ Object
An Integer stays an Integer —
positionisPosition-typed (attribute :value, Integer), so stringifying it fails the type gate rather than feeding it, the same readingJudge#vgives.
Class Method Details
.admit_arguments(runtime, bluebook) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 175 def admit_arguments(runtime, bluebook) all_rows(bluebook, "ArgumentSeed").each_with_index do |row, index| runtime.dispatch("Bluebook::Syntax.Argument", to: "Syntax", with: { position: v(index), keyword: v(row[:keyword]), context: v(row[:context]), at: optional(row[:at]), named: optional(row[:named]), kind: v(row[:kind]), required: v(row[:required]), fills: v(row[:fills]), selects: optional(row[:selects]), pair_key_fills: optional(row[:pair_key_fills]), pair_value_fills: optional(row[:pair_value_fills]), pairs_shape: optional(row[:pairs_shape]), variadic: optional(row[:variadic]), minimum: optional(row[:minimum]), coerce: optional(row[:coerce]), blank_message: optional(row[:blank_message]) }) next unless row[:status].to_s == "deprecated" runtime.dispatch("Bluebook::Syntax.Argument.Deprecate", to: { aggregate: "Syntax", entity: index.to_s }) end end |
.admit_keywords(runtime, bluebook) ⇒ Object
to: "Syntax" NAMES THE RECORD ALREADY OPENED BY declare_syntax
ABOVE — an append onto an existing aggregate, not a second creation
of it. This used to smuggle name: v("Syntax") into the payload
instead, the pre-routing convention Judge#appends (the same
append shape, for ValueObject.Member/ProcessManager.Handler)
already left behind for to:/with: — carrying the receiver in
the payload made Syntax.Keyword's own command.creates? (true:
it declares no reference_to) look like a fresh identity to mint,
which collided with the very "Syntax" row declare_syntax had
just opened.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 158 def admit_keywords(runtime, bluebook) all_rows(bluebook, "KeywordSeed").each_with_index do |row, index| runtime.dispatch("Bluebook::Syntax.Keyword", to: "Syntax", with: { position: v(index), word: v(row[:word]), context: v(row[:context]), body: v(row[:body]), inner: v(row[:inner]), opens: v(row[:opens]), fills: v(row[:fills]), was: optional(row[:was]), resolves_via: optional(row[:resolves_via]), disambiguator: optional(row[:disambiguator]), calls: optional(row[:calls]) }) next unless row[:status].to_s == "deprecated" runtime.dispatch("Bluebook::Syntax.Keyword.Deprecate", to: { aggregate: "Syntax", entity: index.to_s }) end end |
.all_rows(bluebook, name) ⇒ Object
EVERY AGGREGATE-LOCAL TABLE IN EVERY LOADED LANGUAGE CHAPTER.
The table's presence is the registration: no chapter, aggregate,
filename, or context catalog is maintained here. This finds the core
Bluebook concepts, the sibling artifact languages (World, Hecksagon,
Port, Adapter, Translation), and attached sub-languages alike.
Concatenated into ONE sequence because position is minted from the
walk index and must not collide across concepts.
201 202 203 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 201 def all_rows(bluebook, name) seed_chapters(bluebook).flat_map { |chapter| rows(chapter, name) } end |
.boot ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 91 def boot bluebook = MetaValidator.grammar_registry.bluebook("Bluebook") # `MetaValidator.fresh_runtime`, not a brand-new `Runtime:: # Registry` — the grammar registry's own copy already has # "Bluebook" registered (`grammar_registry`'s own boot already # ran `registry.add_bluebook`) AND its adapter ports already # loaded ; a standalone registry would need both wired by hand. # `fresh_runtime` resets `@repositories` on the SAME registry — # the identical isolation `Judge.new` already relies on for # every real domain it judges, proven safe by every dispatch # this session has ever made. runtime = MetaValidator.fresh_runtime declare_syntax(runtime, bluebook) admit_keywords(runtime, bluebook) admit_arguments(runtime, bluebook) read_back(runtime, bluebook) end |
.call ⇒ Object
Memoized the same way MetaValidator.grammar_registry itself
is — the ~284-command dispatch sequence below is real work, not
something three separate callers (ParserTable, syntax_
conformance_spec, bin/reference) should each pay for on every
call.
KEYED BY THE GRAMMAR REGISTRY'S OWN CHAPTER SET, not by a
"ready" flag. boot reads exactly two things: the registry's
"Bluebook" chapter (the core seed rows) and every chapter in the
registry that attaches_to a core context (Paging's rows). So
the table is a pure function of which chapter OBJECTS the
registry holds — and that is the cache key: the same chapters,
by identity, mean the same table; a chapter replaced (the
fixpoint judge swapping a raw chapter for its assembled self)
or added (load_attached_grammar_into) means a fresh boot.
WHY NOT THE EARLIER grammar_registry_ready? GUARD. That guard
was right about the hazard — a snapshot taken mid-build, before
Paging attached, would be missing limit/offset/cursor/nulls
forever — and wrong about the cost of its cure. It refused to
cache ANYTHING until the whole grammar registry had finished
building, calling that window "narrow" and recomputing in it
"cheap". Measured (hecks_ai_training, a six-entity chess domain,
2026-08-22): every word_gate_dispatch landing in that window
re-ran the full ~284-dispatch boot at ~0.76s each — 42 times per
process, 32 of a 35-second Hecks.boot, 95% of which had
nothing to do with the domain being booted. Keying on the
chapter set closes the same hazard by construction (a snapshot
can only be served while the chapters it was read from are
still the chapters the registry holds) and lets the window's
own repeated, identical calls share one boot. A registry reset
(fixpoint_spec's own @grammar_registry = nil) yields new
chapter objects, so it invalidates this the same way it always
invalidated grammar_registry_ready?.
equal?, NOT ==, on the chapters — identity is the fact being
tracked. Holding the chapter objects themselves (not their ids)
in the key also means a collected chapter can never hand its id
to a newcomer behind this cache's back.
76 77 78 79 80 81 82 83 84 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 76 def call chapters = MetaValidator.grammar_registry.bluebooks.to_a return @call if @call && same_chapters?(@call_chapters, chapters) result = boot @call = result @call_chapters = chapters result end |
.declare_syntax(runtime, bluebook) ⇒ Object
Syntax.Declare's own reference_to Bluebook names the CHAPTER
it belongs to — the same fact every other top-level aggregate's
own creating command carries (ProcessManager.Declare,
Policy.Declare, ...). This fresh runtime holds no chapter
record of its own yet (nothing else here needs one), so one is
declared first, named after the real chapter, purely to satisfy
the reference — its own vision/classification are never read
by anything this boot does.
140 141 142 143 144 145 146 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 140 def declare_syntax(runtime, bluebook) syntax = bluebook.aggregate("Syntax") runtime.dispatch("Bluebook::Bluebook.Declare", name: v(bluebook.hecks_name), vision: v("the language's own grammar table, dispatched into itself"), classification: v("core")) runtime.dispatch("Bluebook::Syntax.Declare", bluebook: bluebook.hecks_name, name: v(syntax.hecks_name)) end |
.optional(text) ⇒ Object
ABSENT STAYS ABSENT. A seed row's own optional columns ("was",
"at", "named", ...) are empty strings, not nil — Literal/CSV-
shaped grammar data has no nil to write — so this is the one
place that decides "" means "not given" for the purpose of an
optional: true command argument, the same reading Judge#v
makes for the meta-domain's own dispatches.
126 127 128 129 130 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 126 def optional(text) return nil if text.nil? || text.to_s.empty? v(text) end |
.read_back(runtime, bluebook) ⇒ Object
Reads the dispatched result back into the SAME shape rows
above hands the seed data in as — plain hashes, string values,
status included — so ParserTable/syntax_conformance_spec
need not know or care that a real dispatch happened in between.
230 231 232 233 234 235 236 237 238 239 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 230 def read_back(runtime, bluebook) syntax = bluebook.aggregate("Syntax") repository = runtime.registry.repository("Bluebook", syntax) instance = repository.find(Naming.identity([syntax.hecks_name])) { keywords: Array(instance[:keywords]).map { |row| stringify(row) }, arguments: Array(instance[:arguments]).map { |row| stringify(row) } } end |
.rows(bluebook, name) ⇒ Object
The seed rows themselves — plain hashes, string values, read from every aggregate that owns a same-named local value object. Repeating the value-object shape is deliberate: each concept remains readable by itself, while this discovery is the only grouping mechanism.
217 218 219 220 221 222 223 224 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 217 def rows(bluebook, name) bluebook.aggregates.flat_map do |aggregate| value_object = aggregate.value_objects.find { |vo| vo.hecks_name == name } next [] unless value_object value_object.members.map { |row| row.to_h.transform_values(&:to_s) } end end |
.same_chapters?(cached, current) ⇒ Boolean
86 87 88 89 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 86 def same_chapters?(cached, current) cached.size == current.size && cached.zip(current).all? { |(cached_name, cached_chapter), (name, chapter)| cached_name == name && cached_chapter.equal?(chapter) } end |
.scalar(cell) ⇒ Object
247 248 249 250 251 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 247 def scalar(cell) return cell.to_h.values.first if cell.respond_to?(:to_h) && !cell.is_a?(String) cell end |
.seed_chapters(bluebook) ⇒ Object
Keep the core chapter first, then registry insertion order for every sibling/extension. The name rejection avoids reading the same core object twice without relying on object identity across fixpoint assembly.
209 210 211 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 209 def seed_chapters(bluebook) [bluebook] + MetaValidator.grammar_registry.bluebooks.values.reject { |chapter| chapter.name == bluebook.name } end |
.stringify(row) ⇒ Object
241 242 243 244 245 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 241 def stringify(row) row.to_h.each_with_object({}) do |(key, cell), out| out[key] = scalar(cell).to_s end end |
.v(text) ⇒ Object
An Integer stays an Integer — position is Position-typed
(attribute :value, Integer), so stringifying it fails the type
gate rather than feeding it, the same reading Judge#v gives.
114 115 116 117 118 |
# File 'lib/hecks/bluebook/meta_validator/syntax_boot.rb', line 114 def v(text) return { value: text } if text.is_a?(Integer) { value: text.to_s } end |