Plan: the dbspec editor stack as a standard
This plan is the ground of checklist item T128 and its sub-items in the checklist, and of the items it names in kit, template, flowmark and the other polyspec repositories. Its rulings were verified adversarially in thirteen rounds until a round found nothing; a ruling becomes binding when it is written into the specification its item names.
Context
The owner asked for a dbspec highlighter and formatter "like polyspec/template". What went into orm main under T128 does not follow template, and it is wrong:
- Go-only tools:
cmd/orm-fmt,cmd/orm-lsp,cmd/orm-tokens. engine/dbspec/tokens.go, a regex classifier beside the real lexer: a second grammar.- A docs fence that shells out to a Go binary.
orm main is red on 81eed603 in two places:
cifeature-unit-check:cmd/orm-*/main.goselect no behaviour test.docs-pages, pluscidocs-check and docs-verify-idempotent:go build ./cmd/orm-tokensfinds no toolchain.
No release contains these commits: git tag --contains 8b8ac81b prints nothing.
template has its own gaps:
template-languagere-states lexical grammar beside its parser.- The VS Code extension carries a hand-written TextMate grammar for tags.
format()exists only in TS.
Owner rulings:
- Syntax analysis and formatting are standards of every runtime.
- The scope is everything template ships.
- Pre-1.0: destructive changes are allowed; no compatibility layer, fallback or migration.
- The simplest complete implementation.
- Never redevelop what the ecosystem provides.
- Modular, with concerns separated.
- Long-term architecture, never a stopgap.
- Fixing template's gaps is part of the job.
Rulings (written into the specs first, with their grounding)
- One grammar per runtime.
- ORES-1 Each runtime has one dbspec lexer and parser. Analysis is one of the parser's outputs.
- ORES-2 No editor layer, generated file or regex holds a grammar rule. That excludes TextMate grammars: a regex grammar has no parser context, and
NAMEadmits keyword words that are not reserved, such askey,textandreferences, as column names, so it would disagree withanalyze(tableis reserved; corrected by T128-2-2).
- Syntax analysis:
analyze(text, set)in all six implementations.- ORES-3 It returns the parse result (unchanged) and tokens
{line, start, end, class}. - ORES-4 Line is 1-based; start and end are code points, end exclusive.
- ORES-5 Classes:
keyword,type,name,setting,number,string,operator,punctuation,comment,word. - Defined per case:
- ORES-6 a
limiterror yields no tokens, because the check precedes allocation (docs/spec/dbspec.md:301); - ORES-7 after a
headerorencodingstop, the stop line and every later line yield lexical tokens; - ORES-8 in such a lexical-only line a word is
word, and numbers, strings, operators, punctuation and comment lines keep their class; a number is a word of digits with an optional fraction, as Go's lexer reads it (a T128-2 edit that made numberswordfrom the TS lexer alone was wrong and is undone by T128-2-1); - ORES-9 for
encoding, a bare CR is not a line end, and lexing resumes after it; - ORES-10 an invalid character yields no token;
- ORES-11
wordis also a word on a line the parser abandoned (the recovery path). A line that carries only a semantic diagnostic keeps its parser-assigned classes.
- ORES-6 a
- ORES-12 Comment positions live only in the tokens, never in
DbspecDocument. Tokens serve highlighting; declaration positions are in the parse result beside the document (T128-3p). - ORES-13 Vectors:
tests/dbspec/syntax.jsoncovers the canonical, normalize, invalid, sets and hashes cases ofcases.json.filescases (bytes) stay with readFile and readBytes. The vectors are generated once from TS, reviewed, then fixed; all six match them exactly.
- ORES-3 It returns the parse result (unchanged) and tokens
- Formatting:
format(text, set)=emit(parse(text, set))in all six.- ORES-14 A document with diagnostics is refused, never rewritten.
- Invariants:
- ORES-15
emit(parse(format(x))) == emit(parse(x)); - ORES-16 equal manifest and schema hashes;
- ORES-17
format(format(x)) == format(x). - No model equality: emit sorts constraints and
uselines.
- ORES-15
- ORES-18 On canonical text, LSP formatting answers no edit.
- ORES-19 Vectors: normalize and canonical expect the canonical text; invalid expects refusal.
- The document set is declared once, in a set file (owner ruling).
- ORES-20 One standard file,
dbspec-set.json:{"schema": 1, "documents": [...], "external": [...]}. Paths are relative to the file's own directory, which is the declared root. - ORES-21 Every consumer reads it: the generators of every runtime (
orm-gen gen --set <file>) and the LSP. The command-argument form (--document,--use) is removed, with no compatibility. - Grounding: the set has two consumers today. Two declarations drift.
- ORES-22 Set resolution, the ecosystem pattern of
tsconfig.json: a document's set is thedbspec-set.jsonin its nearest ancestor directory that lists it indocuments. Every consumer stops at the same bound, the filesystem root, as tsserver does. The two-set check reads every ancestor up to that bound. This is discovery along the document's own path, never a walk of the disk. File watching handles only later changes: create, change and delete of set files and listed files. - ORES-23 The rules exist once. The browser-safe subpath exports a pure
parseSet(bytes, path)that applies every non-I/O rule. The language service getsresolveSet(path, host)with an injected host forlstat, listing a directory, and opening a file once to give its bytes and its identity; the LSP and the CLI pass anode:fshost. Every runtime's set reader runs the file-tree set vectors; TSparseSetalso runs the vectors that need no file tree (corrected by the review of T128-2a-1: a pure function cannot run vectors of symbolic and hard links). - ORES-24 A document in the
documentsof two set files is a diagnostic.externallistings do not count: an external document of one set is, by definition, owned by another (docs/spec/dbspec.md:245). - ORES-25 A document that no set file lists forms the set of itself alone, and its
uselines report "is not in the declared document set": a true statement, not a fallback. - ORES-26 The language service and CodeMirror take the set as an option.
- ORES-27 Docs fences: fences on one page that form a set declare it in the info string, for example
dbs set=shop, and the page carries every document of the set as its own fence. The docs build fails on any diagnostic of adbsfence, so every published example is a verified document and no diagnostic is dropped. - ORES-28 Validation of the set file, each rule with accepting and rejecting vectors that every runtime's set reader runs:
- ORES-29 invalid JSON is an error;
- ORES-30
schemamust be 1, and an unknown key is an error; - ORES-31
documentsmust not be empty; - ORES-32 paths are in normal form or rejected: relative,
/separators only, no.or empty segment, no trailing/. An absolute path is an error. - ORES-33
documentspaths lie under the set file's directory, with no.....is allowed only as leading segments ofexternalpaths, because external documents live in sibling directories (flowmark's board uses../block/...). - ORES-34 every path component is
lstat-ed, and the path must end in a regular file. A symlink, at the file or in any directory, is an error and is never followed. - ORES-35 duplicates are judged by file identity: device and inode, or the Windows file ID. A file listed twice, under any two spellings or in both lists, is an error on every platform, case-folding file systems included;
- ORES-36 a missing file is an error naming the path. A generator exits with its error status and writes nothing.
- Failures:
- ORES-37 a listed file that fails the signature or encoding check is published on its own URI, with the
readBytesdiagnostic; - ORES-38 a missing or unreadable file is a set error of docs/spec/dbspec.md "Document sets", shown by
window/showMessagewith the path and the OS error; the reader returns no file of a set that fails, so the set is not resolved and the document is analyzed alone, itsuselines reporting their diagnostic. Both stay visible (corrected by T128-5-1: an earlier text kept the other files as a partial set, which the reader specification refuses).
- ORES-37 a listed file that fails the signature or encoding check is published on its own URI, with the
- ORES-39 The LSP reads bytes with
node:fsand passes them to the language service, which checks them withreadDbspecBytesfrom the subpath of ruling 8. - Changes are events, not polling:
- ORES-40
workspace/didChangeWatchedFilesfor set files and listed files re-analyzes; - ORES-41 an open buffer wins over the disk.
- ORES-40
- ORES-20 One standard file,
- LSP ranges.
- ORES-42 A diagnostic's range runs from its position to the end of the token that starts there. Elsewhere it is one code point; at end of line or end of text it is zero-length.
- ORES-43 Code points convert to UTF-16 in the language service only.
- Editor spec
docs/spec/editor.md(+ko).- Layers: the browser-safe parser subpath of
@polyspec/orm(ruling 8), the language service, and the adapters (LSP, CodeMirror, VS Code). - ORES-44 Boundaries, checked by following imports transitively:
- ORES-45 adapters never import
@polyspec/orm; - ORES-46 the language service, outside its CLI, reaches no
node:module, Node global (Buffer,process,__dirname,require) or driver. This matches template's EDT-3. - ORES-47 Adapters run in Node or the browser, as their hosts require.
- ORES-45 adapters never import
- Also: the results and the editor fixtures.
- Layers: the browser-safe parser subpath of
- Ecosystem mechanisms only.
- ORES-48 LSP:
vscode-languageserver. CLI arguments:node:utilparseArgs. Editor: CodeMirror 6 extensions. - ORES-49 VS Code: semantic tokens with
semanticTokenScopesand no grammar file.configurationDefaultssets"[dbspec]": {"editor.semanticHighlighting.enabled": true}, because the defaultconfiguredByThemeleaves some themes uncoloured. - ORES-50 Docs: the markdown-it fence hook of VitePress
markdown.config, as mermaid uses it (config.mts:149). Shiki is not used, because it accepts only TextMate grammars, which ruling 1 excludes. - ORES-51 Each standard class has a colour in both the light and the dark site theme.
- ORES-48 LSP:
- A browser-safe parser entry.
- ORES-52 The
@polyspec/ormpackage gains a subpath export: parse, emit, analyze, format, readDbspecBytes. - ORES-53 The
@polyspec/ormpackage modules hold nonode:module, no Node global and no driver. parseDbspecandemitDbspecmove out ofdbspec/index.ts, which importsnode:crypto.- ORES-54 The
@polyspec/ormpackage'sreadDbspecBytesusesTextEncoderinstead ofBuffer. - ORES-55 The
@polyspec/ormpackage module-graph test rejectsnode:imports and the Node globals.
- ORES-52 The
orm: T128 in the single checklist docs/plans/execution/checklist.md (+ko)
T128 is rewritten, and sub-items T128-1 to T128-9 are added. Each is one commit: RED, GREEN, the owning command, EN/KO docs, and the CHANGELOG. A sub-item becomes [o] with Evidence. The plan text lives in T128's Cause and Acceptance, plus docs/spec/editor.md.
- T128-1 Remove the Go editor tools, as new removal commits; history is not rewritten.
cmd/orm-fmt,cmd/orm-lsp,cmd/orm-tokens.engine/dbspec/tokens.goandtokens_test.go.- The 4
contracts/features.jsonentries. - The Makefile
dbspec-go-check/toolstarget, its comment lines and.PHONYentries. - The Go symbols: run
node tests/interfaces/check/main.mts -record, review that the diff is exactly the 8 removed lines, and take the hash the checker computes. It should equalsha256:a65cb65f…7bed; any other value is a finding to explain, never a value to paste. docs/.vitepress/dbs.ts, theconfig.mtsimport and fence, thetheme/style.cssdbs classes, and the Go step ofscripts/docs/build.mjs.- The
dbsfence indocs/spec/dbspec.mdand.ko.md(T128-7 brings it back). - Done when
cianddocs-pagesare green. - Then tell the request-hook session: its rebase drops
0c8e25c4with its T125-10 item and changelog lines, then re-records through-recordand reviews the diff the same way. T128-1 lands first.
- T128-2 The rulings in the spec.
docs/spec/dbspec.md(+ko) gains "Syntax analysis" and "Formatting".docs/spec/editor.md(+ko) is new.- The T128 Acceptance is rewritten to the rulings.
- T128-2a The set file in every runtime (ruling 4).
dbspec-set.jsonis specified indocs/spec/dbspec.md"Documents and use" (+ko), with the validation vectors.- A public set-reading API in every runtime (
Dbspec.readSet), with its interfaces rule, symbols, parity unit and feature tests. Go'sruntimemodel.LoadFileSetis replaced by it. - Generators take the set file. The forms removed, with no compatibility:
- Go
--document/--use; - TS and Python
--schema/--use; - PHP positional files;
- Rust
Builder::new([..])anduses, replaced byBuilder::set(path), withcargo:rerun-if-changedfor the set file and every listed document.
- Go
- Every internal caller in orm is updated: the Makefile targets and scripts that run orm-gen.
- Each CLI parses arguments with its ecosystem's standard parser. Python's hand-written loop (
bin_orm_gen.py:40-55) becomesargparse. - A breaking-change changelog entry.
docs/spec/schema.md,docs/operations/usage.md,docs/spec/dbspec.md:245anddocs/operations/packaging.md, each with its ko twin.
- flowmark item, in flowmark's checklist, after T128-R (flowmark consumes released orm tags; precedent
T12.12):- The ruling goes in flowmark's spec first: the catalog reads
dbspec/dbspec-set.json, one declaration instead of the directory form. - Nothing is dropped:
- a
.dbsfile underdbspec/that the set file does not list is a catalog error naming it; - a listed path outside
dbspec/is an error; - each rule has accepting and rejecting vectors in flowmark's tests.
- a
- The catalog loader
catalog/load.go. docs/spec/00-overview.mdand30-schema.md, with ko twins.- A set file in every corpus under
testdata/andexamples/. - The board's orm-gen calls.
- The ruling goes in flowmark's spec first: the catalog reads
- T128-3s Reject repeated state-machine lines, in every runtime (a language defect found by the review).
- Found: the parser accepts, silently, a repeated
initial <state>, a repeatedterminal <state>and a repeated<from> -> <to>of one machine, even with differentrequirelists (parse.ts:2249-2284;docs/spec/dbspec.md:164,172). No vector rejects them. - ORES-56 Ruling: such a repeat is a
settingerror at the later line's keyword, and therequirelists play no part. Ground: a repeat adds no meaning, and a repeat with a differentrequirelist is a contradiction the document must not hold. - In
docs/spec/dbspec.md(+ko), and the parser check in each runtime. - Three rejecting vectors in
cases.json, run in every runtime throughdbspec-compare-check. - A changelog entry records the newly rejected input.
- Found: the parser accepts, silently, a repeated
- T128-3p Declaration positions in the parse result, in every runtime.
- ORES-57 Positions sit in the parse result beside the document, never in
DbspecDocument. The reason: a document is also built without source text, by introspection (introspect_catalog.ts:106) and byimportMermaid, and the same type feeds compare, diff, render and emit. Positions in the model would force either invented values or position-sensitive comparisons. - ORES-58 The successful
DbspecParseResultgainspositions: a list, in source order, of{kind, path, line, column}, with line and column in code points, the unit diagnostics use.- ORES-59
kindis one ofdocument,table,column,constraint,setting,use,use_table,diagram,diagram_entry. They cover every declaration (document, table, column, constraint, setting, use, diagram) and the table references ofuselines and diagram entries. A tool that needs the position of any other reference (a foreign key'sreferencestable, a key's column list, a check operand) first adds its kind to this standard, in every runtime with vectors. The no-scanning rule holds without kinds no tool needs today. The header'sdocumenthaspath[<document>], at its name token. - ORES-60
pathis the enclosing names, then the declaration's own name. For example:["users", "email"]for a column;["main", "users"]for a diagram entry. A setting has no name of its own, and its forms differ too much for a "target" to be defined (entity,select explicit,navigation,state_machine,immutable…). So itspathis[<table>, <keyword>, ...identity]. The identity comes from the spec's own repeat rule (dbspec.md:150), never from the order of a runtime's model, because the models differ: Go foldsstate_machinelines into one struct (model.go:167-188).- ORES-61
codec,markdown: the column; - ORES-62
navigation: the foreign key; - ORES-63
blind_index: the AES column; - ORES-64
checkbox: the column and the state; - ORES-65
state_machine: the line's form and operands —["transition", from, to],["terminal", state],["initial", state],["history"],["limit", state]. The column is left out, because a table holds one machine (parse.ts:2257). - ORES-66 a setting that appears at most once: nothing. Each identity is unique: a repeat of it is the
settingerror, which T128-3s makes true for the three state-machine forms that are accepted today. The identity table is stated indocs/spec/dbspec.md(+ko).
- ORES-61
- ORES-67 A setting that repeats (dbspec.md:150) is one entry per line, in source order, each with its own identity. Nothing is merged or dropped.
- ORES-68 The position is the declaration's name token, or the keyword of a setting.
- ORES-69 The unnamed declaration, the primary key (dbspec.md:99, "exactly one per table, unnamed"), has
path[<table>, "primary key"]and the position of itsprimarykeyword. - The shape is stated in
docs/spec/dbspec.md(+ko). The vectors include a repeatedcodec, acheckbox, each of the fivestate_machineforms, ausetable, a diagram entry, the primary key and the document name.
- ORES-59
- ORES-70 Introspection, Mermaid import, emit, compare, diff, render and the hashes neither take nor produce positions.
- ORES-71 Every runtime's parse result has the same shape, with parity and vectors for each kind.
- It needs vectors in cases.json; the interfaces, symbols and parity wiring; and a statement in
docs/spec/dbspec.md(+ko). - ORES-72 Analysis tokens serve highlighting only. Tools locate declarations through these positions, never by scanning tokens or text.
- ORES-83 The
polyspec-ormwheel's Python parse position is an immutable record: itskind, itspath(a tuple of names, not a list) and itslineandcolumnare fixed when it is built, and assigning to one of them raises an error.
- ORES-57 Positions sit in the parse result beside the document, never in
- T128-3
analyzeandformatin every runtime.- TS
parse.ts: classifyTkby context and record comment tokens. - Go
engine/dbspec, PHPDbspec/Parser.php, the PHP extensionparse.c, Pythondbspec/parse.py, Rustorm-schema/src/dbspec/parser.rs. tests/dbspec/syntax.jsonruns in dbspec-go, dbspec-ts, dbspec-php, dbspec-php-extension, dbspec-rust, python-check and dbspec-compare-check.- requirements:
- interfaces rules
Dbspec.analyzeandDbspec.format, with a mapping in every language; - the symbol snapshots and hashes;
- parity API units;
- the
schema_definitiontests; - the php-extension rules list and stubs;
- the Dbspec row of
docs/spec/interfaces-model.md(+ko).
- interfaces rules
- TS
- T128-4 The browser-safe subpath of
@polyspec/orm(ruling 8). It goes into the interfaces/symbols requirements. A test asserts that its module graph holds nonode:module, no driver, and none of the Node globalsBuffer,process,__dirnameandrequire. - T128-5
packages/dbspec-language.openDocument(text, {set}): diagnostics with ranges, tokens, folding (table, settings and diagram blocks),format().- The
dbspec-fmtCLI follows template-fmt's rules (editor-tools.md:105-126):- ORES-73 it parses arguments with
node:utilparseArgsand reads files withreadDbspecFile; - ORES-74 one path without a mode writes to stdout, and
-reads stdin; - ORES-75 several paths require
--writeor--check; - ORES-76 stderr lines are
path:line:col: LABEL: message; - ORES-77 every path is processed, and the highest exit code wins;
- ORES-78
make orm-install-cliinstalls it without a symlink; - ORES-79 it takes the paths it is given. Ruling 4 is the ground for not walking directories, a stated difference from template's walk.
- ORES-73 it parses arguments with
- Exit codes:
- ORES-80 0: formatted, or nothing to change;
- ORES-81 1:
--checkfound an unformatted file; - ORES-82 2: a parse diagnostic, an unreadable file, or bad arguments.
- Tests: cases.json and syntax.json, the invariants, idempotence, and the editor fixtures (
tests/editor/*.dbs+.json, with a--write|--checkscript).
- T128-6
packages/dbspec-lsponvscode-languageserver.- utf-16, full sync, semantic tokens (the legend is the standard classes), folding, formatting.
- Set-file resolution (ruling 4), watched set files and listed files.
- Stdio session tests against the fixtures.
- T128-7 The docs fence, through the fence hook and the language service. Colours for light and dark; no subprocess.
- T128-8
packages/dbspec-codemirrorandpackages/dbspec-vscode.- CodeMirror 6: marks, lint, fold, format command.
- VS Code: language contribution,
semanticTokenScopes, the bundled LSP client, a.vsixbuild. - Tests: CodeMirror fixtures through EditorState; vscode integration asserting
vscode.provideDocumentSemanticTokens. - The orm CI job that runs the integration test gets a VS Code install step, through the kit tool of T128-K.
- T128-K, kit's item K80: the shared editor tools.
- The transitive editor import checker (template's
editor-boundary-check), its rules declared inconfig/*.json. - The VS Code installer.
- Both move from template into kit. orm and template take them with
make kit-sync. Grounding: kit's rule is "a tool that kit lacks is reported to kit, never copied" (orm AGENTS.md:24-30).
- The transitive editor import checker (template's
- T128-9 Wiring.
- The import check through the synced kit tool, with orm's rules in config.
contracts/features.jsonowners for every new file (a feature or a declared scope).contracts/check-inputs.json,CHECK_TARGETS, a CI group.- Workspace build and test scripts with atomic dist publish.
config/release.json:packagesandarchivemanifests for the language, LSP and CodeMirror npm packages, with a consumer smoke each and the consumer lock;- the vscode package as a
versionmanifest, as template does.
- Dependency review records at latest stable:
vscode-languageserver,vscode-languageserver-textdocument,vscode-languageclient,@codemirror/*. - The packages follow the folder standard S5 of
PRINCIPLES.md(kit K70): a folder underpackages/is a runtime or a component, a nested folderpackages/<component>/<runtime>/holds only a runtime port, and no folder carries the repository prefix. The language service and each adapter are components of their own, so the editor stack ispackages/dbspec-language,packages/dbspec-lsp,packages/dbspec-codemirrorandpackages/dbspec-vscode(corrected by T128-2-3:packages/dbspec/<part>/nests a folder that is no runtime, which S5 and kit'scomponent-portsrule refuse). AGENTS.md (+ko) states this layout.
- T128-R Release of the editor stack, its own item, following T126's precedent and orm AGENTS.md:233-240.
- A
chore(release)bump commit after green CI. - The maintainer's tag.
- The release workflow, and
release-verifyof every npm package.
- A
template: rows of its single checklist (table) and entries of docs/plans/execution-plan.md
Each row gets a real ID of the next wave, T<wave>.<n>, assigned in step 1. orm's T128 cites those IDs. Each row also updates its docs/features.md rows and its EDT-n rules in docs/spec/editor.md.
- Row A, template T23.1: the parser is the only grammar.
analyze/analyzePrefixreturn fully classified tokens and the tag body parts.template-languagedeletes its re-stated tables: tokens.ts SIGILS and OPERATOR, tags.ts WRAPPERS, body.ts LOOP_HEAD and ASSIGN_HEAD, expression.ts RESERVED and IDENT, and the duplicated delimiter-directive regex.- The import check gains a rule: the language service declares no lexical table.
- Row B, template T23.2: token analysis and the formatter in every runtime.
- The formatting style of
docs/operations/editor-tools.mdbecomes spec rules. - The conformance cases carry the expected tokens and the formatted text.
- Go, Rust, PHP, the PHP extension and Python implement
analyzeandformat; Python extends its existinganalyze. Each holds the AST invariant and idempotence. - The parity and conformance runners compare tokens and formatted text.
features.md:template-languagepasses in every runtime.
- The formatting style of
- Row C, template T23.3: no grammar in the VS Code extension.
- Its tmLanguage keeps only the HTML host (
text.html.basicwith embedded CSS and JS). - The tag injection rules are deleted; tags are coloured by the semantic tokens it already maps.
configurationDefaultsenables semantic highlighting.- The grammar tests (
vscode-tmgrammar-test, andcaptureSyntaxTokensfor a tag in an attribute value) are rewritten as semantic-token assertions. docs/operations/editor-tools.md(+ko) is updated.
- Its tmLanguage keeps only the HTML host (
- Row D, template T23.4: take the kit editor tools.
- Take the import checker and the VS Code installer from kit (T128-K) with
make kit-sync. - Delete
scripts/check-editor-boundaries.mjsandscripts/install-vscode.mjs. - Declare its rules (
@polyspec/template,src/cli) in config.
- Take the import checker and the VS Code installer from kit (T128-K) with
- Row E, template T23.5: host-less tools resolve include paths by a declared root marker.
- Found: an include or block path resolves relative to the including template inside a loader root (spec/examples.md:178, conformance.md:17). The host supplies the root as a runtime option. The compiler follows static include paths (compiler.md:26). The language service resolves nothing, so a wrong include path is reported only when the template renders.
- Ruling. The root belongs to the host: the engine takes a
loader, and only the filesystem loader has a root (runtime.md:14, RT-10). The map loader (RT-38) and the artifact loader have none. So the root option stays, and the tools that have no host get a declaration:template-root.jsonis a marker file. Its directory is the root, found by nearest ancestor (as in ruling 4). Only the tools with no caller that names a root use it: the language service, the LSP, and the CodeMirror host through its option.- The compiler keeps its
rootargument, because its caller names the root (template-compilerast-artifact.mjs:67,compileAst({ root, … })). An earlier decision that the compiler reads the marker is corrected on that evidence. - The filesystem loader keeps its root argument; the map and artifact loaders are unchanged; the conformance CLI keeps
--root(CNF-2). - The marker's content is
{"schema": 1}. Invalid JSON, another schema value or an unknown key is a diagnostic on the marker's URI, with vectors. - No marker in any ancestor: every include path, and every block path with a path token, gets one diagnostic, "no template root declared (template-root.json)". This is the honest statement, not a skipped check.
- EDT rule: the editor resolves against the marker. A host's loader root is the host's own declaration, and the editor does not check it; a difference between the two is the host's choice.
- The language service resolves paths through an injected host, as
resolveSetdoes, so it stays node-free (EDT-3).
- What it diagnoses: an include tag, and a block tag carrying a path token, whose path does not resolve to a regular file under the root. A block with only an id, and a target that
definesupplies, are runtime (RT-24), and stayE_LOAD_NOT_FOUNDat render. This is stated in the EDT rule. - Paths keep RT-8's normalization: a leading
/resolves against the root, and.and..are normalized. Only two dbspec rules are shared: duplicate detection by file identity, and symlink rejection. Each is written as an amended RT-10 rule, with conformance cases in every runtime.
- Adapters stay TS by constraint. CodeMirror and VS Code hosts are JS. One LSP server serves every editor, on the TS standard implementation.
The working principles, made rules in every repository (item P, first)
- Found by grep of each AGENTS.md:
- no-backward-compatibility, simplest, modular and long-term: in orm, template, kit and flowmark;
- "do not redevelop what the ecosystem provides": only in kit (AGENTS.md:22);
- "no tricks; the orthodox way, or state exactly that only a trick would meet it": only in flowmark (AGENTS.md:55);
- the owner's further rules are written nowhere:
- errors are never hidden, never turned into a plausible input, never silently dropped;
- a standard is never lowered because it is hard to meet; a wrong standard is raised and corrected;
- "it was there before" is no ground;
- a found oddity is reported, and a rule is made or corrected;
- a question is asked only when needed;
- a task includes its documents, AGENTS and the memory.
- Rule: one principles document, vendored byte for byte, the way kit already shares files.
- kit holds
docs/principles.mdand its ko twin, and vendors them throughvendoredFiles.make kit-checkverifies them. No hand-kept copy and no text-compare check. - kit's Purpose (AGENTS.md:6-9) is amended to say it carries this document.
- Each consumer's AGENTS.md links to the document and keeps only its repository's own rules.
- orm AGENTS.md:30 ("the rules of this repository do not read the vendored files") is corrected to name the vendored principles as the one exception.
- kit holds
- Items:
- kit P-kit: the document, the amended Purpose, a kit release.
- One adoption item in each kit consumer's own checklist. Each one is a kit-sync, an AGENTS.md (+ko) rewrite that removes the duplicated text, and its memory pointer.
- consumer has an AGENTS.md but no kit, and carries its own copies of shared tooling:
.githooks/pre-push,.github/workflows/push-gate.yml, and the release-notes rule of commit91ffd55, which duplicates kit's. On kit's own ground, "the tools that every polyspec repository shares" (kit AGENTS.md:7), its adoption item makes it a kit consumer and replaces those copies with the vendored tools.
flowmark: found oddities, items in flowmark's checklist
- F-W, the abandoned agent worktrees. Two worktrees in
.claude/worktrees/sit on branches atb82c464, an ancestor of main, and hold uncommitted work.agent-a17bb512…: another attempt atT12.1-38, the GFM tables. Main settled it differently (34f76ed), but this worktree'sscripts/record-slugs.mjsholds 53 table vectors that main lacks: lists, block quotes, HTML blocks, definitions, footnotes.- They are worth keeping, so a suffixed item
T12.1-38-1adopts them, red first. Their expected slugs come from the viewers throughrecord-slugs, never from that worktree'sstore/markdown.go. - The worktree's second table reading (
store/markdown.go) and its test edits are discarded; the item says so. - The worktree is kept until
T12.1-38-1adopts the vectors, then removed. That removal condition is stated on the item.
- They are worth keeping, so a suffixed item
agent-ab7cc19b…: another attempt atT12.5-3, commit-timeenvelope.*rules and an in-process fd-limit raise (store/journal/limit_*.go). Main's spec settled the scale limits as validate-time reporting (40-ops, the section on scale limits). The fd-limit raise is the stopgap the owner rejected. Nothing in it is kept.agent-ab7cc19b…and its branch are removed at once, after review.- flowmark AGENTS.md gains the missing rule: an abandoned worktree is reviewed; what is worth keeping becomes an item with its removal condition; the rest is discarded and the worktree deleted with its branch.
- F-G, one grammar in flowmark.
- Criterion: a pattern is legitimate only as the one reading of its grammar, or as a cited copy of an external tool's behaviour.
store/inline.go:148vsGuardis the second kind: it cites@vscode/markdown-it-katex 1.1.2. - The audit covers string matching as well as regexes. Starting points:
catalog/rules.go,server/token.go,store/document.go,store/inline.go,store/journal/lock.go. - Found already:
catalog/rules.go:149locateDeclre-states dbspec declaration syntax to find a name, and returns line 1, column 1 when it finds none. That is a fallback that turns a missing position into a plausible one. Its cause is that orm's Go dbspec model has no declaration positions. F-G uses the declaration positions of T128-3p and removeslocateDeclwith its fallback; scanning tokens would restate the syntax again. So F-G comes after T128-R. - The audit and its fixes are one item, red first where a rule is restated.
- Criterion: a pattern is legitimate only as the one reading of its grammar, or as a cited copy of an external tool's behaviour.
- Oddities outside these repositories, reported to the owner and never touched:
consumer-agents-S-49(looks like a worktree left behind),split-pane-backup-…andtemplate_2026.
Order
- Register T128-1 as a sub-item of T128 in orm's checklist (EN and KO), then run it, as decided: it removes defective code and depends on no ruling. When
cianddocs-pagesare green, notify the request-hook session. - Register P-kit and the eight adoption items in their checklists, then run them: kit first, then every adoption. The rules govern everything after.
- Register every remaining item in the checklists:
- orm: T128 rewritten, with T128-2 to T128-9, T128-2a, T128-3s, T128-3p and T128-R;
- kit: T128-K (K80, registered 34766f3);
- template: rows A–E as Wave 23, T23.1–T23.5 (registered 1349fa4);
- flowmark: the set-file item, F-W, F-G and
T12.1-38-1. Then T128-2, the rulings in the specs.
- T128-K, T128-2a, T128-3s, T128-3p, T128-3 through T128-9, then T128-R.
- template rows A, B, C, D, E.
- flowmark: F-W and
T12.1-38-1(any time after step 2); the set-file item and F-G after T128-R.
One item at a time.
Verification
- orm:
make dbspec-go-check dbspec-ts-check dbspec-php-check dbspec-php-extension-check dbspec-rust-check python-check dbspec-compare-check;node tests/interfaces/check/main.mts --self-test;make parity-check;make feature-unit-check;- the language, LSP, CodeMirror and vscode tests; the import check;
npm run docs:check(the builtdbspec.htmlholds the class spans in both themes);make release-coverage docs-check kit-check;- the
cianddocs-pagesworkflows green; the release verified.
- template:
make orm-check: conformance and parity with tokens and format, the invariant test, the editor import check, the vscode integration;- template CI green.