From e1cddb85991eea075aa8fc39986c6db01cacd718 Mon Sep 17 00:00:00 2001 From: "Claude Fable 5.1 (claude-fable-5-1)" Date: Mon, 19 Oct 2026 00:39:14 +0200 Subject: [PATCH 1/8] docs: every occurrence of a term is a link A defined term is a link to its glossary page at every occurrence in a file: the first emphasized, every later one plain, so a copied paragraph keeps its links. The foundations spec, the ConsultTheOntology rule, the fallback check-terms and fix-terms, and check-coverage all say or do so. Six foundation requirements that crossed the 500-character cap with their links split into pairs, and the physics framing of projections moved to the glossary page. Nine review-ceremony terms join the ontology. Co-Authored-By: Martin Zeman --- .github/workflows/quality.yaml | 2 +- .vale/styles/Core/UndefinedTerm.yml | 9 +- .vale/styles/Core/UnexplainedAbbreviation.yml | 3 +- .../design.md | 2 +- docs/changes/one-domain-one-plugin/adr.md | 28 +-- .../changes/one-domain-one-plugin/proposal.md | 12 +- docs/changes/one-instance-per-model/design.md | 20 +- .../one-instance-per-model/proposal.md | 8 +- .../plain-text-first-principles/design.md | 79 +++---- .../plain-text-first-principles/proposal.md | 42 ++-- .../specs/ceremony-matches-the-task/spec.md | 19 +- .../directories-direct-the-tools/spec.md | 4 +- .../instructions-stay-vendor-neutral/spec.md | 37 ++-- .../spec.md | 14 +- .../markdown-relative-links-matching/spec.md | 2 +- .../markdown-with-yaml-frontmatter/spec.md | 27 +-- .../open-knowledge-format-bundle/spec.md | 8 +- .../specs/plain-text-is-futureproof/spec.md | 10 +- .../specs/single-source-of-truth/spec.md | 2 +- .../specs/user-values-and-variables/spec.md | 26 ++- .../plain-text-first-principles/README.md | 6 +- docs/specs/glossary/agentic-review-lane.md | 12 ++ docs/specs/glossary/commit-trailer.md | 12 ++ docs/specs/glossary/index.md | 13 +- docs/specs/glossary/lane-table.md | 12 ++ docs/specs/glossary/merge-base.md | 12 ++ docs/specs/glossary/merge-seal.md | 12 ++ docs/specs/glossary/open-seal.md | 12 ++ docs/specs/glossary/projection.md | 2 +- docs/specs/glossary/review-controller.md | 12 ++ docs/specs/glossary/review-funnel.md | 12 ++ docs/specs/glossary/review-scorecard.md | 2 +- docs/specs/glossary/work-item.md | 12 ++ ontology/rune.ttl | 51 ++++- runes/core/rules/ConsultTheOntology.md | 5 +- runes/core/skills/ReviewChange/SKILL.md | 34 +-- scripts/check-coverage | 21 +- scripts/fallback/check-terms | 11 +- scripts/fallback/fix-terms | 62 +++++- scripts/fallback/termlib.py | 193 +++++++++++++++++- scripts/generate-vale-style.py | 9 +- tests/fixtures/vale/valid/undefined-term.md | 6 + .../vale/valid/unexplained-abbreviation.md | 2 + tests/test_check_scripts.py | 31 +++ tests/test_fallback_terms.py | 116 +++++++++++ 45 files changed, 812 insertions(+), 214 deletions(-) create mode 100644 docs/specs/glossary/agentic-review-lane.md create mode 100644 docs/specs/glossary/commit-trailer.md create mode 100644 docs/specs/glossary/lane-table.md create mode 100644 docs/specs/glossary/merge-base.md create mode 100644 docs/specs/glossary/merge-seal.md create mode 100644 docs/specs/glossary/open-seal.md create mode 100644 docs/specs/glossary/review-controller.md create mode 100644 docs/specs/glossary/review-funnel.md create mode 100644 docs/specs/glossary/work-item.md diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index 9782260d..a7803dd3 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -108,7 +108,7 @@ jobs: # An in-tree checkout would land under the all-files hooks. - name: Install the pinned rune env: - RUNE_CLI_REV: eb6cec352ea6f0f60893d9ec6555348190853001 + RUNE_CLI_REV: 1cea8c7995591518df25687bc1203e45524ce624 run: | cargo install --quiet --git https://github.com/runedeck/cli --rev "$RUNE_CLI_REV" rune-cli rune --version diff --git a/.vale/styles/Core/UndefinedTerm.yml b/.vale/styles/Core/UndefinedTerm.yml index afc83075..e20803af 100644 --- a/.vale/styles/Core/UndefinedTerm.yml +++ b/.vale/styles/Core/UndefinedTerm.yml @@ -5,11 +5,14 @@ # the text counts as the definition, and italics alone are emphasis. The # nouns are the TERMS subset of OVERUSE, so one list serves this rule and # STE.Overused*. Raw scope, because Vale strips the markers before any -# other scope. +# other scope. A hyphenated name such as review-lanes is a name, not +# the noun, in prose and in link text, and a link whose text ends in +# the noun (agentic review lane) defines it. The first pattern matches +# the stem alone, so a plural use counts as defined by its singular link. extends: conditional message: "'%s' is never defined here. Link it to its glossary page at first use, or say which check, file, or flag it is (rule Deslop)." level: warning ignorecase: true scope: raw -first: '\b(arm|gate|knob|landscape|lane|surface)s?\b' -second: '(?:\[(?arm|gate|knob|landscape|lane|surface)s?\]\[|[*_`](?arm|gate|knob|landscape|lane|surface)s?[*_`])' +first: '(?arm|gate|knob|landscape|lane|surface)s?\]\[|\*\*(?arm|gate|knob|landscape|lane|surface)s?\*\*|__(?arm|gate|knob|landscape|lane|surface)s?__|`(?arm|gate|knob|landscape|lane|surface)s?`)' diff --git a/.vale/styles/Core/UnexplainedAbbreviation.yml b/.vale/styles/Core/UnexplainedAbbreviation.yml index 6e1896b3..bc699089 100644 --- a/.vale/styles/Core/UnexplainedAbbreviation.yml +++ b/.vale/styles/Core/UnexplainedAbbreviation.yml @@ -13,7 +13,8 @@ message: "'%s' is an abbreviation with no explanation here. Spell it out at firs level: suggestion scope: text ignorecase: false -first: '\b([A-Z]{2,})\b' +# A roman numeral (Part II, chapter IV) is a number, not an abbreviation. +first: '\b(?![IVXLC]+\b)([A-Z]{2,})\b' second: '(?:\((?[A-Z]{2,})\)|\b(?[A-Z]{2,})(?=\s*\([A-Za-z]))' exceptions: - MUST diff --git a/docs/changes/environment-variables-supported-syntax/design.md b/docs/changes/environment-variables-supported-syntax/design.md index c873c173..f14d2481 100644 --- a/docs/changes/environment-variables-supported-syntax/design.md +++ b/docs/changes/environment-variables-supported-syntax/design.md @@ -17,7 +17,7 @@ For Claude Code, the *[basis parameters][BASISPARAMETERS]* add an argument hint | environment-variables-supported-syntax | Value Sources Have One Order | missed requirement: no check runs the loader against a fixture that sets all four sources | | environment-variables-supported-syntax | The Loader Exports Environment Variables | missed requirement: no loader exists yet | | environment-variables-supported-syntax | The Shell Resolves the Variable | missed requirement: no check finds a named variable that no step reads, carried over from the foundations' coverage table | -| environment-variables-supported-syntax | Each Harness Runs the Loader | missed requirement: the assembler writes no loader step and no context line yet | +| environment-variables-supported-syntax | Each [Harness][HARNESS] Runs the Loader | missed requirement: the [assembler][ASSEMBLER] writes no loader step and no context line yet | | environment-variables-supported-syntax | Provenance Shows the Parameters | missed requirement: `scripts/check-provenance` checks that the sidecar exists, and no check reads a parameters field yet | ## Commands diff --git a/docs/changes/one-domain-one-plugin/adr.md b/docs/changes/one-domain-one-plugin/adr.md index a5b5a72a..20b0bd80 100644 --- a/docs/changes/one-domain-one-plugin/adr.md +++ b/docs/changes/one-domain-one-plugin/adr.md @@ -31,33 +31,33 @@ change: one-domain-one-plugin ## Context and Problem Statement A *[rune][RUNE]* is the unit the deck is made of, and this record says what one is. -The foundations define the *[instruction][INSTRUCTION]*, the file a *[harness][HARNESS]* loads to steer a *[model][MODEL]*, and require that one source serves every harness and model. -A governed instruction has three kinds of part: what is written once, what varies by harness and model, and what records where an *[instance][INSTANCE]* came from. -Anthropic's glossary calls rules instruction files, and says that Claude Code is the harness and Claude the model inside it ([Claude Code glossary][CC-GLOSSARY]). -Its hooks are settings entries, and a hook rune is the Markdown source that *[assembly][ASSEMBLY]* renders into such an entry. +The foundations define the *[instruction][INSTRUCTION]*, the file a *[harness][HARNESS]* loads to steer a *[model][MODEL]*, and require that one source serves every [harness][HARNESS] and [model][MODEL]. +A governed [instruction][INSTRUCTION] has three kinds of part: what is written once, what varies by [harness][HARNESS] and [model][MODEL], and what records where an *[instance][INSTANCE]* came from. +Anthropic's glossary calls rules [instruction][INSTRUCTION] files, and says that Claude Code is the [harness][HARNESS] and Claude the [model][MODEL] inside it ([Claude Code glossary][CC-GLOSSARY]). +Its hooks are settings entries, and a hook [rune][RUNE] is the Markdown source that *[assembly][ASSEMBLY]* renders into such an entry. ## Considered Options -1. Leave the term to the ontology comment. Then nothing records why a rune has these parts and not others. -2. Write one record that defines the parts and the rule for each: one *[canon][CANON]*, *[companions][COMPANION]* for what one harness needs, *[variants][VARIANT]* for what one harness or model needs, a *[sidecar][SIDECAR]* for provenance, and assembly as the only way to make an instance. +1. Leave the term to the ontology comment. Then nothing records why a [rune][RUNE] has these parts and not others. +2. Write one record that defines the parts and the rule for each: one *[canon][CANON]*, *[companions][COMPANION]* for what one [harness][HARNESS] needs, *[variants][VARIANT]* for what one [harness][HARNESS] or [model][MODEL] needs, a *[sidecar][SIDECAR]* for provenance, and [assembly][ASSEMBLY] as the only way to make an [instance][INSTANCE]. ## Decision Outcome Option 2. -- A rune MUST be one instruction: a Markdown file with YAML frontmatter, of kind skill, agent, rule, or hook. A hook rune is the source of a harness's hook configuration. The rendered configuration is not a rune. -- A rune MUST have exactly one [canon][CANON], the vendor-neutral entrypoint, and the canon MUST NOT contain a harness-specific instruction. -- A harness-specific instruction MUST live in a [companion][COMPANION]. +- A [rune][RUNE] MUST be one [instruction][INSTRUCTION]: a Markdown file with YAML frontmatter, of kind skill, agent, rule, or hook. A hook [rune][RUNE] is the source of a [harness][HARNESS]'s hook configuration. The rendered configuration is not a [rune][RUNE]. +- A [rune][RUNE] MUST have exactly one [canon][CANON], the vendor-neutral entrypoint, and the [canon][CANON] MUST NOT contain a harness-specific [instruction][INSTRUCTION]. +- A harness-specific [instruction][INSTRUCTION] MUST live in a [companion][COMPANION]. - A harness-, model-, or user-specific alternative MUST live in a [variant][VARIANT]. -- An instance MUST come from [assembly][ASSEMBLY], one for each harness and model, and MUST NOT be edited in its *[target][TARGET]*. +- An [instance][INSTANCE] MUST come from [assembly][ASSEMBLY], one for each [harness][HARNESS] and [model][MODEL], and MUST NOT be edited in its *[target][TARGET]*. - Provenance MUST live in the [sidecar][SIDECAR] under `.provenance/`, the one exception to metadata inside the file (CORE-0002). ### Consequences -- [+] Every requirement that says rune now points at one definition, and the term check refuses a prose use with no ontology entry. -- [+] A new harness costs companions and nothing in a canon. -- [-] A canon that contains harness wording fails the rule, and that wording belongs in a companion. -- [-] Model is a second assembly dimension, and resolving it needs the harness to say which model runs. +- [+] Every requirement that says [rune][RUNE] now points at one definition, and the term check refuses a prose use with no ontology entry. +- [+] A new [harness][HARNESS] costs [companions][COMPANION] and nothing in a [canon][CANON]. +- [-] A [canon][CANON] that contains [harness][HARNESS] wording fails the rule, and that wording belongs in a [companion][COMPANION]. +- [-] [Model][MODEL] is a second [assembly][ASSEMBLY] dimension, and resolving it needs the [harness][HARNESS] to say which [model][MODEL] runs. [INSTRUCTION]: ../../specs/glossary/instruction.md [CC-GLOSSARY]: https://code.claude.com/docs/en/glossary diff --git a/docs/changes/one-domain-one-plugin/proposal.md b/docs/changes/one-domain-one-plugin/proposal.md index 5838c825..ce16ca56 100644 --- a/docs/changes/one-domain-one-plugin/proposal.md +++ b/docs/changes/one-domain-one-plugin/proposal.md @@ -13,23 +13,23 @@ decisions: A deck grows by domains, and each domain brings its own *[instructions][INSTRUCTION]*, defaults, and checks. Without a unit that keeps those together, every addition touches shared configuration, and every tool reads every file to find what it needs. -That unit is the *[module][MODULE]*. Parnas gave the criterion for splitting a system in 1972: each module hides one design decision that is likely to change, and no other module depends on it ([Parnas][PARNAS]). Ousterhout adds the measure: a good module is deep, a small interface over a large implementation, so the cost of a caller does not grow with the module ([Ousterhout][OUSTERHOUT]). Both arguments hold for instructions as they hold for code. A domain that owns its defaults, its checks, and its instructions can be added, replaced, or removed without a change anywhere else. +That unit is the *[module][MODULE]*. Parnas gave the criterion for splitting a system in 1972: each [module][MODULE] hides one design decision that is likely to change, and no other [module][MODULE] depends on it ([Parnas][PARNAS]). Ousterhout adds the measure: a good [module][MODULE] is deep, a small interface over a large implementation, so the cost of a caller does not grow with the [module][MODULE] ([Ousterhout][OUSTERHOUT]). Both arguments hold for [instructions][INSTRUCTION] as they hold for code. A domain that owns its defaults, its checks, and its [instructions][INSTRUCTION] can be added, replaced, or removed without a change anywhere else. -Three properties make a domain a module here: +Three properties make a domain a [module][MODULE] here: - It carries its own defaults, and a user overrides them without editing the committed file. -- Every check it needs runs through one shared hook configuration, locally and in CI, so a module cannot pass on one machine and fail on another. +- Every check it needs runs through one shared hook configuration, locally and in CI, so a [module][MODULE] cannot pass on one machine and fail on another. - Anything it fetches from the network runs only after its digest matches one committed in the repository. ## What Changes -Define module and *[rune][RUNE]* in the ontology. State the configuration overlay, one checker per concern, one validation path, and verified remote execution as the requirements of one capability. The record `adr.md` defines the parts of a rune: one *[canon][CANON]*, *[companions][COMPANION]* for a *[harness][HARNESS]*, *[variants][VARIANT]*, a provenance *[sidecar][SIDECAR]*, and *[assembly][ASSEMBLY]*. +Define [module][MODULE] and *[rune][RUNE]* in the ontology. State the configuration overlay, one checker per concern, one validation path, and verified remote execution as the requirements of one capability. The record `adr.md` defines the parts of a [rune][RUNE]: one *[canon][CANON]*, *[companions][COMPANION]* for a *[harness][HARNESS]*, *[variants][VARIANT]*, a provenance *[sidecar][SIDECAR]*, and *[assembly][ASSEMBLY]*. ## Capabilities ### New Capabilities -- `one-domain-one-plugin`: a module has defaults a user overlays, runs every check through one shared hook configuration, and verifies anything it fetches before it runs. +- `one-domain-one-plugin`: a [module][MODULE] has defaults a user overlays, runs every check through one shared hook configuration, and verifies anything it fetches before it runs. ## Impact @@ -39,7 +39,7 @@ Define module and *[rune][RUNE]* in the ontology. State the configuration overla ## Sources - [On the Criteria To Be Used in Decomposing Systems into Modules][PARNAS], D. L. Parnas, Communications of the ACM 15(12), 1972 -- [A Philosophy of Software Design][OUSTERHOUT], John Ousterhout, 2018, on deep modules +- [A Philosophy of Software Design][OUSTERHOUT], John Ousterhout, 2018, on deep [modules][MODULE] [MODULE]: ../../specs/glossary/module.md [INSTRUCTION]: ../../specs/glossary/instruction.md diff --git a/docs/changes/one-instance-per-model/design.md b/docs/changes/one-instance-per-model/design.md index 2e5d30e5..b40e0574 100644 --- a/docs/changes/one-instance-per-model/design.md +++ b/docs/changes/one-instance-per-model/design.md @@ -2,34 +2,34 @@ ## Approach -Two halves: variants at build time, activation at runtime. The *[assembler][ASSEMBLER]* builds a *[variant][VARIANT]* for each *[model][MODEL]* it knows, because the differences are known before a session. A *[hook][HOOK]* reads the active [model][MODEL] at session start and on a [model][MODEL] change. The [model][MODEL] is chosen at runtime and can change in a session, so build time cannot know it. +Two halves: *[variants][VARIANT]* at build time, activation at runtime. The *[assembler][ASSEMBLER]* builds a [variant][VARIANT] for each *[model][MODEL]* it knows, because the differences are known before a session. A *[hook][HOOK]* reads the active [model][MODEL] at session start and on a [model][MODEL] change. The [model][MODEL] is chosen at runtime and can change in a session, so build time cannot know it. -The alternative was one *[instance][INSTANCE]* for each *[harness][HARNESS]* and [model][MODEL], deployed side by side. It lost twice: it breaks One Instance per Harness, and it still cannot change with the [model][MODEL] in a session. The owner chose the swap: the [hook][HOOK] puts the active [model][MODEL]'s [variant][VARIANT] in place of the one [instance][INSTANCE]. +The alternative was one *[instance][INSTANCE]* for each *[harness][HARNESS]* and [model][MODEL], deployed side by side. It lost twice: it breaks One [Instance][INSTANCE] per [Harness][HARNESS], and it still cannot change with the [model][MODEL] in a session. The owner chose the swap: the [hook][HOOK] puts the active [model][MODEL]'s [variant][VARIANT] in place of the one [instance][INSTANCE]. ## Coverage table | Capability | Requirement | Check or missed requirement | |---|---|---| -| one-instance-per-model | A Variant per Known Model | missed requirement: the assembler builds no model variant yet | -| one-instance-per-model | Variant Resolution Has One Order | missed requirement: no fixture holds a user variant, a harness and model variant, and a harness variant of one instruction | -| one-instance-per-model | A Runtime Hook Activates the Variant | missed requirement: no hook reads the active model yet | +| one-instance-per-model | A [Variant][VARIANT] per Known [Model][MODEL] | missed requirement: the [assembler][ASSEMBLER] builds no [model][MODEL] [variant][VARIANT] yet | +| one-instance-per-model | [Variant][VARIANT] Resolution Has One Order | missed requirement: no fixture holds a user [variant][VARIANT], a [harness][HARNESS] and [model][MODEL] [variant][VARIANT], and a [harness][HARNESS] [variant][VARIANT] of one instruction | +| one-instance-per-model | A Runtime [Hook][HOOK] Activates the [Variant][VARIANT] | missed requirement: no [hook][HOOK] reads the active [model][MODEL] yet | ## Commands -No command implements this change yet. When the cli change that builds it exists, this table names the command, flags, hook, and diagnostic ids for each requirement. +No command implements this change yet. When the cli change that builds it exists, this table names the command, flags, [hook][HOOK], and diagnostic ids for each requirement. ## Decisions -- Reusable Across Harnesses and Models stays in the foundations, with "and models" backed by this change (voice review, 2026-10-18). -- One Instance per Harness stays as written. The [hook][HOOK] swaps in the [model][MODEL]'s [variant][VARIANT] instead of a second [instance][INSTANCE] for each [model][MODEL]. +- Reusable Across [Harnesses][HARNESS] and [Models][MODEL] stays in the foundations, with "and [models][MODEL]" backed by this change (voice review, 2026-10-18). +- One [Instance][INSTANCE] per [Harness][HARNESS] stays as written. The [hook][HOOK] swaps in the [model][MODEL]'s [variant][VARIANT] instead of a second [instance][INSTANCE] for each [model][MODEL]. - The ontology's [variant][VARIANT] term already states the resolution order: user, then [harness][HARNESS] and [model][MODEL], then [harness][HARNESS], then the base *[rune][RUNE]*. This change makes the [assembler][ASSEMBLER] and the [hook][HOOK] apply it. - Companions Earn Their Place decides whether a [model][MODEL] [variant][VARIANT] stays. It beats the *[canon][CANON]* alone on an evaluation for its [harness][HARNESS] and [model][MODEL], or it goes. ## Risks -- A [harness][HARNESS] may not tell a [hook][HOOK] which [model][MODEL] is active, or may not fire a hook on a model change. Guard: where a swap is impossible, the [hook][HOOK] injects the [variant][VARIANT]'s difference as context. Where neither works, the [harness][HARNESS] gets its [harness][HARNESS] [variant][VARIANT] only, and the coverage table says so. +- A [harness][HARNESS] may not tell a [hook][HOOK] which [model][MODEL] is active, or may not fire a [hook][HOOK] on a [model][MODEL] change. Guard: where a swap is impossible, the [hook][HOOK] injects the [variant][VARIANT]'s difference as context. Where neither works, the [harness][HARNESS] gets its [harness][HARNESS] [variant][VARIANT] only, and the coverage table says so. - A swap in a session leaves the deployment manifest naming the base [instance][INSTANCE]. Guard: the swap is a runtime overlay that the next deployment resets, and the deployment check reads the manifest and ignores the overlay. -- Variants multiply files. Guard: Companions Earn Their Place removes a [variant][VARIANT] that does not beat the canon on an evaluation. +- [Variants][VARIANT] multiply files. Guard: Companions Earn Their Place removes a [variant][VARIANT] that does not beat the [canon][CANON] on an evaluation. [ASSEMBLER]: ../../specs/glossary/assembler.md [VARIANT]: ../../specs/glossary/variant.md diff --git a/docs/changes/one-instance-per-model/proposal.md b/docs/changes/one-instance-per-model/proposal.md index 0c2aea26..bfbba0ad 100644 --- a/docs/changes/one-instance-per-model/proposal.md +++ b/docs/changes/one-instance-per-model/proposal.md @@ -6,14 +6,14 @@ status: proposed ## Why -No *[harness][HARNESS]* solves model-specific *[instructions][INSTRUCTION]*. The *[model][MODEL]* can change in the middle of a session. Build time alone cannot know which [model][MODEL] reads the [instruction][INSTRUCTION]. The foundations promise an [instruction][INSTRUCTION] reusable across harnesses and models. One Instance per Harness keeps one *[instance][INSTANCE]* for each [harness][HARNESS]. This *[change][CHANGE]* backs both. The *[assembler][ASSEMBLER]* builds a *[variant][VARIANT]* for each known [model][MODEL]. A *[hook][HOOK]* puts the active [model][MODEL]'s [variant][VARIANT] in place at runtime. +No *[harness][HARNESS]* solves model-specific *[instructions][INSTRUCTION]*. The *[model][MODEL]* can *[change][CHANGE]* in the middle of a session. Build time alone cannot know which [model][MODEL] reads the [instruction][INSTRUCTION]. The foundations promise an [instruction][INSTRUCTION] reusable across [harnesses][HARNESS] and [models][MODEL]. One *[Instance][INSTANCE]* per [Harness][HARNESS] keeps one [instance][INSTANCE] for each [harness][HARNESS]. This [change][CHANGE] backs both. The *[assembler][ASSEMBLER]* builds a *[variant][VARIANT]* for each known [model][MODEL]. A *[hook][HOOK]* puts the active [model][MODEL]'s [variant][VARIANT] in place at runtime. ## What Changes - The [assembler][ASSEMBLER] builds a [variant][VARIANT] of each [instruction][INSTRUCTION] for every [model][MODEL] it knows. -- A [hook][HOOK] runs at session start and when the [model][MODEL] changes. It reads the active [model][MODEL] and swaps in the matching [variant][VARIANT], or injects that [variant][VARIANT]'s difference as context. +- A [hook][HOOK] runs at session start and when the [model][MODEL] [changes][CHANGE]. It reads the active [model][MODEL] and swaps in the matching [variant][VARIANT], or injects that [variant][VARIANT]'s difference as context. - The [variant][VARIANT] resolution order is the user's own [variant][VARIANT], then the [harness][HARNESS] and [model][MODEL] [variant][VARIANT], then the [harness][HARNESS] [variant][VARIANT], then the base *[rune][RUNE]*. -- One Instance per Harness stays as written. The [harness][HARNESS] tree holds one [instance][INSTANCE] for each [instruction][INSTRUCTION], and the [hook][HOOK] swaps one [variant][VARIANT] for another and deploys no second [instance][INSTANCE]. +- One [Instance][INSTANCE] per [Harness][HARNESS] stays as written. The [harness][HARNESS] tree holds one [instance][INSTANCE] for each [instruction][INSTRUCTION], and the [hook][HOOK] swaps one [variant][VARIANT] for another and deploys no second [instance][INSTANCE]. ## Capabilities @@ -24,7 +24,7 @@ No *[harness][HARNESS]* solves model-specific *[instructions][INSTRUCTION]*. The ## Impact - The [assembler][ASSEMBLER], the *[deployer][DEPLOYER]*, the [hook][HOOK] of each [harness][HARNESS], and the *[provenance record][PROVENANCERECORD]* of a [variant][VARIANT]. -- The foundations requirements Reusable Across Harnesses and Models and One Instance per Harness, which rest on this [change][CHANGE]. +- The foundations requirements Reusable Across [Harnesses][HARNESS] and [Models][MODEL] and One [Instance][INSTANCE] per [Harness][HARNESS], which rest on this [change][CHANGE]. - Companions Earn Their Place in the foundations. A [model][MODEL] [variant][VARIANT] stays when it beats the *[canon][CANON]* alone on an evaluation for its [harness][HARNESS] and [model][MODEL]. [INSTRUCTION]: ../../specs/glossary/instruction.md diff --git a/docs/changes/plain-text-first-principles/design.md b/docs/changes/plain-text-first-principles/design.md index 3d7f3d3d..119372f8 100644 --- a/docs/changes/plain-text-first-principles/design.md +++ b/docs/changes/plain-text-first-principles/design.md @@ -2,50 +2,55 @@ ## Coverage table -The table has one row for each foundation *[requirement][REQUIREMENT]*. Each row states the check that enforces the requirement, or the *[missed requirement][MISSEDREQUIREMENT]* a later *[analysis tool][ANALYSISTOOL]* closes. A requirement names a role. The Commands table below names the program behind each row. +The table has one row for each foundation *[requirement][REQUIREMENT]*. Each row states the check that enforces the [requirement][REQUIREMENT], or the *[missed requirement][MISSEDREQUIREMENT]* a later *[analysis tool][ANALYSISTOOL]* closes. A [requirement][REQUIREMENT] names a role. The Commands table below names the program behind each row. -| Capability | Requirement | Check or missed requirement | +| Capability | [Requirement][REQUIREMENT] | Check or [missed requirement][MISSEDREQUIREMENT] | |---|---|---| -| plain-text-is-futureproof | Plain Text Representation | missed requirement: because representability stays a review judgment | -| plain-text-is-futureproof | UTF-8 Encoding | missed requirement: no check decodes every text file as UTF-8 or rejects a byte order mark | +| plain-text-is-futureproof | Plain Text Representation | [missed requirement][MISSEDREQUIREMENT]: because representability stays a review judgment | +| plain-text-is-futureproof | UTF-8 Encoding | [missed requirement][MISSEDREQUIREMENT]: no check decodes every text file as UTF-8 or rejects a byte order mark | | plain-text-is-futureproof | Binary Assets Carry a Description | `scripts/check-binary-assets` | -| plain-text-is-futureproof | Lossless Renders | the `glossary-current` hook for the glossary pages, and a missed requirement for every other render, because no check regenerates a proof GIF or a Vale style from its state file | -| plain-text-is-futureproof | Projections Are Never Authoritative | the `sync-idea` hook for an idea snapshot against its vault source state, and a missed requirement for a snapshot committed without a provenance record and for a committed file generated from a projection | -| markdown-with-yaml-frontmatter | Markdown System Language | the `rune validate` artifact checks, rumdl MD033 for raw HTML, the Core.DeckLinks Vale rule for wikilinks, and a missed requirement for callouts | -| markdown-with-yaml-frontmatter | Metadata Inside Files | `scripts/check-provenance` for the sealed sidecar, and a missed requirement for an external metadata index, because no mechanical check detects one | +| plain-text-is-futureproof | Lossless Renders | the `glossary-current` hook for the glossary pages, and a [missed requirement][MISSEDREQUIREMENT] for every other render, because no check regenerates a proof GIF or a Vale style from its state file | +| plain-text-is-futureproof | Projections Are Never Authoritative | the `sync-idea` hook for an idea snapshot against its vault source state, and a [missed requirement][MISSEDREQUIREMENT] for a snapshot committed without a provenance record and for a committed file generated from a projection | +| markdown-with-yaml-frontmatter | Markdown System Language | the `rune validate` artifact checks, rumdl MD033 for raw HTML, the Core.DeckLinks Vale rule for wikilinks, and a [missed requirement][MISSEDREQUIREMENT] for callouts | +| markdown-with-yaml-frontmatter | Metadata Inside Files | `scripts/check-provenance` for the sealed sidecar, and a [missed requirement][MISSEDREQUIREMENT] for an external metadata index, because no mechanical check detects one | | markdown-with-yaml-frontmatter | Reference Citations | the Core.Citations Vale rule | -| markdown-with-yaml-frontmatter | Defined Terms Cite the Ontology | `rune spec validate` for a first use that is not emphasized or a later use that is, the `check-terms` hook without rune, rumdl MD052 and MD053 for the reference tags, and a missed requirement for the assembler stripping links from an instance | -| markdown-with-yaml-frontmatter | The Ontology Defines Every Term | `rune spec validate` for a term link with no ontology entry and a warning on a draft term, the `check-terms` hook without rune, and a missed requirement for the archive refusal until the archiver refuses a draft term | +| markdown-with-yaml-frontmatter | Defined Terms Cite the Ontology | `rune spec validate` for a first use that is not emphasized or a later use that is, the `check-terms` hook without rune, rumdl MD052 and MD053 for the reference tags, and a [missed requirement][MISSEDREQUIREMENT] for the assembler stripping links from an instance | +| markdown-with-yaml-frontmatter | The Ontology Defines Every Term | `rune spec validate` for a term link with no ontology entry and a warning on a draft term, the `check-terms` hook without rune, and a [missed requirement][MISSEDREQUIREMENT] for the archive refusal until the archiver refuses a draft term | +| markdown-with-yaml-frontmatter | Draft Terms Warn And Archive Refuses Them | `rune spec validate` warning on a draft term and `rune spec archive` refusing one, the `fix-terms` hook before `check-terms` | | markdown-with-yaml-frontmatter | Glossary Renders From The Ontology | the `glossary-current` hook: `scripts/fallback/generate-glossary --check` over every page, which hands over to `rune spec glossary --check` when the installed rune writes a directory | -| markdown-relative-links-matching | Markdown Links Between Files | the Core.DeckLinks Vale rule for a wikilink or an at-sign reference in a record, a specification, or a proposal, and a missed requirement for the include translation until the assembler does it | +| markdown-relative-links-matching | Markdown Links Between Files | the Core.DeckLinks Vale rule for a wikilink or an at-sign reference in a record, a specification, or a proposal, and a [missed requirement][MISSEDREQUIREMENT] for the include translation until the assembler does it | | markdown-relative-links-matching | Link Targets Resolve | the lychee offline link check with fragments and no exclusions on the commit path, `rune spec validate` and the `check-terms` hook for term links, the pre-push lychee hook that warns on external links in changed files, and the weekly `links.yaml` workflow that reports dead links | -| open-knowledge-format-bundle | Type On Every File | `scripts/check-okf`, which derives the type from the nearest structure schema and accepts an untyped file, and a missed requirement for the graph export filling the type in | -| open-knowledge-format-bundle | Path Is The Identifier | `scripts/check-okf` for an `id` key that differs from the path, and a missed requirement for the move that rewrites links until the move command exists | -| open-knowledge-format-bundle | Reserved Names | `scripts/check-okf` for frontmatter on a reserved name, and a missed requirement for a hand-edited directory index until the generator writes it | +| open-knowledge-format-bundle | Type On Every File | `scripts/check-okf`, which derives the type from the nearest structure schema and accepts an untyped file, and a [missed requirement][MISSEDREQUIREMENT] for the graph export filling the type in | +| open-knowledge-format-bundle | Path Is The Identifier | `scripts/check-okf` for an `id` key that differs from the path, and a [missed requirement][MISSEDREQUIREMENT] for the move that rewrites links until the move command exists | +| open-knowledge-format-bundle | Reserved Names | `scripts/check-okf` for frontmatter on a reserved name, and a [missed requirement][MISSEDREQUIREMENT] for a hand-edited directory index until the generator writes it | | open-knowledge-format-bundle | Bundle Declares Its Version | `scripts/check-okf`, warning-first until the generator writes the root `index.md` | -| directories-direct-the-tools | Directories Direct | the Core.Terminology Vale rule for the word `folder`, and a missed requirement for the routing judgment, which review enforces | -| directories-direct-the-tools | Qualifier Directory Names Are Checked | missed requirement: the narrow check on the directory names a harness acts on has no tool chosen yet | +| directories-direct-the-tools | Directories Direct | the Core.Terminology Vale rule for the word `folder`, and a [missed requirement][MISSEDREQUIREMENT] for the routing judgment, which review enforces | +| directories-direct-the-tools | Qualifier Directory Names Are Checked | [missed requirement][MISSEDREQUIREMENT]: the narrow check on the directory names a harness acts on has no tool chosen yet | | directories-direct-the-tools | Catch-All Directories Drain | `scripts/check-stale-queue`: an item older than sixty days since its first commit is stale, older than sixty-seven days an error, and the unsorted archive never ages | -| user-values-and-variables | Variables Are $NAME | the Core.UserValue Vale rule on instructions, and a missed requirement for the assembler leaving a user variable unsubstituted | -| user-values-and-variables | Canon Stays Vendor-Neutral | the Core.HarnessPlaceholder Vale rule on instructions, off in a harness companion directory, `rune validate` for the harness-file layout, and a missed requirement for harness wording inside a canon | -| instructions-stay-vendor-neutral | Reusable Across Harnesses and Models | `rune validate` for one entrypoint per instruction, the deployment drift check, and a missed requirement for an assembly run for every harness with no sidecar present | -| instructions-stay-vendor-neutral | Layout Is Enforced | `rune validate` and mdschema for the canon and its sidecars, the deployment manifest for every target, and a missed requirement for each harness's own schema over its instances, where the Agent Skills validator runs strict only for a harness that loads that format | -| instructions-stay-vendor-neutral | One Instance per Harness | `rune validate` for the deployment manifest and `scripts/check-provenance` for the provenance record, and a missed requirement for the runtime hook that swaps a model variant | -| instructions-stay-vendor-neutral | Companions Earn Their Place | missed requirement: no evaluation run compares a companion with the canon alone in the hooks yet | -| ceremony-matches-the-task | Ceremony Matches The Task | missed requirement: no check challenges a commit marked small that touches a specification, the ontology, a record, or a test expectation | -| single-source-of-truth | Code Is Truth | missed requirement: no check compares a comment with its code, and the `glossary-current` hook is the only regeneration check so far | -| single-source-of-truth | One Home Per Fact | missed requirement: because no check finds a copied statement | -| ceremony-matches-the-task | Semantic Density | missed requirement for a sentence that states nothing, while Vale with the Simplified Technical English and Deslop styles reports filler | +| user-values-and-variables | Variables Are $NAME | the Core.UserValue Vale rule on instructions | +| user-values-and-variables | Both Shell Forms Resolve In The Shell | [missed requirement][MISSEDREQUIREMENT]: no check proves the assembler leaves a user variable unsubstituted | +| user-values-and-variables | Canon Stays Vendor-Neutral | the Core.HarnessPlaceholder Vale rule on instructions, off in a harness companion directory, `rune validate` for the harness-file layout, and a [missed requirement][MISSEDREQUIREMENT] for harness wording inside a canon | +| user-values-and-variables | Assembly Projects Out One Harness | [missed requirement][MISSEDREQUIREMENT]: no check proves the assembler projects one harness from the canon and its basis parameters | +| instructions-stay-vendor-neutral | Reusable Across Harnesses and Models | `rune validate` for one entrypoint per instruction, the deployment drift check, and a [missed requirement][MISSEDREQUIREMENT] for an assembly run for every harness with no sidecar present | +| instructions-stay-vendor-neutral | Layout Is Enforced | `rune validate` and mdschema for the canon and its sidecars, the deployment manifest for every target, and a [missed requirement][MISSEDREQUIREMENT] for each harness's own schema over its instances, where the Agent Skills validator runs strict only for a harness that loads that format | +| instructions-stay-vendor-neutral | One Instance per Harness | `rune validate` for the deployment manifest, and a [missed requirement][MISSEDREQUIREMENT] for the runtime hook that swaps a model variant | +| instructions-stay-vendor-neutral | Every Instance Has A Provenance Record | `scripts/check-provenance` for the provenance record beside every deployed file | +| instructions-stay-vendor-neutral | Companions Earn Their Place | [missed requirement][MISSEDREQUIREMENT]: no evaluation run compares a companion with the canon alone in the hooks yet | +| ceremony-matches-the-task | Ceremony Matches The Task | [missed requirement][MISSEDREQUIREMENT]: review decides whether a fix is a plain commit or a change | +| ceremony-matches-the-task | A Check Challenges The Small Mark | [missed requirement][MISSEDREQUIREMENT]: no check challenges a commit marked small that touches a specification, the ontology, a record, or a test expectation | +| single-source-of-truth | Code Is Truth | [missed requirement][MISSEDREQUIREMENT]: no check compares a comment with its code, and the `glossary-current` hook is the only regeneration check so far | +| single-source-of-truth | One Home Per Fact | [missed requirement][MISSEDREQUIREMENT]: because no check finds a copied statement | +| ceremony-matches-the-task | Semantic Density | [missed requirement][MISSEDREQUIREMENT] for a sentence that states nothing, while Vale with the Simplified Technical English and Deslop styles reports filler | | ceremony-matches-the-task | Verbs Name Actions | the Core.Nominalization Vale rule on headings and steps, warning-first until its baseline is clean | -| lint-enforced-foundation-requirements | Analysis Tool Coverage | `scripts/check-coverage`: every stated requirement has a row and every row names a stated requirement | -| lint-enforced-foundation-requirements | Record Cites Its Proofs | missed requirement: the graph has the `proves` edge and no check reads it against a record's status yet | -| lint-enforced-foundation-requirements | Analysis Tool Has Fixtures | `scripts/check-fixtures` against `tests/baselines/fixtures.txt`, a missed requirement for the scripts in the baseline | +| lint-enforced-foundation-requirements | [Analysis Tool][ANALYSISTOOL] Coverage | `scripts/check-coverage`: every stated [requirement][REQUIREMENT] has a row and every row names a stated [requirement][REQUIREMENT] | +| lint-enforced-foundation-requirements | Record Cites Its Proofs | [missed requirement][MISSEDREQUIREMENT]: the graph has the `proves` edge and no check reads it against a record's status yet | +| lint-enforced-foundation-requirements | [Analysis Tool][ANALYSISTOOL] Has Fixtures | `scripts/check-fixtures` against `tests/baselines/fixtures.txt`, a [missed requirement][MISSEDREQUIREMENT] for the scripts in the baseline | ## Commands -A requirement names a role. This table names the program, flags, hook, and diagnostic ids behind each row, so nothing is lost when a command moves out of a requirement. A row that says "not built" is a task in `tasks.md`. +A [requirement][REQUIREMENT] names a role. This table names the program, flags, hook, and diagnostic ids behind each row, so nothing is lost when a command moves out of a [requirement][REQUIREMENT]. A row that says "not built" is a task in `tasks.md`. -| Requirement | Role | Command and flags | Hook | Diagnostic ids | +| [Requirement][REQUIREMENT] | Role | Command and flags | Hook | Diagnostic ids | |---|---|---|---|---| | Binary Assets Carry a Description | validator | `python3 scripts/check-binary-assets` | `check-binary-assets` | `binary` | | Lossless Renders, Glossary Renders From The Ontology | validator, generator | `rune spec glossary --check`, `rune spec glossary --write`, fallback `scripts/fallback/generate-glossary --check` | `glossary-current` | | @@ -74,18 +79,18 @@ A requirement names a role. This table names the program, flags, hook, and diagn | Code Is Truth | generator | `rune spec glossary --write` today. Root index, directory indexes, changelog: not built | `glossary-current` | | | Semantic Density | validator | Vale with the Simplified Technical English and Deslop styles | `vale` | | | Verbs Name Actions | validator | Vale Core.Nominalization | `vale` | | -| Analysis Tool Coverage | validator | `python3 scripts/check-coverage` | `check-coverage` | | -| Analysis Tool Has Fixtures | validator | `python3 scripts/check-fixtures` against `tests/baselines/fixtures.txt`, fixtures under `tests/fixtures//valid/` and `tests/fixtures//invalid/` | `check-fixtures` | | +| [Analysis Tool][ANALYSISTOOL] Coverage | validator | `python3 scripts/check-coverage` | `check-coverage` | | +| [Analysis Tool][ANALYSISTOOL] Has Fixtures | validator | `python3 scripts/check-fixtures` against `tests/baselines/fixtures.txt`, fixtures under `tests/fixtures//valid/` and `tests/fixtures//invalid/` | `check-fixtures` | | | Record Cites Its Proofs | validator | none yet | | | ## Decisions -- The record CORE-0023 Rune Fills the Tool Roles says that rune fills the roles the requirements name: validator, assembler, generator, deployer, fixer, signer, archiver. The requirements stay a philosophy for treating instructions as artifacts, and the record lists the capabilities each role requires. +- The record CORE-0023 Rune Fills the Tool Roles says that rune fills the roles the [requirements][REQUIREMENT] name: validator, assembler, generator, deployer, fixer, signer, archiver. The [requirements][REQUIREMENT] stay a philosophy for treating instructions as artifacts, and the record lists the capabilities each role requires. - Path Is The Identifier rejects two alternatives. A permanently minted id, such as a random id in the frontmatter, pays off only when outsiders hold links into the repository. A bare file name repeats, like every README and every skill's entrypoint. - Reserved Names: `index.md` lists a directory and `log.md` is a newest-first history under date headings, as sections 8 and 9 of the Open Knowledge Format describe. The directory's authored page follows Obsidian's `folder note` pattern under the name directory note. The generator writes each directory's `index.md` as the directory note included with `![text](relative path)`, then the generated list of contents. The human text lives only in the directory note. The changelog is a separate file from `log.md`. -- Two changes carry what the foundations only name. `environment-variables-supported-syntax` says how a value gets resolved and absorbs the requirement The Shell Resolves the Variable. `one-instance-per-model` holds a variant per model and the runtime hook that swaps it in. -- Four requirements of the first draft are gone. Flat Frontmatter is dropped, and frontmatter may nest. The Shell Resolves the Variable moved to `environment-variables-supported-syntax`. Harness Specifics Stay Beside the Source merged into Canon Stays Vendor-Neutral. Portable Instructions merged into Reusable Across Harnesses and Models as its scenario Every sidecar is removed. -- Directories Direct split on 2026-10-18. The narrow check on the directory names a harness acts on is its own requirement, Qualifier Directory Names Are Checked. The 500-character cap on a requirement body left no room for both the examples and the check. The record CORE-0004 cites Directories Direct alone. +- Two changes carry what the foundations only name. `environment-variables-supported-syntax` says how a value gets resolved and absorbs the [requirement][REQUIREMENT] The Shell Resolves the Variable. `one-instance-per-model` holds a variant per model and the runtime hook that swaps it in. +- Four [requirements][REQUIREMENT] of the first draft are gone. Flat Frontmatter is dropped, and frontmatter may nest. The Shell Resolves the Variable moved to `environment-variables-supported-syntax`. Harness Specifics Stay Beside the Source merged into Canon Stays Vendor-Neutral. Portable Instructions merged into Reusable Across Harnesses and Models as its scenario Every sidecar is removed. +- Directories Direct split on 2026-10-18. The narrow check on the directory names a harness acts on is its own [requirement][REQUIREMENT], Qualifier Directory Names Are Checked. The 500-character cap on a [requirement][REQUIREMENT] body left no room for both the examples and the check. The record CORE-0004 cites Directories Direct alone. - Layout Is Enforced checks each instance against its own harness's schema because the schemas differ. Claude Code uses PascalCase skill directories where the Agent Skills specification uses kebab-case. - Metadata Inside Files counts the deployment manifest as a derived store because it describes outputs and no authored file. Frontmatter wins over it, and the generator rewrites it. - Type On Every File keeps its heading until the owner chooses between the name and its plain-words rewrite, Type Comes From The Directory. The record CORE-0006 cites the current anchor, so the rename changes the record too. diff --git a/docs/changes/plain-text-first-principles/proposal.md b/docs/changes/plain-text-first-principles/proposal.md index 4ddb87c2..04a32621 100644 --- a/docs/changes/plain-text-first-principles/proposal.md +++ b/docs/changes/plain-text-first-principles/proposal.md @@ -18,9 +18,9 @@ decisions: ## Why -We steer language *[models][MODEL]* with written *[instructions][INSTRUCTION]*, and we want those instructions to outlast any model, *[harness][HARNESS]*, or vendor. A harness is replaced every year. A model changes with every release, and an instruction tuned for one generation over-specifies for the next ([context engineering][CTX], [persona study][PERSONA]). The durable part is the instructions, the records of why they are what they are, and the specifications they answer to. That part needs a form that survives the turnover. +We steer language *[models][MODEL]* with written *[instructions][INSTRUCTION]*, and we want those [instructions][INSTRUCTION] to outlast any [model][MODEL], *[harness][HARNESS]*, or vendor. A [harness][HARNESS] is replaced every year. A [model][MODEL] *[changes][CHANGE]* with every release, and an [instruction][INSTRUCTION] tuned for one generation over-specifies for the next ([context engineering][CTX], [persona study][PERSONA]). The durable part is the [instructions][INSTRUCTION], the records of why they are what they are, and the specifications they answer to. That part needs a form that survives the turnover. -Plain text is that form. Anything with a text *[representation][REPRESENTATION]* is something every tool we use can read, diff, search, and version ([Textuality][TAOUP]). Among plain-text formats, Markdown with YAML frontmatter is the one a person reads in an editor and a model reads through a harness. A note-taking tool such as Obsidian indexes the same file with no conversion ([Obsidian Properties][OBS-PROPS], [Jekyll][JEKYLL], [Hugo][HUGO]). Markdown is also the format the models themselves were trained to read and write. +Plain text is that form. Anything with a text *[representation][REPRESENTATION]* is something every tool we use can read, diff, search, and version ([Textuality][TAOUP]). Among plain-text formats, Markdown with YAML frontmatter is the one a person reads in an editor and a [model][MODEL] reads through a [harness][HARNESS]. A note-taking tool such as Obsidian indexes the same file with no conversion ([Obsidian Properties][OBS-PROPS], [Jekyll][JEKYLL], [Hugo][HUGO]). Markdown is also the format the [models][MODEL] themselves were trained to read and write. From that choice the rest follows: @@ -28,53 +28,53 @@ From that choice the rest follows: - Files link to each other with relative Markdown links. A file's type comes from its directory's *[structure schema][STRUCTURESCHEMA]* unless the file says otherwise. So a directory of files reads as a graph with no database beside it ([Open Knowledge Format][OKF]). - The work sits in files the owner holds, in a format any tool reads. A tool keeps at most a cache that rebuilds from the files ([File over app][FOA]). - A directory name tells a tool what to do with the files inside it. The tree then needs no guide about where things go ([Convention over Configuration][RAILS]). -- A shared instruction refers to a user-specific value as a shell variable, `$NAME` or `${NAME}`, and never spells it out. How the value gets resolved is the subject of the change `environment-variables-supported-syntax`. -- An instruction is written once and reused by every harness and model. What one harness needs sits beside the *[state file][STATEFILE]* as *[basis parameters][BASISPARAMETERS]*. The *[assembler][ASSEMBLER]* produces one *[instance][INSTANCE]* per harness with its *[provenance record][PROVENANCERECORD]* sealed. A model's variant is swapped in at run time, the subject of the change `one-instance-per-model`. +- A shared [instruction][INSTRUCTION] refers to a user-specific value as a shell variable, `$NAME` or `${NAME}`, and never spells it out. How the value gets resolved is the subject of the [change][CHANGE] `environment-variables-supported-syntax`. +- An [instruction][INSTRUCTION] is written once and reused by every [harness][HARNESS] and [model][MODEL]. What one [harness][HARNESS] needs sits beside the *[state file][STATEFILE]* as *[basis parameters][BASISPARAMETERS]*. The *[assembler][ASSEMBLER]* produces one *[instance][INSTANCE]* per [harness][HARNESS] with its *[provenance record][PROVENANCERECORD]* sealed. A [model][MODEL]'s variant is swapped in at run time, the subject of the [change][CHANGE] `one-instance-per-model`. - Every expectation is a *[requirement][REQUIREMENT]* with a *[scenario][SCENARIO]* that can fail ([RFC 2119][RFC-2119]). Every decision with alternatives gets a record with a status lifecycle and a fixed structure ([Nygard][NYGARD], [MADR][MADR], [Structured MADR][SMADR]). A record cites a source by name where it uses it. A bare number is never a citation. -- A requirement is proven: each scenario ends in a recording a reader can replay. An analysis tool is proven by one input it accepts and one it rejects. -- Complexity is removed wherever a way is found. Ceremony matches the task. Code is authoritative over prose, because the code is what runs, and anything derived regenerates on demand or does not exist. A fact has one home, and every other file links there. Every line in a model's context has to earn its place. A line that stays in context costs tokens on every turn ([context engineering][CTX]). An action is named with a verb: `accepting a specification` rather than `specification acceptance`. +- A [requirement][REQUIREMENT] is proven: each [scenario][SCENARIO] ends in a recording a reader can replay. An analysis tool is proven by one input it accepts and one it rejects. +- Complexity is removed wherever a way is found. Ceremony matches the task. Code is authoritative over prose, because the code is what runs, and anything derived regenerates on demand or does not exist. A fact has one home, and every other file links there. Every line in a [model][MODEL]'s context has to earn its place. A line that stays in context costs tokens on every turn ([context engineering][CTX]). An action is named with a verb: `accepting a specification` rather than `specification acceptance`. -Two ideas from science decide what stays in an instruction, *[falsifiability][FALSIFIABILITY]* and *[Occam's razor][OCCAMSRAZOR]*. A requirement states a check that can fail, and the shortest instruction that passes the check is the one we keep. Removing over eighty percent of a production system prompt kept the same evaluation score. That is the measure a cut has to meet ([context engineering][CTX]). Instructions say what gets done, and never how it should feel. Compliance comes from four levers, strongest first: +Two ideas from science decide what stays in an [instruction][INSTRUCTION], *[falsifiability][FALSIFIABILITY]* and *[Occam's razor][OCCAMSRAZOR]*. A [requirement][REQUIREMENT] states a check that can fail, and the shortest [instruction][INSTRUCTION] that passes the check is the one we keep. Removing over eighty percent of a production system prompt kept the same evaluation score. That is the measure a cut has to meet ([context engineering][CTX]). [Instructions][INSTRUCTION] say what gets done, and never how it should feel. Compliance comes from four levers, strongest first: 1. **Architecture** removes the cause: better structure, better data. One canonical tree ends mirror drift. 2. **Tests and linters** catch every recurrence mechanically. A prose rule flags a banned word on write. -3. **Instructions**, meaning skills and rules, steer the model directly. +3. **Instructions**, meaning skills and rules, steer the [model][MODEL] directly. 4. **Human review** comes last. Human attention is the most expensive resource, so it catches only what nothing else did. Three positions argue against parts of this, and each gets an answer rather than silence: -- Prompts are technical debt ([Theo][THEO-DEBT]): behavioral steering decays silently with every model upgrade, so an instruction file holds concrete facts. Our answer is the benchmark. A behavioral instruction stays only while it beats a facts-only baseline, and the benchmark runs again when the model changes. +- Prompts are technical debt ([Theo][THEO-DEBT]): behavioral steering decays silently with every [model][MODEL] upgrade, so an [instruction][INSTRUCTION] file holds concrete facts. Our answer is the benchmark. A behavioral [instruction][INSTRUCTION] stays only while it beats a facts-only baseline, and the benchmark runs again when the [model][MODEL] [changes][CHANGE]. - HTML is the better output for people ([Theo][THEO-HTML]): an agent's report is denser and interactive as HTML. Our answer keeps the split. Markdown is the *[source state][SOURCESTATE]*, and HTML is a *[render][RENDER]* of it. The reverse never holds. - Markdown is a terrible language ([Theo][THEO-MD]): ambiguous syntax and dialect drift break the portability promise. Our answer is a defined subset. The *[repository][REPOSITORY]* names the Markdown it accepts and checks it on every write, because a shared file extension is not a shared parse. ## What Changes -The foundations are ten *[capabilities][CAPABILITY]*. Each capability has one specification, and each requirement in it has a scenario that can fail. Each requirement is bound to its check or to a *[missed requirement][MISSEDREQUIREMENT]* in a *[coverage][COVERAGE]* table. A requirement names a role and never a program. The *[validator][VALIDATOR]* reports, the assembler builds, the *[generator][GENERATOR]* writes derived files, the *[fixer][FIXER]* repairs, and the *[archiver][ARCHIVER]* accepts a change. The record CORE-0023 says that rune fills those roles. +The foundations are ten *[capabilities][CAPABILITY]*. Each [capability][CAPABILITY] has one specification, and each [requirement][REQUIREMENT] in it has a [scenario][SCENARIO] that can fail. Each [requirement][REQUIREMENT] is bound to its check or to a *[missed requirement][MISSEDREQUIREMENT]* in a *[coverage][COVERAGE]* table. A [requirement][REQUIREMENT] names a role and never a program. The *[validator][VALIDATOR]* reports, the [assembler][ASSEMBLER] builds, the *[generator][GENERATOR]* writes derived files, the *[fixer][FIXER]* repairs, and the *[archiver][ARCHIVER]* accepts a [change][CHANGE]. The record CORE-0023 says that rune fills those roles. -Two further *[changes][CHANGE]* carry what the foundations only name. `environment-variables-supported-syntax` says how a value a shared instruction refers to gets resolved. `one-instance-per-model` says how a model's variant is swapped in at run time. +Two further [changes][CHANGE] carry what the foundations only name. `environment-variables-supported-syntax` says how a value a shared [instruction][INSTRUCTION] refers to gets resolved. `one-instance-per-model` says how a [model][MODEL]'s variant is swapped in at run time. ## Capabilities ### New Capabilities -- `plain-text-is-futureproof`: every *[artifact][ARTIFACT]* has an authoritative plain-text representation, and text is UTF-8. A committed render regenerates from its state file, and a *[projection][PROJECTION]* is never authoritative. +- `plain-text-is-futureproof`: every *[artifact][ARTIFACT]* has an authoritative plain-text [representation][REPRESENTATION], and text is UTF-8. A committed [render][RENDER] regenerates from its [state file][STATEFILE], and a *[projection][PROJECTION]* is never authoritative. - `markdown-with-yaml-frontmatter`: GitHub Flavored Markdown with YAML frontmatter is the system language, and metadata is inside the file. Citations are reference links, a defined term links to its glossary page, and the glossary is one generated page per term. -- `markdown-relative-links-matching`: files reference each other with relative Markdown links that all resolve. A state file includes another with the exclamation-mark link that the assembler translates per harness, and wikilinks stay out. -- `open-knowledge-format-bundle`: the repository is an Open Knowledge Format 0.2 bundle. A file's type comes from its directory's structure schema, and the path is the identifier. The reserved names are respected, and the root index is generated with the version. +- `markdown-relative-links-matching`: files reference each other with relative Markdown links that all resolve. A [state file][STATEFILE] includes another with the exclamation-mark link that the [assembler][ASSEMBLER] translates per [harness][HARNESS], and wikilinks stay out. +- `open-knowledge-format-bundle`: the [repository][REPOSITORY] is an Open Knowledge Format 0.2 bundle. A file's type comes from its directory's [structure schema][STRUCTURESCHEMA], and the path is the identifier. The reserved names are respected, and the root index is generated with the version. - `directories-direct-the-tools`: a directory name tells a tool what to do. An item in a queue directory is stale after sixty days and an error after sixty-seven. -- `user-values-and-variables`: a shared instruction refers to a user-specific value as `$NAME` or `${NAME}`. The canon stays vendor-neutral, with harness specifics in its basis parameters. -- `instructions-stay-vendor-neutral`: an instruction is written once, and assembly makes one instance per harness with a sealed provenance record. Each instance is checked by its harness's schema, and a companion stays only while it beats the canon on an evaluation. +- `user-values-and-variables`: a shared [instruction][INSTRUCTION] refers to a user-specific value as `$NAME` or `${NAME}`. The canon stays vendor-neutral, with [harness][HARNESS] specifics in its [basis parameters][BASISPARAMETERS]. +- `instructions-stay-vendor-neutral`: an [instruction][INSTRUCTION] is written once, and assembly makes one [instance][INSTANCE] per [harness][HARNESS] with a sealed [provenance record][PROVENANCERECORD]. Each [instance][INSTANCE] is checked by its [harness][HARNESS]'s schema, and a companion stays only while it beats the canon on an evaluation. - `ceremony-matches-the-task`: ceremony matches the task and a check challenges a commit marked small. Every line in context earns its place, and actions are named with verbs. - `single-source-of-truth`: code is authoritative over prose, and anything derived regenerates on demand or does not exist. Each fact has one home that every other file links to. -- `lint-enforced-foundation-requirements`: a coverage table binds every foundation requirement to its check or to a missed requirement that closes warning-first. Every analysis tool is proven by fixtures. +- `lint-enforced-foundation-requirements`: a [coverage][COVERAGE] table binds every foundation [requirement][REQUIREMENT] to its check or to a [missed requirement][MISSEDREQUIREMENT] that closes warning-first. Every analysis tool is proven by fixtures. ## Impact - Ten canonical specifications under `docs/specs/`. -- Ten records under `docs/decisions/`, each citing the requirements it answers as its upstream. CORE-0003 is amended so frontmatter may nest, and CORE-0023 names the roles rune fills. +- Ten records under `docs/decisions/`, each citing the [requirements][REQUIREMENT] it answers as its upstream. CORE-0003 is amended so frontmatter may nest, and CORE-0023 names the roles rune fills. - An ontology, `ontology/rune.ttl`, that defines every term the specifications use. Shape constraints (SHACL) that a commit must pass sit beside it, with a JSON-LD context that maps every frontmatter key to its term. - A glossary of one generated page per term under `docs/specs/glossary/`, which every term link points at. -- A coverage table in `design.md` with one row per requirement and the commands behind each row. The linters and check scripts those rows name are each proven by fixtures. +- A [coverage][COVERAGE] table in `design.md` with one row per [requirement][REQUIREMENT] and the commands behind each row. The linters and check scripts those rows name are each proven by fixtures. - A plain-text snapshot of the canvas under `docs/ideas/`. ## Sources @@ -88,8 +88,8 @@ Two further *[changes][CHANGE]* carry what the foundations only name. `environme - [Jekyll][JEKYLL] and [Hugo][HUGO] front matter - [Documenting Architecture Decisions][NYGARD], Michael Nygard - [MADR][MADR], the [MADR Template Primer][MADR-PRIMER], and [Structured MADR][SMADR] -- [OpenSpec][OPENSPEC], the change lifecycle -- [RFC 2119][RFC-2119], the MUST vocabulary of requirements +- [OpenSpec][OPENSPEC], the [change][CHANGE] lifecycle +- [RFC 2119][RFC-2119], the MUST vocabulary of [requirements][REQUIREMENT] - [Convention over Configuration][RAILS], the Rails doctrine - [The new rules of context engineering for Claude 5 generation models][CTX] and the [persona study][PERSONA] - Theo Browne (t3.gg) on [prompts as technical debt][THEO-DEBT], [HTML as agent output][THEO-HTML], and [Markdown as a terrible language][THEO-MD] diff --git a/docs/changes/plain-text-first-principles/specs/ceremony-matches-the-task/spec.md b/docs/changes/plain-text-first-principles/specs/ceremony-matches-the-task/spec.md index 7d51f718..14e8dac2 100644 --- a/docs/changes/plain-text-first-principles/specs/ceremony-matches-the-task/spec.md +++ b/docs/changes/plain-text-first-principles/specs/ceremony-matches-the-task/spec.md @@ -2,23 +2,26 @@ ### Requirement: Ceremony Matches The Task -The weight of the process MUST match the size of the task. A fix that changes no requirement takes a plain commit. A behavior change takes a *[change][CHANGE]* directory, and a decision with alternatives takes a record. Unneeded ceremony is complexity, and we remove it. -The author marks a commit as small, and the *[validator][VALIDATOR]* challenges that mark whenever it touches anything that defines behavior. The author then takes the full process, or overrides the challenge with a reason. - -#### Scenario: Small mark touches a specification - -- **WHEN** a commit marked small touches a specification, the ontology, a decision record, or a test's expected result -- **THEN** the [validator][VALIDATOR] challenges the mark, and the author takes the full process or overrides the challenge with a reason +The weight of the process MUST match the size of the task. A fix that *[changes][CHANGE]* no requirement takes a plain commit. A behavior [change][CHANGE] takes a [change][CHANGE] directory, and a decision with alternatives takes a record. Unneeded ceremony is complexity, and we remove it. #### Scenario: Small fix arrives with full ceremony - **WHEN** a [change][CHANGE] directory contains only a fix that alters no requirement - **THEN** review turns it into a plain commit and removes the directory +### Requirement: A Check Challenges The Small Mark + +The author marks a commit as small. The *[validator][VALIDATOR]* MUST challenge that mark whenever the commit touches anything that defines behavior. The author then takes the full process, or overrides the challenge with a reason. + +#### Scenario: Small mark touches a specification + +- **WHEN** a commit marked small touches a specification, the ontology, a decision record, or a test's expected result +- **THEN** the [validator][VALIDATOR] challenges the mark, and the author takes the full process or overrides the challenge with a reason + ### Requirement: Semantic Density A line an *[instruction][INSTRUCTION]* loads into a *[model][MODEL]*'s context MUST state a fact, a constraint, or an action. -A line the model could drop without losing anything is filler. Filler that stays in context costs tokens on every turn. +A line the [model][MODEL] could drop without losing anything is filler. Filler that stays in context costs tokens on every turn. #### Scenario: Filler enters an instruction diff --git a/docs/changes/plain-text-first-principles/specs/directories-direct-the-tools/spec.md b/docs/changes/plain-text-first-principles/specs/directories-direct-the-tools/spec.md index 294cfee2..f5535cd9 100644 --- a/docs/changes/plain-text-first-principles/specs/directories-direct-the-tools/spec.md +++ b/docs/changes/plain-text-first-principles/specs/directories-direct-the-tools/spec.md @@ -17,13 +17,13 @@ Review enforces the judgment, a *[missed requirement][MISSEDREQUIREMENT]* in the ### Requirement: Qualifier Directory Names Are Checked -One narrow check MUST cover the directory names a [harness][HARNESS] acts on, such as its rules directory. A typo there silently disables content, and the harness reports no error. The [validator][VALIDATOR] reports a name the harness does not document. +One narrow check MUST cover the directory names a [harness][HARNESS] acts on, such as its rules directory. A typo there silently disables content, and the [harness][HARNESS] reports no error. The [validator][VALIDATOR] reports a name the [harness][HARNESS] does not document. The routing judgment itself stays with review. A wrong name elsewhere costs little, and a general check would force every directory onto an allowlist, which the owner rejected. #### Scenario: Qualifier directory name is misspelled - **WHEN** a directory name a [harness][HARNESS] acts on, such as its rules directory, is misspelled -- **THEN** the [validator][VALIDATOR] reports the name, because the harness loads nothing from the misspelling and reports no error +- **THEN** the [validator][VALIDATOR] reports the name, because the [harness][HARNESS] loads nothing from the misspelling and reports no error ### Requirement: Catch-All Directories Drain diff --git a/docs/changes/plain-text-first-principles/specs/instructions-stay-vendor-neutral/spec.md b/docs/changes/plain-text-first-principles/specs/instructions-stay-vendor-neutral/spec.md index 910e5c4c..09dc2df1 100644 --- a/docs/changes/plain-text-first-principles/specs/instructions-stay-vendor-neutral/spec.md +++ b/docs/changes/plain-text-first-principles/specs/instructions-stay-vendor-neutral/spec.md @@ -2,12 +2,12 @@ ### Requirement: Reusable Across Harnesses and Models -An *[instruction][INSTRUCTION]* MUST be written once and reused by every *[harness][HARNESS]* and every *[model][MODEL]*. Its *[state file][STATEFILE]* is one *[canon][CANON]* per instruction, and a harness or model difference MUST NOT fork it. The difference goes into *[basis parameters][BASISPARAMETERS]* beside the canon or into a model *[variant][VARIANT]*. Where a form every harness reads exists, the canon MUST use it. +An *[instruction][INSTRUCTION]* MUST be written once and reused by every *[harness][HARNESS]* and every *[model][MODEL]*. Its *[state file][STATEFILE]* is one *[canon][CANON]* per [instruction][INSTRUCTION], and a [harness][HARNESS] or [model][MODEL] difference MUST NOT fork it. The difference goes into *[basis parameters][BASISPARAMETERS]* beside the [canon][CANON] or into a [model][MODEL] *[variant][VARIANT]*. Where a form every [harness][HARNESS] reads exists, the [canon][CANON] MUST use it. #### Scenario: Second canon appears for one instruction - **WHEN** an [instruction][INSTRUCTION] gets a second [canon][CANON] beside the one it already has -- **THEN** the *[validator][VALIDATOR]* reports the duplicate, and the author merges it into the canon or into a [harness][HARNESS]'s [basis parameters][BASISPARAMETERS] +- **THEN** the *[validator][VALIDATOR]* reports the duplicate, and the author merges it into the [canon][CANON] or into a [harness][HARNESS]'s [basis parameters][BASISPARAMETERS] #### Scenario: Deployed copy is edited in place @@ -26,9 +26,9 @@ The [validator][VALIDATOR] checks a [canon][CANON] and its [sidecars][SIDECAR] a #### Scenario: Instance breaks its harness's schema -- **WHEN** an assembled [instance][INSTANCE] for a [harness][HARNESS] breaks that harness's schema, a Claude Code instance against Claude Code's rules -- **THEN** the [validator][VALIDATOR] reports the instance, and the author fixes the [canon][CANON] or its [basis parameters][BASISPARAMETERS] -- **AND** the Agent Skills reference validator runs only for a harness that loads that format +- **WHEN** an assembled [instance][INSTANCE] for a [harness][HARNESS] breaks that [harness][HARNESS]'s schema, a Claude Code [instance][INSTANCE] against Claude Code's rules +- **THEN** the [validator][VALIDATOR] reports the [instance][INSTANCE], and the author fixes the [canon][CANON] or its [basis parameters][BASISPARAMETERS] +- **AND** the Agent Skills reference [validator][VALIDATOR] runs only for a [harness][HARNESS] that loads that format #### Scenario: Layout rule has no analysis tool @@ -37,28 +37,31 @@ The [validator][VALIDATOR] checks a [canon][CANON] and its [sidecars][SIDECAR] a ### Requirement: One Instance per Harness -The [assembler][ASSEMBLER] MUST produce one [instance][INSTANCE] of each *[rune][RUNE]* per [harness][HARNESS], from the [canon][CANON] plus that harness's [basis parameters][BASISPARAMETERS]. When [model][MODEL] [variants][VARIANT] exist, a runtime hook swaps in the active model's variant at session start or when the model changes. The *[deployer][DEPLOYER]* never writes one instance per model. -Each instance MUST have a *[provenance record][PROVENANCERECORD]*. - -#### Scenario: Assembly writes an instance - -- **WHEN** the [deployer][DEPLOYER] writes an [instance][INSTANCE] of an [instruction][INSTRUCTION] into a [target][TARGET] -- **THEN** the deployment manifest records the instance's fingerprint, and the [provenance record][PROVENANCERECORD] seals which [state file][STATEFILE], which [basis parameters][BASISPARAMETERS], and which operator produced it +The [assembler][ASSEMBLER] MUST produce one [instance][INSTANCE] of each *[rune][RUNE]* per [harness][HARNESS], from the [canon][CANON] plus that [harness][HARNESS]'s [basis parameters][BASISPARAMETERS]. When [model][MODEL] [variants][VARIANT] exist, a runtime hook swaps in the active [model][MODEL]'s [variant][VARIANT] at session start or when the [model][MODEL] changes. The *[deployer][DEPLOYER]* never writes one [instance][INSTANCE] per [model][MODEL]. #### Scenario: New harness joins - **WHEN** a new [harness][HARNESS] joins the deployment roster -- **THEN** the [assembler][ASSEMBLER] produces that harness's [instance][INSTANCE] of every [instruction][INSTRUCTION] from the unchanged [canons][CANON]. The only new files in the [repository][REPOSITORY] are that harness's [basis parameters][BASISPARAMETERS] +- **THEN** the [assembler][ASSEMBLER] produces that [harness][HARNESS]'s [instance][INSTANCE] of every [instruction][INSTRUCTION] from the unchanged [canons][CANON]. The only new files in the [repository][REPOSITORY] are that [harness][HARNESS]'s [basis parameters][BASISPARAMETERS] + +### Requirement: Every Instance Has A Provenance Record + +Each [instance][INSTANCE] MUST have a *[provenance record][PROVENANCERECORD]*. + +#### Scenario: Assembly writes an instance + +- **WHEN** the [deployer][DEPLOYER] writes an [instance][INSTANCE] of an [instruction][INSTRUCTION] into a [target][TARGET] +- **THEN** the deployment manifest records the [instance][INSTANCE]'s fingerprint, and the [provenance record][PROVENANCERECORD] seals which [state file][STATEFILE], which [basis parameters][BASISPARAMETERS], and which operator produced it ### Requirement: Companions Earn Their Place -A *[companion][COMPANION]* for one [harness][HARNESS] or one [model][MODEL] is its [basis parameters][BASISPARAMETERS] or its [variant][VARIANT]. It MUST beat the [canon][CANON] alone on an evaluation for that harness and model, or the author removes it. -This applies *[Occam's razor][OCCAMSRAZOR]*: of two [instructions][INSTRUCTION] that pass the same check, the shorter one wins. A companion that adds nothing the evaluation can see is ceremony. +A *[companion][COMPANION]* for one [harness][HARNESS] or one [model][MODEL] is its [basis parameters][BASISPARAMETERS] or its [variant][VARIANT]. It MUST beat the [canon][CANON] alone on an evaluation for that [harness][HARNESS] and [model][MODEL], or the author removes it. +This applies *[Occam's razor][OCCAMSRAZOR]*: of two [instructions][INSTRUCTION] that pass the same check, the shorter one wins. A [companion][COMPANION] that adds nothing the evaluation can see is ceremony. #### Scenario: Companion fails to beat the canon -- **WHEN** an evaluation for a [harness][HARNESS] and [model][MODEL] scores the [canon][CANON] alone at least as well as the canon with its [companion][COMPANION] -- **THEN** the author removes the companion +- **WHEN** an evaluation for a [harness][HARNESS] and [model][MODEL] scores the [canon][CANON] alone at least as well as the [canon][CANON] with its [companion][COMPANION] +- **THEN** the author removes the [companion][COMPANION] [INSTRUCTION]: ../../../../specs/glossary/instruction.md [HARNESS]: ../../../../specs/glossary/harness.md diff --git a/docs/changes/plain-text-first-principles/specs/lint-enforced-foundation-requirements/spec.md b/docs/changes/plain-text-first-principles/specs/lint-enforced-foundation-requirements/spec.md index ee707211..9c5bdad4 100644 --- a/docs/changes/plain-text-first-principles/specs/lint-enforced-foundation-requirements/spec.md +++ b/docs/changes/plain-text-first-principles/specs/lint-enforced-foundation-requirements/spec.md @@ -18,27 +18,27 @@ An enforcing check is one run of an *[analysis tool][ANALYSISTOOL]* from the sha ### Requirement: Analysis Tool Has Fixtures Every [analysis tool][ANALYSISTOOL] the *[repository][REPOSITORY]* depends on MUST have one *[fixture][FIXTURE]* it accepts and one it rejects, with the expected result asserted. A change to an *[analysis rule][ANALYSISRULE]* that alters the asserted result fails the test. -Where fixtures are files, the directory name states the assertion: one directory holds the accepted fixtures and one the rejected ones. +Where [fixtures][FIXTURE] are files, the directory name states the assertion: one directory holds the accepted [fixtures][FIXTURE] and one the rejected ones. #### Scenario: Analysis tool arrives without fixtures -- **WHEN** an [analysis tool][ANALYSISTOOL] is added to the repository without a passing and a failing [fixture][FIXTURE] -- **THEN** the [coverage][COVERAGE] table lists the tool as a [missed requirement][MISSEDREQUIREMENT] until the fixtures exist +- **WHEN** an [analysis tool][ANALYSISTOOL] is added to the [repository][REPOSITORY] without a passing and a failing [fixture][FIXTURE] +- **THEN** the [coverage][COVERAGE] table lists the tool as a [missed requirement][MISSEDREQUIREMENT] until the [fixtures][FIXTURE] exist #### Scenario: Analysis rule is weakened - **WHEN** an [analysis rule][ANALYSISRULE] a tool enforced is removed -- **THEN** the failing [fixture][FIXTURE] passes, and the fixture test fails +- **THEN** the failing [fixture][FIXTURE] passes, and the [fixture][FIXTURE] test fails ### Requirement: Record Cites Its Proofs -An accepted decision record MUST list the *[proofs][PROOF]* of the scenarios under the requirements it points at. A record whose scenarios have no proof MUST stay proposed. -The `record-proof-per-scenario` capability says how a proof is recorded and replayed. This requirement only asks that the record cites it. +An accepted decision record MUST list the *[proofs][PROOF]* of the scenarios under the requirements it points at. A record whose scenarios have no [proof][PROOF] MUST stay proposed. +The `record-proof-per-scenario` capability says how a [proof][PROOF] is recorded and replayed. This requirement only asks that the record cites it. #### Scenario: Record is accepted without proof - **WHEN** a record's status changes to accepted and one scenario under a requirement it points at has no [proof][PROOF] -- **THEN** the check refuses the status and lists the scenarios without a proof +- **THEN** the check refuses the status and lists the scenarios without a [proof][PROOF] [COVERAGE]: ../../../../specs/glossary/coverage.md [MISSEDREQUIREMENT]: ../../../../specs/glossary/missed-requirement.md diff --git a/docs/changes/plain-text-first-principles/specs/markdown-relative-links-matching/spec.md b/docs/changes/plain-text-first-principles/specs/markdown-relative-links-matching/spec.md index 0eb390d1..a53d20cf 100644 --- a/docs/changes/plain-text-first-principles/specs/markdown-relative-links-matching/spec.md +++ b/docs/changes/plain-text-first-principles/specs/markdown-relative-links-matching/spec.md @@ -24,7 +24,7 @@ A *[state file][STATEFILE]* includes another file with `![text](relative path)`, ### Requirement: Link Targets Resolve Every internal link in a [repository][REPOSITORY] file MUST resolve. A relative link points at a file, a fragment at a heading in that file, and a term link at a generated glossary page. The [validator][VALIDATOR] checks every link offline, excludes nothing, and only reports. -The *[fixer][FIXER]* repairs a link to a term the ontology lacks by adding a draft entry. A commit hook may run the fixer before the validator. An external link never blocks a commit. +The *[fixer][FIXER]* repairs a link to a term the ontology lacks by adding a draft entry. A commit hook may run the [fixer][FIXER] before the [validator][VALIDATOR]. An external link never blocks a commit. #### Scenario: Link target moves diff --git a/docs/changes/plain-text-first-principles/specs/markdown-with-yaml-frontmatter/spec.md b/docs/changes/plain-text-first-principles/specs/markdown-with-yaml-frontmatter/spec.md index c496ed98..535b62bc 100644 --- a/docs/changes/plain-text-first-principles/specs/markdown-with-yaml-frontmatter/spec.md +++ b/docs/changes/plain-text-first-principles/specs/markdown-with-yaml-frontmatter/spec.md @@ -3,7 +3,7 @@ ### Requirement: Markdown System Language Every *[instruction][INSTRUCTION]* MUST be Markdown with YAML frontmatter, in the dialect the *[repository][REPOSITORY]* accepts: GitHub Flavored Markdown [GFM] plus one YAML frontmatter block. Wikilinks, callouts, and raw HTML blocks are outside the dialect. -Markdown is our plain text format of choice, because language models are trained on it. One file serves a git or jj repository, a vault such as Obsidian, and a *[harness][HARNESS]*. +Markdown is our plain text format of choice, because language *[models][MODEL]* are trained on it. One file serves a git or jj [repository][REPOSITORY], a vault such as Obsidian, and a *[harness][HARNESS]*. #### Scenario: Instruction arrives in another format @@ -23,17 +23,17 @@ The one exception is the *[provenance record][PROVENANCERECORD]*, sealed in a *[ #### Scenario: Provenance evidence arrives - **WHEN** an adoption or an assembly produces a [provenance record][PROVENANCERECORD] for an *[artifact][ARTIFACT]* -- **THEN** the *[assembler][ASSEMBLER]* seals the record in a [sidecar][SIDECAR] beside the artifact, and the artifact's frontmatter is untouched +- **THEN** the *[assembler][ASSEMBLER]* seals the record in a [sidecar][SIDECAR] beside the [artifact][ARTIFACT], and the [artifact][ARTIFACT]'s frontmatter is untouched #### Scenario: Derived index disagrees with frontmatter -- **WHEN** a derived store, such as the deployment manifest that records the fingerprints of assembled instances, disagrees with an [artifact][ARTIFACT]'s frontmatter on one field +- **WHEN** a derived store, such as the deployment manifest that records the fingerprints of assembled *[instances][INSTANCE]*, disagrees with an [artifact][ARTIFACT]'s frontmatter on one field - **THEN** frontmatter wins, and the *[generator][GENERATOR]* regenerates the store #### Scenario: Harness field arrives - **WHEN** a field only one [harness][HARNESS] reads, such as an argument hint or an allowed-tools list, is added for a [state file][STATEFILE] -- **THEN** it goes into that harness's [basis parameters][BASISPARAMETERS] beside the state file. That input tells the [assembler][ASSEMBLER] how to build the *[instance][INSTANCE]* for that harness +- **THEN** it goes into that [harness][HARNESS]'s [basis parameters][BASISPARAMETERS] beside the [state file][STATEFILE]. That input tells the [assembler][ASSEMBLER] how to build the [instance][INSTANCE] for that [harness][HARNESS] ### Requirement: Reference Citations @@ -47,8 +47,8 @@ A bare number tells the reader nothing at the point of use. Inserting a source r ### Requirement: Defined Terms Cite the Ontology -A term with a special meaning MUST be marked once per file, at its first use, by an emphasized link to its glossary page. Later uses are plain words, and a later link stays plain. Plain italics mean emphasis only. -The [assembler][ASSEMBLER] strips the links from each [instance][INSTANCE] and adds a definition block for only the terms it uses. An always-on rule tells every *[model][MODEL]* that the [repository][REPOSITORY] has an ontology. +A term with a special meaning MUST be marked at every use by a link to its glossary page. The first use in a file is an emphasized link, so a reader notices a new term, and every later use is a plain link. Plain italics mean emphasis only. +The [assembler][ASSEMBLER] strips the links from each [instance][INSTANCE] and adds a definition block for only the terms it uses. An always-on rule tells every [model][MODEL] that the [repository][REPOSITORY] has an ontology. #### Scenario: First use is not emphasized @@ -58,22 +58,25 @@ The [assembler][ASSEMBLER] strips the links from each [instance][INSTANCE] and a #### Scenario: Assembler builds an instance with term links - **WHEN** the [assembler][ASSEMBLER] builds an [instance][INSTANCE] from a [state file][STATEFILE] that links terms -- **THEN** the instance contains no term links and no reference definition block. A short block built from the ontology defines only the terms the instance uses +- **THEN** the [instance][INSTANCE] contains no term links and no reference definition block. A short block built from the ontology defines only the terms the [instance][INSTANCE] uses #### Scenario: Model meets a word with no entry - **WHEN** a [model][MODEL] is about to use or introduce a word that seems to have a special meaning -- **THEN** the always-on rule has the model look the word up in the ontology first. The model asks the user when the ontology has no entry for it +- **THEN** the always-on rule has the [model][MODEL] look the word up in the ontology first. The [model][MODEL] asks the user when the ontology has no entry for it ### Requirement: The Ontology Defines Every Term The ontology MUST be the only definition of a term. Before writing, the author reads the glossary and uses an existing term, or adds the term to the ontology first. -A term link whose tag resolves to no ontology entry is a finding. While a *[change][CHANGE]* is active, the *[fixer][FIXER]* adds a draft entry for the term and the [validator][VALIDATOR] warns on every link to a draft. The *[archiver][ARCHIVER]* refuses to archive a change that links to a draft term still without a definition. #### Scenario: Term link points at no entry - **WHEN** a term link's tag resolves to no entry in the ontology -- **THEN** the [validator][VALIDATOR] reports the term and the file, and the [fixer][FIXER] adds a draft entry, run by the author or by the commit hook +- **THEN** the [validator][VALIDATOR] reports the term and the file, and the *[fixer][FIXER]* adds a draft entry, run by the author or by the commit hook + +### Requirement: Draft Terms Warn And Archive Refuses Them + +A term link whose tag resolves to no ontology entry is a finding. While a *[change][CHANGE]* is active, the [fixer][FIXER] adds a draft entry for the term and the [validator][VALIDATOR] MUST warn on every link to a draft. The *[archiver][ARCHIVER]* MUST refuse to archive a [change][CHANGE] that links to a draft term still without a definition. #### Scenario: Change links to a draft term at archive @@ -83,11 +86,11 @@ A term link whose tag resolves to no ontology entry is a finding. While a *[chan ### Requirement: Glossary Renders From The Ontology The glossary MUST be a *[render][RENDER]* of the ontology: one generated page per term and an index. A vault keeps one note per concept in the same way, and term links point at those pages. -The [validator][VALIDATOR] renders every page in memory and compares the bytes with the committed page, so a hand edit is drift. People edit only the ontology, and the [generator][GENERATOR] writes the pages, one hop from every link. +The [validator][VALIDATOR] [renders][RENDER] every page in memory and compares the bytes with the committed page, so a hand edit is drift. People edit only the ontology, and the [generator][GENERATOR] writes the pages, one hop from every link. #### Scenario: Glossary page drifts from the ontology -- **WHEN** a committed glossary page differs by one byte from a fresh render of the ontology +- **WHEN** a committed glossary page differs by one byte from a fresh [render][RENDER] of the ontology - **THEN** the [validator][VALIDATOR] reports the page, and the ontology is what counts [INSTRUCTION]: ../../../../specs/glossary/instruction.md diff --git a/docs/changes/plain-text-first-principles/specs/open-knowledge-format-bundle/spec.md b/docs/changes/plain-text-first-principles/specs/open-knowledge-format-bundle/spec.md index c053dfe8..aa8a429f 100644 --- a/docs/changes/plain-text-first-principles/specs/open-knowledge-format-bundle/spec.md +++ b/docs/changes/plain-text-first-principles/specs/open-knowledge-format-bundle/spec.md @@ -17,8 +17,8 @@ The graph export fills the type in. A strict outside reader of the Open Knowledg ### Requirement: Path Is The Identifier -A file's identifier MUST be its web address: the *[repository][REPOSITORY]*'s base address plus the file's path inside the repository without the `.md` suffix. The Open Knowledge Format and a vault derive it the same way. -The repository adds one rule of its own: an `id` key, when present, MUST equal that identifier. Moving a file rewrites every link to it in the same commit, and the [validator][VALIDATOR] reports any link missed. +A file's identifier MUST be its web address: the *[repository][REPOSITORY]*'s base address plus the file's path inside the [repository][REPOSITORY] without the `.md` suffix. The Open Knowledge Format and a vault derive it the same way. +The [repository][REPOSITORY] adds one rule of its own: an `id` key, when present, MUST equal that identifier. Moving a file rewrites every link to it in the same commit, and the [validator][VALIDATOR] reports any link missed. #### Scenario: Identifier key appears @@ -48,8 +48,8 @@ Only the root index has the version line. The [generator][GENERATOR] writes the ### Requirement: Bundle Declares Its Version The root `index.md` of a [repository][REPOSITORY] read as a bundle MUST declare `okf_version: "0.2"`. The [generator][GENERATOR] writes the declaration into the root index, and the [validator][VALIDATOR] checks it. -The Open Knowledge Format makes the declaration optional. We require it, so a consumer that reads the repository as a bundle finds the version there. -The check is a warning until the generator exists, and an error after. +The Open Knowledge Format makes the declaration optional. We require it, so a consumer that reads the [repository][REPOSITORY] as a bundle finds the version there. +The check is a warning until the [generator][GENERATOR] exists, and an error after. #### Scenario: Version declaration is absent diff --git a/docs/changes/plain-text-first-principles/specs/plain-text-is-futureproof/spec.md b/docs/changes/plain-text-first-principles/specs/plain-text-is-futureproof/spec.md index 964b5a30..ed6f4471 100644 --- a/docs/changes/plain-text-first-principles/specs/plain-text-is-futureproof/spec.md +++ b/docs/changes/plain-text-first-principles/specs/plain-text-is-futureproof/spec.md @@ -34,18 +34,18 @@ A [model][MODEL] cannot use an image, a font, or an archive without that descrip ### Requirement: Lossless Renders -A committed *[render][RENDER]* of a *[state file][STATEFILE]* MUST regenerate from that file byte for byte. The *[generator][GENERATOR]* that writes the render is kept in the [repository][REPOSITORY], so the [validator][VALIDATOR] can rerun it and compare. A render nobody can rerun is a second home for the same facts, and the two drift apart. -An assembled *[instance][INSTANCE]* is not a render: instructions-stay-vendor-neutral covers it. +A committed *[render][RENDER]* of a *[state file][STATEFILE]* MUST regenerate from that file byte for byte. The *[generator][GENERATOR]* that writes the [render][RENDER] is kept in the [repository][REPOSITORY], so the [validator][VALIDATOR] can rerun it and compare. A [render][RENDER] nobody can rerun is a second home for the same facts, and the two drift apart. +An assembled *[instance][INSTANCE]* is not a [render][RENDER]: instructions-stay-vendor-neutral covers it. #### Scenario: Render drifts from its state file - **WHEN** a committed [render][RENDER] differs by one byte from a fresh regeneration from its [state file][STATEFILE] -- **THEN** the [validator][VALIDATOR] reports the drift, and the state file is what counts +- **THEN** the [validator][VALIDATOR] reports the drift, and the [state file][STATEFILE] is what counts ### Requirement: Projections Are Never Authoritative -A *[projection][PROJECTION]* is a representation that drops information from its *[source state][SOURCESTATE]*. A projection operator in quantum mechanics does the same: it keeps one component of a state and discards the rest. -A projection MUST NOT replace the source state it was taken from. A search index is never committed. A snapshot of something outside the [repository][REPOSITORY] is committed only with a *[provenance record][PROVENANCERECORD]* that says where it came from. +A *[projection][PROJECTION]* is a [representation][REPRESENTATION] that drops information from its *[source state][SOURCESTATE]*. +A [projection][PROJECTION] MUST NOT replace the [source state][SOURCESTATE] it was taken from. A search index is never committed. A snapshot of something outside the [repository][REPOSITORY] is committed only with a *[provenance record][PROVENANCERECORD]* that says where it came from. #### Scenario: Committed file is generated from a projection diff --git a/docs/changes/plain-text-first-principles/specs/single-source-of-truth/spec.md b/docs/changes/plain-text-first-principles/specs/single-source-of-truth/spec.md index 8e4d6843..da008f59 100644 --- a/docs/changes/plain-text-first-principles/specs/single-source-of-truth/spec.md +++ b/docs/changes/plain-text-first-principles/specs/single-source-of-truth/spec.md @@ -14,7 +14,7 @@ Anything derived MUST regenerate from its *[source state][SOURCESTATE]* on deman #### Scenario: Derived file has no generator - **WHEN** a committed file is derived and no [generator][GENERATOR] in the *[repository][REPOSITORY]* regenerates it from its [source state][SOURCESTATE] -- **THEN** the [validator][VALIDATOR] reports the file, and the author deletes the file or writes its generator +- **THEN** the [validator][VALIDATOR] reports the file, and the author deletes the file or writes its [generator][GENERATOR] - **AND** the root index, the directory indexes, the glossary pages, and the changelog each have one ### Requirement: One Home Per Fact diff --git a/docs/changes/plain-text-first-principles/specs/user-values-and-variables/spec.md b/docs/changes/plain-text-first-principles/specs/user-values-and-variables/spec.md index 82026b79..28d2bc80 100644 --- a/docs/changes/plain-text-first-principles/specs/user-values-and-variables/spec.md +++ b/docs/changes/plain-text-first-principles/specs/user-values-and-variables/spec.md @@ -2,40 +2,46 @@ ### Requirement: Variables Are $NAME -A shared *[instruction][INSTRUCTION]* MUST NOT contain a value that belongs to one user or one machine. Personal paths and account names leak into instructions easily. -It MUST refer to the value as a shell variable, `$NAME`, or `${NAME}` when text follows the name directly, and no *[harness][HARNESS]* syntax is needed. A name ends at the first character that is not a letter, a digit, or an underscore. The shell default form, `${NAME:-fallback}`, supplies a value when nothing sets the variable. +A shared *[instruction][INSTRUCTION]* MUST NOT contain a value that belongs to one user or one machine. Personal paths and account names leak into [instructions][INSTRUCTION] easily. #### Scenario: Instruction embeds a configurable value - **WHEN** an [instruction][INSTRUCTION] contains a hardcoded path, account, or host that belongs to one user or machine - **THEN** the *[validator][VALIDATOR]* reports the value, and the author replaces it with a variable reference and explains the variable in one sentence +### Requirement: Both Shell Forms Resolve In The Shell + +It MUST refer to the value as a shell variable, `$NAME`, or `${NAME}` when text follows the name directly, and no *[harness][HARNESS]* syntax is needed. A name ends at the first character that is not a letter, a digit, or an underscore. The shell default form, `${NAME:-fallback}`, supplies a value when nothing sets the variable. + #### Scenario: Assembler meets a user variable - **WHEN** the *[assembler][ASSEMBLER]* builds an *[instance][INSTANCE]* from a *[state file][STATEFILE]* that refers to `$NAME` -- **THEN** the instance keeps the reference as written, because the assembler substitutes no user variable -- **AND** the value resolves in the shell when the *[model][MODEL]* runs a command, because every harness has a shell +- **THEN** the [instance][INSTANCE] keeps the reference as written, because the [assembler][ASSEMBLER] substitutes no user variable +- **AND** the value resolves in the shell when the *[model][MODEL]* runs a command, because every [harness][HARNESS] has a shell ### Requirement: Canon Stays Vendor-Neutral -Some things only one [harness][HARNESS] understands: its own placeholders, such as Claude Code's `$ARGUMENTS`, and wording or mechanisms only that harness needs. These MUST be in that harness's *[basis parameters][BASISPARAMETERS]* beside the *[canon][CANON]*, and the canon MUST NOT contain them. -The *[source state][SOURCESTATE]* is the superposition of the canon and its basis parameters, and the [assembler][ASSEMBLER] projects out one harness. The canon alone stays readable and neutral. +Some things only one [harness][HARNESS] understands: its own placeholders, such as Claude Code's `$ARGUMENTS`, and wording or mechanisms only that [harness][HARNESS] needs. These MUST be in that [harness][HARNESS]'s *[basis parameters][BASISPARAMETERS]* beside the *[canon][CANON]*, and the [canon][CANON] MUST NOT contain them. #### Scenario: Harness placeholder enters the canon - **WHEN** a [canon][CANON] uses a placeholder form that one [harness][HARNESS] documents and the others do not -- **THEN** the [validator][VALIDATOR] reports the line, and the author moves it into that harness's [basis parameters][BASISPARAMETERS] and keeps the `$NAME` form in the canon +- **THEN** the [validator][VALIDATOR] reports the line, and the author moves it into that [harness][HARNESS]'s [basis parameters][BASISPARAMETERS] and keeps the `$NAME` form in the [canon][CANON] #### Scenario: Harness needs different wording - **WHEN** one [harness][HARNESS] needs wording the others do not -- **THEN** the wording goes into that harness's [basis parameters][BASISPARAMETERS], and the [canon][CANON] does not change -- **AND** the basis parameters file names its harness and adds to the canon without replacing it +- **THEN** the wording goes into that [harness][HARNESS]'s [basis parameters][BASISPARAMETERS], and the [canon][CANON] does not change +- **AND** the [basis parameters][BASISPARAMETERS] file names its [harness][HARNESS] and adds to the [canon][CANON] without replacing it + +### Requirement: Assembly Projects Out One Harness + +The *[source state][SOURCESTATE]* is the superposition of the [canon][CANON] and its [basis parameters][BASISPARAMETERS], and the [assembler][ASSEMBLER] MUST project out one [harness][HARNESS] from it. The [canon][CANON] alone MUST stay readable and neutral. #### Scenario: Instruction depends on one harness's hook event - **WHEN** an [instruction][INSTRUCTION] depends on a mechanism that only one [harness][HARNESS] has -- **THEN** the dependency moves into that harness's [basis parameters][BASISPARAMETERS], and the [canon][CANON] works without it +- **THEN** the dependency moves into that [harness][HARNESS]'s [basis parameters][BASISPARAMETERS], and the [canon][CANON] works without it [INSTRUCTION]: ../../../../specs/glossary/instruction.md [HARNESS]: ../../../../specs/glossary/harness.md diff --git a/docs/proofs/plain-text-first-principles/README.md b/docs/proofs/plain-text-first-principles/README.md index 1edc8ab4..6fe12cb7 100644 --- a/docs/proofs/plain-text-first-principles/README.md +++ b/docs/proofs/plain-text-first-principles/README.md @@ -20,7 +20,7 @@ scenes: kind: check - scenario: markdown-with-yaml-frontmatter#the-ontology-defines-every-term/term-link-points-at-no-entry kind: unproven - - scenario: markdown-with-yaml-frontmatter#the-ontology-defines-every-term/change-links-to-a-draft-term-at-archive + - scenario: markdown-with-yaml-frontmatter#draft-terms-warn-and-archive-refuses-them/change-links-to-a-draft-term-at-archive kind: unproven - scenario: markdown-relative-links-matching#markdown-links-between-files/wikilink-enters-a-repository-file kind: check @@ -52,9 +52,9 @@ scenes: kind: unproven - scenario: user-values-and-variables#canon-stays-vendor-neutral/harness-needs-different-wording kind: run - - scenario: user-values-and-variables#canon-stays-vendor-neutral/instruction-depends-on-one-harness-s-hook-event + - scenario: user-values-and-variables#assembly-projects-out-one-harness/instruction-depends-on-one-harness-s-hook-event kind: unproven - - scenario: instructions-stay-vendor-neutral#one-instance-per-harness/assembly-writes-an-instance + - scenario: instructions-stay-vendor-neutral#every-instance-has-a-provenance-record/assembly-writes-an-instance kind: run - scenario: instructions-stay-vendor-neutral#one-instance-per-harness/new-harness-joins kind: run diff --git a/docs/specs/glossary/agentic-review-lane.md b/docs/specs/glossary/agentic-review-lane.md new file mode 100644 index 00000000..75695953 --- /dev/null +++ b/docs/specs/glossary/agentic-review-lane.md @@ -0,0 +1,12 @@ +--- +title: agentic review lane +iri: https://runedeck.ai/ns#AgenticReviewLane +kind: class +status: defined +--- + +# agentic review lane + +One automated reviewer of a pull request: one harness and model that reports one check run. A free lane runs on every push. The paid lane runs when the review controller admits a head, or when the owner asks for it by label. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/commit-trailer.md b/docs/specs/glossary/commit-trailer.md new file mode 100644 index 00000000..9f9d51d7 --- /dev/null +++ b/docs/specs/glossary/commit-trailer.md @@ -0,0 +1,12 @@ +--- +title: commit trailer +iri: https://runedeck.ai/ns#CommitTrailer +kind: class +status: defined +--- + +# commit trailer + +A key and value line in the last paragraph of a commit message, as git reads it. A co-author trailer names a model contributor. A resolves-thread trailer names the review thread a fix answers. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/index.md b/docs/specs/glossary/index.md index 4a791ec7..acd6f139 100644 --- a/docs/specs/glossary/index.md +++ b/docs/specs/glossary/index.md @@ -5,6 +5,7 @@ Generated from `ontology/rune.ttl`. Edit the ontology, not these pages. - [Occam's razor](occams-razor.md): Of two instructions that pass the same check, the shorter one wins. - [accountable](accountable.md): On a record: who approves the decision, the A of RACI. - [agent](agent.md): The document that defines a persona and its tools for a harness to run. The running process is a prov:SoftwareAgent. +- [agentic review lane](agentic-review-lane.md): One automated reviewer of a pull request: one harness and model that reports one check run. A free lane runs on every push. The paid lane runs when the review controller admits a head, or when the owner asks for it by label. - [aliases](aliases.md): On every artifact: other names it is known by, an Obsidian default. - [analysis baseline](analysis-baseline.md): The set of results one run of analysis tools produced on a set of artifacts, kept so a later run is compared against it. - [analysis result](analysis-result.md): One condition an analysis tool reports in an artifact, with the rule it violates and the location. @@ -24,6 +25,7 @@ Generated from `ontology/rune.ttl`. Edit the ontology, not these pages. - [capability](capability.md): One specification: canonical under docs/specs/, or a delta inside a change. - [change](change.md): One spec-driven change under docs/changes/, active or archived. - [change](change-key.md): On a record: the change that produced it. +- [commit trailer](commit-trailer.md): A key and value line in the last paragraph of a commit message, as git reads it. A co-author trailer names a model contributor. A resolves-thread trailer names the review thread a fix answers. - [companion](companion.md): A document beside a canon that the canon routes to: reference material, or the instructions and quirks of one harness, kept out of the canon so they are easy to review and to compile. - [consulted](consulted.md): On a record: whose input was used, people and models as model-id@harness, MADR's consulted. - [coverage](coverage.md): The binding of each requirement to the analysis tool that enforces it, or its listing as a missed requirement. @@ -50,16 +52,20 @@ Generated from `ontology/rune.ttl`. Edit the ontology, not these pages. - [informed](informed.md): On a record: who is told the outcome, MADR's informed. - [instance](instance.md): The source state projected onto one harness's basis: the file assembly writes into a target for one harness and model. The deployment manifest fingerprints it and its provenance record seals it. One concrete realization of its rune, never authoritative. - [instruction](instruction.md): A Markdown file a harness loads to steer a model: a skill, a rule, an agent, or a routine. +- [lane table](lane-table.md): The list of expected review lanes in the repository: each lane's bot login and whether it is free or paid. The template carries it, a consumer may edit its copy, and the review controller falls back to its own copy when a consumer has none. - [lifecycle](lifecycle.md): The path a change takes: an optional canvas, then the specification, the decision record, the implementation, the review, and the merge. - [lifecycle stage](lifecycle-stage.md): One step of the lifecycle, in order: canvas, specification, decision, implementation, review, merge. - [linter](linter.md): An analysis tool that matches rule patterns against a file, one file at a time, and reports style and convention violations. - [markdown architecture decision record](markdown-architecture-decision-record.md): An architecture decision record in Markdown with the MADR template: decision drivers, considered options, their pros and cons, and the outcome. +- [merge base](merge-base.md): The most recent commit that a pull request head and its base branch both descend from. The commits a pull request introduces are the range from that commit to the head. +- [merge seal](merge-seal.md): An empty commit above the reviewed commit that the owner signs with their key. It keeps its parent's tree and names the scorecard's generation and digest. It proves the owner authorized this exact reviewed head to merge, and a later push or a rebuilt scorecard unseals it. - [missed requirement](missed-requirement.md): A requirement no analysis tool checks yet, listed in the coverage as such. - [model](model.md): The language model a harness runs. - [module](module.md): One domain of a deck that keeps its instructions, its defaults, and its checks together behind a small interface, so it can be added, replaced, or removed without a change elsewhere. - [name](name.md): On a rune canon: its name, which the Agent Skills specification requires beside description. +- [open seal](open-seal.md): An empty commit beneath a pull request head that the owner signs with their key. Its message binds the repository, the base, the head tree, and a single-use nonce, and the ready flip writes the nonce into the body. It proves the owner put their name on this pull request. - [project](project.md): On a record: the project it belongs to, a DOAP project such as this repository. -- [projection](projection.md): A representation that drops information from its source state and is never authoritative. A search index stays uncommitted, and a snapshot of a state outside the repository is committed only with a provenance record that names its upstream. +- [projection](projection.md): A representation that drops information from its source state and is never authoritative. A search index stays uncommitted, and a snapshot of a state outside the repository is committed only with a provenance record that names its upstream. A projection operator in quantum mechanics does the same: it keeps one component of a state and discards the rest. - [proof](proof.md): Evidence that a candidate commit shows the behavior a scenario states. - [proof runner](proof-runner.md): The role that runs every scene of a proof from its fence and binds the transcript to it. - [provenance record](provenance-record.md): The sealed output of assembly, kept under .provenance/ as an in-toto statement. It says which source state, which basis parameters, which transforms, and which builder produced an instance, with the digest of each. @@ -70,7 +76,9 @@ Generated from `ontology/rune.ttl`. Edit the ontology, not these pages. - [representation](representation.md): The source state expressed in one basis. The plain-text representation is the authoritative one, and every other representation derives from it. - [requirement](requirement.md): One `### Requirement:` with MUST text, part of a capability. - [responsible](responsible.md): On a record: who does the work, the R of RACI, which MADR leaves out. -- [review scorecard](review-scorecard.md): The review bot's record of one pull request: each review thread with its disposition, the commit it reviewed, a generation counter, the verdict, and the coverage. The signer admits a merge only when the scorecard names the head and its verdict is clean. +- [review controller](review-controller.md): The fixed workflow the ceremony owns, bound to no vendor. It builds the review scorecard, runs the triage, invites the paid lane, reports the correctness check, and bounds paid spend to three rounds per work item. +- [review funnel](review-funnel.md): The ordered run of the review lanes on one pull request. The free lanes fill the review scorecard on every push. The paid adjudicator judges the range since the last verdict. The owner enters twice, at the open seal and at the merge seal. +- [review scorecard](review-scorecard.md): The review controller's record of one pull request: the reviewed commit, a generation counter, a status for every expected lane, and every review thread with its disposition. Its public record is a check run named ledger on the reviewed commit, whose first line names the artifact, its digest, the generation, and the pull request. The signer admits a merge only when the scorecard names the head and its verdict is clean. - [routine](routine.md): An instruction installed on a harness account, which runs it on a schedule. - [rule](rule.md): An always-on instruction artifact under a rules/ tree. - [rune](rune.md): An instruction that follows the rune template: one canon, optional sidecar, companions, and variants. A skill, a rule, or an agent is a rune when it does. @@ -98,3 +106,4 @@ Generated from `ontology/rune.ttl`. Edit the ontology, not these pages. - [validator](validator.md): The role that reads artifacts and reports findings, and never edits. As an analysis tool it decides whether an artifact is valid against a grammar, a schema, or a set of shapes. It says nothing about style. - [variant](variant.md): A first-class alternative of a rune for one harness, one model, or one user, resolved at assembly: user, then harness and model, then harness, then the base rune. - [verdict](verdict.md): The bench's judgment of one artifact against its baseline, from the metrics the bench measured. +- [work item](work-item.md): The change a pull request names in its body, or the pull request number when it names none. The paid review budget is keyed to it, so a replacement pull request does not reset the count. diff --git a/docs/specs/glossary/lane-table.md b/docs/specs/glossary/lane-table.md new file mode 100644 index 00000000..09d23345 --- /dev/null +++ b/docs/specs/glossary/lane-table.md @@ -0,0 +1,12 @@ +--- +title: lane table +iri: https://runedeck.ai/ns#LaneTable +kind: class +status: defined +--- + +# lane table + +The list of expected review lanes in the repository: each lane's bot login and whether it is free or paid. The template carries it, a consumer may edit its copy, and the review controller falls back to its own copy when a consumer has none. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/merge-base.md b/docs/specs/glossary/merge-base.md new file mode 100644 index 00000000..07c87584 --- /dev/null +++ b/docs/specs/glossary/merge-base.md @@ -0,0 +1,12 @@ +--- +title: merge base +iri: https://runedeck.ai/ns#MergeBase +kind: class +status: defined +--- + +# merge base + +The most recent commit that a pull request head and its base branch both descend from. The commits a pull request introduces are the range from that commit to the head. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/merge-seal.md b/docs/specs/glossary/merge-seal.md new file mode 100644 index 00000000..73ad59eb --- /dev/null +++ b/docs/specs/glossary/merge-seal.md @@ -0,0 +1,12 @@ +--- +title: merge seal +iri: https://runedeck.ai/ns#MergeSeal +kind: class +status: defined +--- + +# merge seal + +An empty commit above the reviewed commit that the owner signs with their key. It keeps its parent's tree and names the scorecard's generation and digest. It proves the owner authorized this exact reviewed head to merge, and a later push or a rebuilt scorecard unseals it. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/open-seal.md b/docs/specs/glossary/open-seal.md new file mode 100644 index 00000000..5161f611 --- /dev/null +++ b/docs/specs/glossary/open-seal.md @@ -0,0 +1,12 @@ +--- +title: open seal +iri: https://runedeck.ai/ns#OpenSeal +kind: class +status: defined +--- + +# open seal + +An empty commit beneath a pull request head that the owner signs with their key. Its message binds the repository, the base, the head tree, and a single-use nonce, and the ready flip writes the nonce into the body. It proves the owner put their name on this pull request. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/projection.md b/docs/specs/glossary/projection.md index 2f83c9cb..e4181d0c 100644 --- a/docs/specs/glossary/projection.md +++ b/docs/specs/glossary/projection.md @@ -7,6 +7,6 @@ status: defined # projection -A representation that drops information from its source state and is never authoritative. A search index stays uncommitted, and a snapshot of a state outside the repository is committed only with a provenance record that names its upstream. +A representation that drops information from its source state and is never authoritative. A search index stays uncommitted, and a snapshot of a state outside the repository is committed only with a provenance record that names its upstream. A projection operator in quantum mechanics does the same: it keeps one component of a state and discards the rest. Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/review-controller.md b/docs/specs/glossary/review-controller.md new file mode 100644 index 00000000..ce0b25bf --- /dev/null +++ b/docs/specs/glossary/review-controller.md @@ -0,0 +1,12 @@ +--- +title: review controller +iri: https://runedeck.ai/ns#ReviewController +kind: class +status: defined +--- + +# review controller + +The fixed workflow the ceremony owns, bound to no vendor. It builds the review scorecard, runs the triage, invites the paid lane, reports the correctness check, and bounds paid spend to three rounds per work item. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/review-funnel.md b/docs/specs/glossary/review-funnel.md new file mode 100644 index 00000000..b165ae9d --- /dev/null +++ b/docs/specs/glossary/review-funnel.md @@ -0,0 +1,12 @@ +--- +title: review funnel +iri: https://runedeck.ai/ns#ReviewFunnel +kind: class +status: defined +--- + +# review funnel + +The ordered run of the review lanes on one pull request. The free lanes fill the review scorecard on every push. The paid adjudicator judges the range since the last verdict. The owner enters twice, at the open seal and at the merge seal. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/review-scorecard.md b/docs/specs/glossary/review-scorecard.md index a4b9f23b..0a3e53f5 100644 --- a/docs/specs/glossary/review-scorecard.md +++ b/docs/specs/glossary/review-scorecard.md @@ -7,6 +7,6 @@ status: defined # review scorecard -The review bot's record of one pull request: each review thread with its disposition, the commit it reviewed, a generation counter, the verdict, and the coverage. The signer admits a merge only when the scorecard names the head and its verdict is clean. +The review controller's record of one pull request: the reviewed commit, a generation counter, a status for every expected lane, and every review thread with its disposition. Its public record is a check run named ledger on the reviewed commit, whose first line names the artifact, its digest, the generation, and the pull request. The signer admits a merge only when the scorecard names the head and its verdict is clean. Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/docs/specs/glossary/work-item.md b/docs/specs/glossary/work-item.md new file mode 100644 index 00000000..b47a0189 --- /dev/null +++ b/docs/specs/glossary/work-item.md @@ -0,0 +1,12 @@ +--- +title: work item +iri: https://runedeck.ai/ns#WorkItem +kind: class +status: defined +--- + +# work item + +The change a pull request names in its body, or the pull request number when it names none. The paid review budget is keyed to it, so a replacement pull request does not reset the count. + +Generated from `ontology/rune.ttl`. Edit the ontology, not this page. diff --git a/ontology/rune.ttl b/ontology/rune.ttl index ea40b906..0984558e 100644 --- a/ontology/rune.ttl +++ b/ontology/rune.ttl @@ -105,7 +105,7 @@ rune:Render a rdfs:Class ; rune:Projection a rdfs:Class ; rdfs:subClassOf rune:Representation ; rdfs:label "projection" ; - rdfs:comment "A representation that drops information from its source state and is never authoritative. A search index stays uncommitted, and a snapshot of a state outside the repository is committed only with a provenance record that names its upstream." . + rdfs:comment "A representation that drops information from its source state and is never authoritative. A search index stays uncommitted, and a snapshot of a state outside the repository is committed only with a provenance record that names its upstream. A projection operator in quantum mechanics does the same: it keeps one component of a state and discards the rest." . # An instance is the state seen in one harness's basis: the file assembly # writes into a target, fingerprinted in the deployment manifest and @@ -449,7 +449,54 @@ rune:Attestation a rdfs:Class ; rune:ReviewScorecard a rdfs:Class ; rdfs:subClassOf prov:Entity ; rdfs:label "review scorecard" ; - rdfs:comment "The review bot's record of one pull request: each review thread with its disposition, the commit it reviewed, a generation counter, the verdict, and the coverage. The signer admits a merge only when the scorecard names the head and its verdict is clean." . + rdfs:comment "The review controller's record of one pull request: the reviewed commit, a generation counter, a status for every expected lane, and every review thread with its disposition. Its public record is a check run named ledger on the reviewed commit, whose first line names the artifact, its digest, the generation, and the pull request. The signer admits a merge only when the scorecard names the head and its verdict is clean." . + +# The review ceremony's own vocabulary, defined here because the skeleton's +# specifications use it and the ontology is the one place a term lives. +rune:AgenticReviewLane a rdfs:Class ; + rdfs:subClassOf prov:SoftwareAgent ; + rdfs:label "agentic review lane" ; + rdfs:comment "One automated reviewer of a pull request: one harness and model that reports one check run. A free lane runs on every push. The paid lane runs when the review controller admits a head, or when the owner asks for it by label." . + +rune:ReviewFunnel a rdfs:Class ; + rdfs:subClassOf prov:Activity ; + rdfs:label "review funnel" ; + rdfs:comment "The ordered run of the review lanes on one pull request. The free lanes fill the review scorecard on every push. The paid adjudicator judges the range since the last verdict. The owner enters twice, at the open seal and at the merge seal." . + +rune:ReviewController a rdfs:Class ; + rdfs:subClassOf prov:SoftwareAgent ; + rdfs:label "review controller" ; + rdfs:comment "The fixed workflow the ceremony owns, bound to no vendor. It builds the review scorecard, runs the triage, invites the paid lane, reports the correctness check, and bounds paid spend to three rounds per work item." . + +rune:LaneTable a rdfs:Class ; + rdfs:subClassOf prov:Entity ; + rdfs:label "lane table" ; + rdfs:comment "The list of expected review lanes in the repository: each lane's bot login and whether it is free or paid. The template carries it, a consumer may edit its copy, and the review controller falls back to its own copy when a consumer has none." . + +rune:WorkItem a rdfs:Class ; + rdfs:subClassOf prov:Entity ; + rdfs:label "work item" ; + rdfs:comment "The change a pull request names in its body, or the pull request number when it names none. The paid review budget is keyed to it, so a replacement pull request does not reset the count." . + +rune:OpenSeal a rdfs:Class ; + rdfs:subClassOf prov:Entity ; + rdfs:label "open seal" ; + rdfs:comment "An empty commit beneath a pull request head that the owner signs with their key. Its message binds the repository, the base, the head tree, and a single-use nonce, and the ready flip writes the nonce into the body. It proves the owner put their name on this pull request." . + +rune:MergeSeal a rdfs:Class ; + rdfs:subClassOf prov:Entity ; + rdfs:label "merge seal" ; + rdfs:comment "An empty commit above the reviewed commit that the owner signs with their key. It keeps its parent's tree and names the scorecard's generation and digest. It proves the owner authorized this exact reviewed head to merge, and a later push or a rebuilt scorecard unseals it." . + +rune:MergeBase a rdfs:Class ; + rdfs:subClassOf prov:Entity ; + rdfs:label "merge base" ; + rdfs:comment "The most recent commit that a pull request head and its base branch both descend from. The commits a pull request introduces are the range from that commit to the head." . + +rune:CommitTrailer a rdfs:Class ; + rdfs:subClassOf prov:Entity ; + rdfs:label "commit trailer" ; + rdfs:comment "A key and value line in the last paragraph of a commit message, as git reads it. A co-author trailer names a model contributor. A resolves-thread trailer names the review thread a fix answers." . # The candidate commit a proof covers, an entity IRI under /id/. A # candidate is proven when a proof with exit status zero exists for its diff --git a/runes/core/rules/ConsultTheOntology.md b/runes/core/rules/ConsultTheOntology.md index 720cf8fb..e7e8d15b 100644 --- a/runes/core/rules/ConsultTheOntology.md +++ b/runes/core/rules/ConsultTheOntology.md @@ -1,6 +1,6 @@ The *[repository][REPOSITORY]* has an ontology, `ontology/rune.ttl`. It defines every word with a special meaning once, and a generator renders one glossary page per term under `docs/specs/glossary/`. You read the pages. People edit the ontology. Nobody edits a page. -Before you use or introduce a word that has a special meaning, look it up. Open its glossary page, or search `ontology/rune.ttl` for its label. Use the word the way its definition says. A *[projection][PROJECTION]* is a form that drops information. A *[canon][CANON]* is a rune's entrypoint file. A *[deck][DECK]* is a set of runes deployed together, and the [repository][REPOSITORY] is the container that holds decks. +Before you use or introduce a word that has a special meaning, look it up. Open its glossary page, or search `ontology/rune.ttl` for its label. Use the word the way its definition says. A *[projection][PROJECTION]* is a form that drops information. A *[canon][CANON]* is a rune's entrypoint file. A *[deck][DECK]* is a set of runes deployed together, and the [repository][REPOSITORY] is the container that holds [decks][DECK]. A word that seems to have a meaning but has no entry is a question. Do not guess. Ask the user what it means. When the user asks for it, run the *[fixer][FIXER]*, which adds a draft entry to the ontology and writes the term's page. A draft term warns while a change is active, and the *[archiver][ARCHIVER]* refuses a change that still links to one. @@ -12,7 +12,7 @@ The *[analysis tool][ANALYSISTOOL]* reads the file. A second [analysis tool][ANA [ANALYSISTOOL]: ../../../docs/specs/glossary/analysis-tool.md ``` -Later uses in the same file are plain words. A later link is allowed and stays plain. Define each tag once at the end of the file as a reference definition that points at the page by a relative path. The tag is the label in upper case with every non-letter removed. Plain italics are emphasis and mean nothing more. +Every later use in the same file is a plain link, so a copied paragraph keeps its links. The [fixer][FIXER] links a plain occurrence of a term the file links, and it cannot tell the verb `changes` from the term, the everyday `rule` from the artifact, or the `target` of a link from a harness *[target][TARGET]*. So a file that links a term uses that word in the term's sense only. For the everyday sense, choose another word: `alters`, `principle`, `destination`. A cited record title or file name is an inline link to the file, never bare text, so its words stay its own. Define each tag once at the end of the file as a reference definition that points at the page by a relative path. The tag is the label in upper case with every non-letter removed. Plain italics are emphasis and mean nothing more. Why: a model given a bare word takes its everyday meaning, and the guess reads as fact. A link puts the definition one step away and keeps one home for it. The validator reports a term that points at no entry. @@ -22,3 +22,4 @@ Why: a model given a bare word takes its everyday meaning, and the guess reads a [DECK]: ../../../docs/specs/glossary/deck.md [FIXER]: ../../../docs/specs/glossary/fixer.md [ARCHIVER]: ../../../docs/specs/glossary/archiver.md +[TARGET]: ../../../docs/specs/glossary/target.md diff --git a/runes/core/skills/ReviewChange/SKILL.md b/runes/core/skills/ReviewChange/SKILL.md index e56a345e..696e3aa2 100644 --- a/runes/core/skills/ReviewChange/SKILL.md +++ b/runes/core/skills/ReviewChange/SKILL.md @@ -7,23 +7,23 @@ metadata: # ReviewChange -The owner decides a specification one *[requirement][REQUIREMENT]* at a time. You bring the requirement, its tension, and concrete ways to change it. The owner answers in one word or in their own words. The walk ends in a readback that the implementing session works from. The owner asked on 2026-10-17 to keep this pattern as the way specifications get reviewed. +The owner decides a specification one *[requirement][REQUIREMENT]* at a time. You bring the [requirement][REQUIREMENT], its tension, and concrete ways to *[change][CHANGE]* it. The owner answers in one word or in their own words. The walk ends in a readback that the implementing session works from. The owner asked on 2026-10-17 to keep this pattern as the way specifications get reviewed. ## Prerequisites -- The *[change][CHANGE]* directory with its specifications, the `design.md` coverage table, the *[decision records][DECISIONRECORD]* it cites, and any reviewer verdicts. -- The glossary pages, so you can explain a term inside a requirement from its definition. +- The [change][CHANGE] directory with its specifications, the `design.md` coverage table, the *[decision records][DECISIONRECORD]* it cites, and any reviewer verdicts. +- The glossary pages, so you can explain a term inside a [requirement][REQUIREMENT] from its definition. - One channel, either the terminal with `AskUserQuestion` or voice through a project built from voice-safe files. ## Constraints -- One requirement at a time. Short turns. The owner's attention is the scarce resource. -- Name a requirement by its heading, never by an index letter or number, in the walk and in the readback. +- One [requirement][REQUIREMENT] at a time. Short turns. The owner's attention is the scarce resource. +- Name a [requirement][REQUIREMENT] by its heading, never by an index letter or number, in the walk and in the readback. - Numbers are words in anything spoken or read back. - Never skip past "I do not understand" or "I have no idea what you just said". Re-explain with the four-part template first, then ask again. -- Before the next requirement, discuss the owner's answers and answer the owner's questions. The owner's words: "always before you move on, I need you to discuss with me and answer my questions". -- A verdict binds to the requirement's current text. When the owner changes the text, say what the new text claims before you move on. -- When a decision changes during the walk, the readback states only the final decision and marks the reversal where it matters. +- Before the next [requirement][REQUIREMENT], discuss the owner's answers and answer the owner's questions. The owner's words: "always before you move on, I need you to discuss with me and answer my questions". +- A verdict binds to the [requirement][REQUIREMENT]'s current text. When the owner [changes][CHANGE] the text, say what the new text claims before you move on. +- When a decision [changes][CHANGE] during the walk, the readback states only the final decision and marks the reversal where it matters. ## Instructions @@ -32,14 +32,14 @@ The owner decides a specification one *[requirement][REQUIREMENT]* at a time. Yo Say three things, then stop: 1. The heading. -2. One line of context: what the requirement asks for, in everyday words. -3. The real tension, usually the gap between the requirement and the check or the record behind it. An example: "The statement says a projection never stands in for its source. The only scenario tests a snapshot without its sidecar. So the core claim has no failing case." +2. One line of context: what the [requirement][REQUIREMENT] asks for, in everyday words. +3. The real tension, usually the gap between the [requirement][REQUIREMENT] and the check or the record behind it. An example: "The statement says a projection never stands in for its source. The only scenario tests a snapshot without its sidecar. So the core claim has no failing case." -Ask for a verdict: keep, change, drop, split, or merge with another requirement. Offer concrete proposals instead of an open question: "Rename plus that new scenario, sidecar rule kept as is. Does that work?" +Ask for a verdict: keep, [change][CHANGE], drop, split, or merge with another [requirement][REQUIREMENT]. Offer concrete proposals instead of an open question: "Rename plus that new scenario, sidecar rule kept as is. Does that work?" ### Explain on request -When the owner asks what a requirement means, or says they do not understand, use the four-part template in this order and add nothing: +When the owner asks what a [requirement][REQUIREMENT] means, or says they do not understand, use the four-part template in this order and add nothing: 1. The rule in everyday words. "Do not use the full process for small stuff." 2. A concrete example. "If you fix a typo, you just commit it." @@ -50,24 +50,24 @@ Explain a concept by its effect, the failure it prevents, and one example. Its n ### Rename when the explanation is clearer -When a requirement or a term has an arcane name, rewrite it in plain words and offer both, the name and the rewrite. The owner picks. When the plain words win, the name was wrong, and the rename goes into the readback as a decision. The name `harness-neutral` became "the main file works without any vendor's extras", and the heading became Canon Stays Vendor-Neutral. +When a [requirement][REQUIREMENT] or a term has an arcane name, rewrite it in plain words and offer both, the name and the rewrite. The owner picks. When the plain words win, the name was wrong, and the rename goes into the readback as a decision. The name `harness-neutral` became "the main file works without any vendor's extras", and the heading became Canon Stays Vendor-Neutral. ### Read back as prose -At the end, or when the owner asks, read every decision back as prose. Write one paragraph per requirement, named by heading, that says what the owner decided and why, in the plain register. Rules that apply to every specification, new requirements, new changes, and ontology changes get their own sections. Open items come last. Write the readback to a dated file that the implementing session reads first. The first one was `runedeck-review-decisions-2026-10-17.md` in the workshop. +At the end, or when the owner asks, read every decision back as prose. Write one paragraph per [requirement][REQUIREMENT], named by heading, that says what the owner decided and why, in the plain register. Rules that apply to every specification, new [requirements][REQUIREMENT], new [changes][CHANGE], and ontology [changes][CHANGE] get their own sections. Open items come last. Write the readback to a dated file that the implementing session reads first. The first one was `runedeck-review-decisions-2026-10-17.md` in the workshop. ## Verification -- Every requirement in scope has one verdict in the readback, or sits under open items with the question that blocks it. +- Every [requirement][REQUIREMENT] in scope has one verdict in the readback, or sits under open items with the question that blocks it. - No item is named by a letter or a number anywhere in the walk or the readback. - Each explanation the owner asked for has all four parts. - The owner confirmed the readback, or its unconfirmed parts carry a mark. ## References -- The terminal form: `AskUserQuestion` with four requirements per call and a header such as `N/32`. Each item gets one summary line. Do not copy the requirement over. The options are `Keep` plus three concrete proposed changes, and `Other` for the owner's own words. Keep the state in a `state.json` in the workshop's spec directory so the walk resumes. Discuss after every batch before the next one. +- The terminal form: `AskUserQuestion` with four [requirements][REQUIREMENT] per call and a header such as `N/32`. Each item gets one summary line. Do not copy the [requirement][REQUIREMENT] over. The options are `Keep` plus three concrete proposed [changes][CHANGE], and `Other` for the owner's own words. Keep the state in a `state.json` in the workshop's spec directory so the walk resumes. Discuss after every batch before the next one. - The voice form: a project built from voice-safe files. Such a file has no index ids and reads as narrative under headings. It has no tables, lists, code, paths, extensions, or abbreviations. Each section is self-contained, and the file stays under twelve kilobytes. -- The SelfContainedQuestionOptions rule: describe each option's change in plain words. +- The SelfContainedQuestionOptions rule: describe each option's [change][CHANGE] in plain words. [REQUIREMENT]: ../../../../docs/specs/glossary/requirement.md [CHANGE]: ../../../../docs/specs/glossary/change.md diff --git a/scripts/check-coverage b/scripts/check-coverage index d181ef1b..679973c9 100755 --- a/scripts/check-coverage +++ b/scripts/check-coverage @@ -17,9 +17,17 @@ import pathlib import re import sys +# The term tag grammar comes from the term model, so a cell's term link +# reads the same here as in check-terms. +sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent / "fallback")) +from termlib import TAG + ROOT = pathlib.Path(__file__).resolve().parent.parent REQUIREMENT = re.compile(r"^### Requirement: (.+?)\s*$") -ROW = re.compile(r"^\|\s*([a-z0-9-]+)\s*\|\s*(.+?)\s*\|\s*(.*?)\s*\|\s*$") +ROW = re.compile(r"^\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*(.*?)\s*\|\s*$") +# A capability is a kebab-case name once its term links read as words. +CAPABILITY = re.compile(r"[a-z0-9-]+") +TERM_LINK = re.compile(r"\*?\[([^\]]+)\]\[" + TAG + r"\]\*?") TOOLS = ("rune validate", "rune spec validate", "rune spec doctor", "mdschema", "rumdl", "lychee", "vale", "shellcheck", "ruff", "openspec validate", "rudof") @@ -56,12 +64,19 @@ def rows(change): inside = line.strip().lower() == "## coverage table" continue match = ROW.match(line) if inside else None - if match and match.group(1) != "Capability" and not set(match.group(1)) <= set("-"): - table.setdefault((match.group(1), match.group(2)), match.group(3)) + capability = plain(match.group(1)) if match else "" + if CAPABILITY.fullmatch(capability) and not set(capability) <= set("-"): + table.setdefault((capability, plain(match.group(2))), match.group(3)) return table +def plain(cell): + """A cell with its term links read as words: `[requirement][REQUIREMENT]` is `requirement`.""" + return TERM_LINK.sub(r"\1", cell) + + def names_a_check(cell, root): + cell = plain(cell) if cell.lower().startswith(("missed requirement", "open gap")): return True hooks = (root / ".pre-commit-config.yaml").read_text() if (root / ".pre-commit-config.yaml").is_file() else "" diff --git a/scripts/fallback/check-terms b/scripts/fallback/check-terms index 073239f0..c2bf8a0a 100755 --- a/scripts/fallback/check-terms +++ b/scripts/fallback/check-terms @@ -6,7 +6,7 @@ A defined term is a reference link to its generated glossary page: `*[analysis tool][ANALYSISTOOL]*` at the first use in a file, -`[analysis tool][ANALYSISTOOL]` after it, and +`[analysis tool][ANALYSISTOOL]` at every later use, and `[ANALYSISTOOL]: ../../specs/glossary/analysis-tool.md` at the end of the file. Plain italics are emphasis and are not checked. The findings carry the meaning of rune's spec-term-* and delta-term-* diagnostics: @@ -14,6 +14,7 @@ the meaning of rune's spec-term-* and delta-term-* diagnostics: - term-undefined (error): the link resolves to no ontology entry. - term-draft (warning): the term carries `rune:status "draft"`; the archiver refuses the change. - term-first-use (error): the first use in a file is not emphasized, or a later use is. +- term-unlinked (error): a plain occurrence of a term the file links elsewhere; every occurrence is a link. - term-page (error): the page is missing, or its bytes differ from the render. - term-legacy (warning): the old `*term* [TAG]` form with an ontology IRI definition; run scripts/fallback/fix-terms --links. @@ -82,6 +83,14 @@ def check_file(root, path, index, rendered): report(match.start(), "error", "term-first-use", f"first use of [{label}][{tag}] is not emphasized; write `*[{label}][{tag}]*`") elif emphasized: report(match.start(), "error", "term-first-use", f"later use of [{label}][{tag}] is emphasized; write `[{label}][{tag}]`") + # Every occurrence of a term the file links is a link: a plain word + # between two links is the one a copied paragraph loses. A term counts + # as linked once the file uses a link to it, not when it only defines one. + linked = termlib.linked_terms(text, resolved, index) + for start, end, name, tag in termlib.term_occurrences(text, linked, index): + label = index.by_name[name].label + word = text[start:end] + report(start, "error", "term-unlinked", f"'{word}' is the term '{label}' and this file links it elsewhere; every occurrence links, write `[{word}][{tag}]`, or run {FIX} --links") return found diff --git a/scripts/fallback/fix-terms b/scripts/fallback/fix-terms index cd6fd689..22f4bac9 100755 --- a/scripts/fallback/fix-terms +++ b/scripts/fallback/fix-terms @@ -6,8 +6,9 @@ migrate legacy citations to term links. scripts/fallback/fix-terms --terms add a draft entry to ontology/rune.ttl for every term link that resolves to no term, and write its page scripts/fallback/fix-terms --links rewrite the legacy `*term* [TAG]` form to `*[term][TAG]*` at - the first use and `[term][TAG]` after it, and point every - `[TAG]: https://runedeck.ai/ns#Term` definition at the term's page + the first use and `[term][TAG]` after it, point every + `[TAG]: https://runedeck.ai/ns#Term` definition at the term's page, + and link every plain occurrence of a term the file links scripts/fallback/fix-terms [...] DIR fix another root, never through rune A validator only reports. This is the separate fix step, like a linter's @@ -48,10 +49,11 @@ def splice(text, edits): def redefine(root, path, text, tag, page_name): - """The text with the definition of tag pointing at the page.""" + """The text with the definition of tag pointing at the page. Only the + target changes, so a definition keeps its title.""" blanked = termlib.blank_code(text) edits = [ - (m.start(), m.end(), f"[{tag}]: {termlib.relative_link(path, root, page_name)}") + (m.start(2), m.end(2), termlib.relative_link(path, root, page_name)) for m in termlib.DEFINITION.finditer(blanked) if m.group(1) == tag ] return splice(text, edits) @@ -91,6 +93,57 @@ def fix_links(root, index): return changed, notes +def wrapping_stars(text, start, end): + """How many emphasis stars wrap text[start:end] on both sides, up to three.""" + count = 0 + while count < 3 and start - count > 0 and end + count < len(text) and text[start - count - 1] == "*" and text[end + count] == "*": + count += 1 + return count + + +def link_occurrences(root, index): + """Link every plain occurrence of a term the file already links, so a + copied paragraph keeps its links. Where two terms overlap, the longer + one wins (termlib.term_occurrences). The emphasis stays on the first + use only: a new link before the old first use takes the emphasis from + it, and an italic word that becomes a later link drops its stars. + Returns the files changed.""" + changed = [] + for path in termlib.markdown_files(root): + raw = path.read_text() + blanked = termlib.blank_code(raw) + resolved = {} + for tag, target in termlib.DEFINITION.findall(blanked): + res = termlib.resolve(root, path, tag, target, index) + if res.form == "link" and res.name is not None: + resolved[tag] = res + found = termlib.term_occurrences(blanked, termlib.linked_terms(blanked, resolved, index), index) + if not found: + continue + uses = {} + for match in termlib.REF_LINK.finditer(blanked): + if match.group(3) in resolved: + uses.setdefault(resolved[match.group(3)].name, match) + first = {} + for start, _end, name, _tag in found: + first.setdefault(name, start) + edits = [] + for start, end, name, tag in found: + link = f"[{raw[start:end]}][{tag}]" + wrap = wrapping_stars(raw, start, end) + if start == first[name] and start < uses[name].start(): + edits.append((start, end, link if wrap else f"*{link}*")) + else: + edits.append((start - wrap, end + wrap, link)) + for name, use in uses.items(): + opened, text, tag, closed = use.groups() + if first.get(name, len(raw)) < use.start() and opened and closed: + edits.append((use.start(), use.end(), f"[{text}][{tag}]")) + path.write_text(splice(raw, edits)) + changed.append(path.relative_to(root)) + return changed + + def ghosts(root, index): """(path, tag, label) for every term link whose page is absent and whose tag names no term. A page with an unknown IRI is stale, not a ghost, and @@ -157,6 +210,7 @@ def main(): index = termlib.load(root) if not flags or "--links" in flags: changed, notes = fix_links(root, index) + changed.extend(rel for rel in link_occurrences(root, index) if rel not in changed) for rel in changed: print(f"links: rewrote {rel}") for note in notes: diff --git a/scripts/fallback/termlib.py b/scripts/fallback/termlib.py index 2041eb6f..2f16cee6 100644 --- a/scripts/fallback/termlib.py +++ b/scripts/fallback/termlib.py @@ -6,7 +6,7 @@ labeled class, property, or concept gets one generated page under docs/specs/glossary/, and a Markdown file marks a term by a reference link to that page: `*[analysis tool][ANALYSISTOOL]*` at first use, then -`[analysis tool][ANALYSISTOOL]`, with `[ANALYSISTOOL]: ` at +`[analysis tool][ANALYSISTOOL]` at every later use, with `[ANALYSISTOOL]: ` at the end of the file. The rune CLI renders the same bytes; this module is the fallback for a machine without it. """ @@ -38,14 +38,29 @@ PRECEDENCE = {"rdfs:Class": 0, "rdf:Property": 1, "skos:Concept": 2} TAG = r"[A-Z][A-Z0-9-]*" -DEFINITION = re.compile(r"^\[(" + TAG + r")\]:[ \t]*(\S+)[ \t]*$", re.MULTILINE) +# A term definition: `[TAG]: target`, with an optional Markdown title in +# double quotes, single quotes, or parentheses. Two groups: tag and target. +DEFINITION = re.compile(r"^\[(" + TAG + r")\]:[ \t]*(\S+)(?:[ \t]+(?:\"[^\"\n]*\"|'[^'\n]*'|\([^()\n]*\)))?[ \t]*$", re.MULTILINE) +# Any definition line, titled or not, as rune's prose view skips it: any +# Markdown link label, so `[MORE INFO]: ./x.md` is a definition too. +DEFINITION_LINE = re.compile(r"^ {0,3}\[(?!\s*\])(?:\\.|[^\\\[\]])+\]:\s*\S") # The link form: an optional emphasis star on each side of `[text][TAG]`. REF_LINK = re.compile(r"(\*?)\[([^\]\n]+)\]\[(" + TAG + r")\](\*?)") # The legacy form: an italic term followed by a bare tag. The italic text # never starts with `[`, so a converted link is never matched again. LEGACY = re.compile(r"(?|\||#)") +# A leading YAML block closed by `---` or `...`, as rune's prose view sees it. +FRONT_MATTER = re.compile(r"\A---[ \t]*\n.*?^(?:---|\.\.\.)[ \t]*$", re.MULTILINE | re.DOTALL) SCHEME = re.compile(r"^[a-z][a-z0-9+.-]*:") Term = collections.namedtuple("Term", "label precedence name kind comment status") @@ -181,17 +196,173 @@ def markdown_files(root): return sorted(found) +def fenced_blocks(text): + """(start, end) of every fenced code block: the opener, the body, and a + closing line of the opener's character at least as long as the opener, + indented at most three spaces. A fence with no closer runs to the end.""" + found, opener, offset = [], None, 0 + for line in text.split("\n"): + body = line.rstrip("\r") + if opener is None: + match = FENCE_OPEN.match(body) + if match and not (match.group(1)[0] == "`" and "`" in match.group(2)): + opener = (match.group(1)[0], len(match.group(1)), offset) + elif re.match(r" {0,3}" + re.escape(opener[0]) + "{" + str(opener[1]) + r",}[ \t]*$", body): + found.append((opener[2], offset + len(body))) + opener = None + offset += len(line) + 1 + if opener is not None: + found.append((opener[2], len(text))) + return found + + def blank_code(text): - """Code fences and spans replaced by spaces, so offsets and lines hold.""" - def blank(match): - return re.sub(r"[^\n]", " ", match.group(0)) - return CODE_SPAN.sub(blank, CODE_FENCE.sub(blank, text)) + """Front matter, code fences, and code spans replaced by spaces, so + offsets and lines hold. Rune's prose view skips the same three.""" + def blank(chunk): + return re.sub(r"[^\n]", " ", chunk) + text = FRONT_MATTER.sub(lambda match: blank(match.group(0)), text, count=1) + out, last = [], 0 + for start, end in fenced_blocks(text): + out += [text[last:start], blank(text[start:end])] + last = end + out.append(text[last:]) + return CODE_SPAN.sub(lambda match: blank(match.group(0)), "".join(out)) def line_of(text, offset): return text.count("\n", 0, offset) + 1 +def heading_lines(lines): + """Indexes of the heading lines: a `#` line, and every line of a + paragraph that a Setext `===` or `---` underline closes. Text that + continues a list item, a quote, or a table is not a heading.""" + found = {i for i, line in enumerate(lines) if line.lstrip().startswith("#")} + for i, line in enumerate(lines): + if i == 0 or not SETEXT_UNDERLINE.match(line): + continue + top = i - 1 + while top >= 0 and lines[top].strip() and not NOT_PARAGRAPH.match(lines[top]) and not DEFINITION_LINE.match(lines[top]): + top -= 1 + if top == i - 1: + continue + if top >= 0 and lines[top].strip() and NOT_PARAGRAPH.match(lines[top]) and not lines[top].lstrip().startswith("#"): + continue + found.update(range(top + 1, i + 1)) + return found + + +def destination_end(line, start): + """The offset after the `)` that closes the inline-link destination + opening at start. Parentheses balance and a backslash escapes, as in + `[record](path(foo)x)`. An unclosed destination runs to the line end.""" + depth, i = 0, start + while i < len(line): + if line[i] == "\\": + i += 2 + continue + if line[i] == "(": + depth += 1 + elif line[i] == ")": + depth -= 1 + if depth == 0: + return i + 1 + i += 1 + return len(line) + + +def prose_mask(line): + """True for each character of a line that is not prose: a term link, a + legacy citation, text in brackets, and an inline-link destination.""" + masked = [False] * len(line) + for pattern in (REF_LINK, LEGACY): + for match in pattern.finditer(line): + masked[match.start():match.end()] = [True] * (match.end() - match.start()) + depth, i = 0, 0 + while i < len(line): + if line[i] == "[": + depth += 1 + elif line[i] == "]": + depth = max(depth - 1, 0) + if depth == 0 and line[i + 1:i + 2] == "(": + end = destination_end(line, i + 1) + masked[i:end] = [True] * (end - i) + i = end + continue + if depth > 0: + masked[i] = True + i += 1 + return masked + + +def label_pattern(label): + """A whole-word, case-insensitive pattern for a label and its plural: + `s` or `es`, and `es` alone for a label that ends in s (`harnesses`). + It matches the source text itself, so offsets hold for any letter.""" + plural = "(?:es)?" if label.lower().endswith("s") else "(?:es|s)?" + return re.compile(r"(?= kept[1] for kept in chosen): + chosen.append(cand) + return sorted(chosen) + + def page_path(root, path, target): """The glossary page a definition target names, or None when the target is not a relative path into docs/specs/glossary/.""" @@ -228,7 +399,9 @@ def relative_link(path, root, file_name): def resolve(root, path, tag, target, index): - """What a reference definition stands for, from its target and its tag.""" + """What a reference definition stands for, from its target and its tag. + A page that is not written yet resolves by its file name first, so a + link that a migration pointed at a missing page keeps its term.""" if target.startswith(NS): name = target[len(NS):] return Resolution("legacy", name if name in index.by_name else None, None) @@ -238,4 +411,4 @@ def resolve(root, path, tag, target, index): if page.is_file(): name = iri_of(page) return Resolution("link", name if name in index.by_name else None, page) - return Resolution("link", index.by_tag.get(tag), page) + return Resolution("link", index.by_file.get(page.name) or index.by_tag.get(tag), page) diff --git a/scripts/generate-vale-style.py b/scripts/generate-vale-style.py index f72ad32a..081467d0 100755 --- a/scripts/generate-vale-style.py +++ b/scripts/generate-vale-style.py @@ -206,12 +206,15 @@ def render_core() -> dict[str, str]: + "# the text counts as the definition, and italics alone are emphasis. The\n" + "# nouns are the TERMS subset of OVERUSE, so one list serves this rule and\n" + "# STE.Overused*. Raw scope, because Vale strips the markers before any\n" - + "# other scope.\n" + + "# other scope. A hyphenated name such as review-lanes is a name, not\n" + + "# the noun, in prose and in link text, and a link whose text ends in\n" + + "# the noun (agentic review lane) defines it. The first pattern matches\n" + + "# the stem alone, so a plural use counts as defined by its singular link.\n" + "extends: conditional\n" + "message: \"'%s' is never defined here. Link it to its glossary page at first use, or say which check, file, or flag it is (rule Deslop).\"\n" + "level: warning\nignorecase: true\nscope: raw\n" - + f"first: '\\b({nouns})s?\\b'\n" - + f"second: '(?:\\[(?{nouns})s?\\]\\[|[*_`](?{nouns})s?[*_`])'\n", + + f"first: '(?{nouns})s?\\]\\[|\\*\\*(?{nouns})s?\\*\\*|__(?{nouns})s?__|`(?{nouns})s?`)'\n", } diff --git a/tests/fixtures/vale/valid/undefined-term.md b/tests/fixtures/vale/valid/undefined-term.md index 5169034e..fc53ee18 100644 --- a/tests/fixtures/vale/valid/undefined-term.md +++ b/tests/fixtures/vale/valid/undefined-term.md @@ -1,3 +1,9 @@ # Note The check blocks the push. + +The `review-lanes` capability and the *[agentic review lane][AGENTICREVIEWLANE]* run. A lane again, and two lanes. + +A **gate** is the push check, and the gate runs. A `knob` is the flag, and the knob turns. + +[AGENTICREVIEWLANE]: ../../../docs/specs/glossary/agentic-review-lane.md diff --git a/tests/fixtures/vale/valid/unexplained-abbreviation.md b/tests/fixtures/vale/valid/unexplained-abbreviation.md index c5ea984e..251007de 100644 --- a/tests/fixtures/vale/valid/unexplained-abbreviation.md +++ b/tests/fixtures/vale/valid/unexplained-abbreviation.md @@ -1,3 +1,5 @@ # Note A software bill of materials (SBOM) lists every part. The SBOM is short, and the file MUST be UTF-8. An IRI (internationalized resource identifier) names each term. + +Part II and chapter IV carry roman numerals. diff --git a/tests/test_check_scripts.py b/tests/test_check_scripts.py index 42974eff..d1e6b36d 100644 --- a/tests/test_check_scripts.py +++ b/tests/test_check_scripts.py @@ -95,6 +95,21 @@ def test_missing_root_is_a_finding(self): result = run(SCRIPTS["binary-assets"], FIXTURES / "binary-assets" / "absent") self.assertEqual(result.returncode, 1) + def test_coverage_reads_term_links_in_cells(self): + """A capability or requirement cell may hold a term link with any + tag the term grammar allows, and the check reads it as its words.""" + with tempfile.TemporaryDirectory() as scratch: + change = Path(scratch) / "change" + (change / "specs" / "cap").mkdir(parents=True) + (change / "specs" / "cap" / "spec.md").write_text("## ADDED Requirements\n\n### Requirement: Lane Is Checked\n\n### Requirement: A lane Exists\n") + (change / "design.md").write_text( + "# Design\n\n## Coverage table\n\n| Capability | Requirement | Check |\n| --- | --- | --- |\n" + "| [cap][CAP] | Lane Is Checked | missed requirement |\n| cap | A [lane][LANE2] Exists | missed requirement |\n" + ) + result = run(SCRIPTS["coverage"], change) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + self.assertIn("coverage: 0 findings", result.stdout) + AGE = re.compile(r"^age_days:\s*(\d+)\s*$", re.MULTILINE) @@ -244,6 +259,22 @@ def test_each_valid_fixture_is_clean_for_its_rule(self): rules = {rule for _, rule in self.hits(config, fixture)} self.assertNotIn(self.rule_of(fixture), rules, f"{fixture.name} trips its own rule") + def test_undefined_term_needs_a_link_a_bold_or_backticks(self): + """Italics alone define no term, and a hyphenated name inside link + text is a name, not the noun. The valid fixture holds the forms that + do define one.""" + config = FIXTURES / "vale" / ".vale.ini" + cases = { + "italic.md": "# Note\n\nAn *lane* exists. A lane runs.\n", + "hyphen-link.md": "# Note\n\nA [review-lanes][X] label exists. A lane runs.\n\n[X]: ./x.md\n", + } + with tempfile.TemporaryDirectory() as scratch: + for name, body in cases.items(): + path = Path(scratch) / name + path.write_text(body) + with self.subTest(name): + self.assertIn((name, "Core.UndefinedTerm"), self.hits(config, path)) + def test_production_scoping(self): """The deck's own .vale.ini decides where a rule applies: instructions get the placeholder rules, a companion directory does not, a build diff --git a/tests/test_fallback_terms.py b/tests/test_fallback_terms.py index 7f767b2a..14ccc4ac 100644 --- a/tests/test_fallback_terms.py +++ b/tests/test_fallback_terms.py @@ -3,6 +3,7 @@ and add a draft term, idempotently (CORE-0007). The fixtures under tests/fixtures/terms/ carry their own ontology and glossary pages, so no test touches the repository's own tree or delegates to rune.""" +import re import shutil import subprocess import sys @@ -170,5 +171,120 @@ def test_unknown_flag_is_a_usage_error(self): self.assertEqual(run("fix-terms", "--bogus", FIXTURES / "legacy").returncode, 2) +LINKED_ONTOLOGY = "@prefix rune: .\n@prefix rdfs: .\n\n" + "".join( + f'rune:{name} a rdfs:Class ;\n rdfs:label "{label}" ;\n rdfs:comment "The {label}." .\n\n' + for name, label in (("Harness", "harness"), ("Rule", "rule"), ("Requirement", "requirement"), ("MissedRequirement", "missed requirement"), ("RedReview", "red review"), ("ReviewLane", "review lane")) +) +PAGE = {"HARNESS": "harness", "RULE": "rule", "REQUIREMENT": "requirement", "MISSEDREQUIREMENT": "missed-requirement", "REDREVIEW": "red-review", "REVIEWLANE": "review-lane"} + + +def definitions(*tags): + return "\n" + "".join(f"[{tag}]: ../specs/glossary/{PAGE[tag]}.md\n" for tag in tags) + + +FIRST = "*[harness][HARNESS]* first.\n\n" +HARNESS = definitions("HARNESS") +# One file per case: the body, and the plain words the checker reports in it. +UNLINKED = { + "line-edges": (FIRST + "harness works.\nThe harness\n" + HARNESS, ["harness", "harness"]), + "es-plural": (FIRST + "Two harnesses work.\n" + HARNESS, ["harnesses"]), + # An emphasized word is a run-in label (`**Change ceremony.**`) and stays plain, as in rune. + "italic-word": (FIRST + "A later *harness* here.\n" + HARNESS, []), + "legacy-citation": (FIRST + "A *harness* [HARNESS] cite.\n" + HARNESS, []), + "fences": (FIRST + "~~~md\nThe harness works.\n~~~~\n\n ```\n The harness works.\n ```\n" + HARNESS, []), + "unclosed-fence": (HARNESS + "\n" + FIRST + "```\nThe harness works.\n", []), + "code-spans": (FIRST + "Use ``the harness`` and ``a `harness` b`` here.\n" + HARNESS, []), + "destinations": (FIRST + "See [record](path(foo)harness) and [record](a]harness) now.\n" + HARNESS, []), + "setext-heading": (FIRST + "The harness guide\n=================\n" + HARNESS, []), + "definition-label": (FIRST + "[MORE INFO]: ./harness.md\n" + HARNESS, []), + "titled-definition": (FIRST + "A plain harness.\n\n[HARNESS]: ../specs/glossary/harness.md \"title\"\n", ["harness"]), + "definition-only": ("The harness works.\n" + HARNESS, []), + "legacy-definition-only": ("The harness works.\n\n[HARNESS]: https://runedeck.ai/ns#Harness\n", []), + "longer-term": ("*[requirement][REQUIREMENT]* and *[missed requirement][MISSEDREQUIREMENT]*.\n\nA missed requirement remains.\n" + definitions("REQUIREMENT", "MISSEDREQUIREMENT"), ["missed requirement"]), +} +OWN_TAG = "\n[OTHER]: ../specs/glossary/harness.md\n[HARNESS]: ../specs/glossary/harness.md\n" +LANES = definitions("REDREVIEW", "REVIEWLANE") +RULE = definitions("RULE") +# One file per case: the body, and the text after `fix-terms --links`. +FIXED = { + "dotted-capital": (FIRST + "The İ harness works.\n" + HARNESS, FIRST + "The İ [harness][HARNESS] works.\n" + HARNESS), + "own-tag": (FIRST + "A plain harness.\n" + OWN_TAG, FIRST + "A plain [harness][HARNESS].\n" + OWN_TAG), + "later-longer-term": ("*[red review][REDREVIEW]* and *[review lane][REVIEWLANE]*.\n\nThe red review lane runs.\n" + LANES, "*[red review][REDREVIEW]* and *[review lane][REVIEWLANE]*.\n\nThe red [review lane][REVIEWLANE] runs.\n" + LANES), + "first-use-moves": ("A rule governs. Then *[rule][RULE]* follows.\n" + RULE, "A *[rule][RULE]* governs. Then [rule][RULE] follows.\n" + RULE), + "italic-later-use": (FIRST + "A later *harness* here.\n" + HARNESS, FIRST + "A later *harness* here.\n" + HARNESS), +} +UNLINKED_LINE = re.compile(r"^error\[term-unlinked\]: docs/notes/([^:]+)\.md:\d+: '([^']*)'", re.MULTILINE) + + +def unlinked_words(output, name): + return [word for file, word in UNLINKED_LINE.findall(output) if file == name] + + +class LinkedTermOccurrences(unittest.TestCase): + """Every plain occurrence of a term a file links is a link. The checker + finds one at a line edge, as an `es` plural, and in italics, and never + in code, a heading, a definition, a legacy citation, or a link + destination. The fixer links the longer term first and keeps the + emphasis on the first use. Each case is one file of a scratch deck.""" + + @staticmethod + def deck(scratch, cases, missing=()): + root = Path(scratch) / "deck" + (root / "ontology").mkdir(parents=True) + (root / termlib.ONTOLOGY).write_text(LINKED_ONTOLOGY) + glossary = root / termlib.GLOSSARY + glossary.mkdir(parents=True) + for name, text in termlib.load(root).render().items(): + if name not in missing: + (glossary / name).write_text(text) + notes = root / "docs" / "notes" + notes.mkdir(parents=True) + for name, body in cases.items(): + (notes / f"{name}.md").write_text(body) + return root, notes + + def test_checker_reports_each_plain_occurrence_once(self): + with tempfile.TemporaryDirectory() as scratch: + root, _ = self.deck(scratch, {name: body for name, (body, _) in UNLINKED.items()}) + result = run("check-terms", root) + self.assertEqual(result.stderr, "", "a check that crashes is not a finding") + for name, (_, words) in UNLINKED.items(): + with self.subTest(name): + self.assertEqual(unlinked_words(result.stdout, name), words, result.stdout) + + def test_checker_suggests_the_tag_the_file_uses(self): + with tempfile.TemporaryDirectory() as scratch: + root, _ = self.deck(scratch, {"own-tag": FIXED["own-tag"][0]}) + result = run("check-terms", root) + self.assertIn("write `[harness][HARNESS]`", result.stdout) + + def test_fixer_links_once_and_moves_the_emphasis(self): + cases = {name: body for name, (body, _) in {**UNLINKED, **FIXED}.items()} + with tempfile.TemporaryDirectory() as scratch: + root, notes = self.deck(scratch, cases) + result = run("fix-terms", "--links", root) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + for name, (_, expected) in FIXED.items(): + with self.subTest(name): + self.assertEqual((notes / f"{name}.md").read_text(), expected) + for name, (body, words) in UNLINKED.items(): + if not words and name != "legacy-definition-only": + with self.subTest(name): + self.assertEqual((notes / f"{name}.md").read_text(), body, "nothing to link, so nothing changes") + check = run("check-terms", root) + self.assertNotIn("term-unlinked", check.stdout) + self.assertNotIn("term-first-use", check.stdout) + again = run("fix-terms", "--links", root) + self.assertIn("links: 0 files rewritten", again.stdout) + + def test_a_migrated_link_to_a_missing_page_keeps_its_term(self): + body = "A *rule* [CUSTOM] governs. Another rule follows.\n\n[CUSTOM]: https://runedeck.ai/ns#Rule\n" + with tempfile.TemporaryDirectory() as scratch: + root, notes = self.deck(scratch, {"custom-tag": body}, missing={"rule.md"}) + result = run("fix-terms", "--links", root) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + self.assertEqual((notes / "custom-tag.md").read_text(), "A *[rule][CUSTOM]* governs. Another [rule][CUSTOM] follows.\n\n[CUSTOM]: ../specs/glossary/rule.md\n") + + if __name__ == "__main__": unittest.main() From 44e932d45969b0a00b71f1cc89f22976efd2fb92 Mon Sep 17 00:00:00 2001 From: "Claude Fable 5.1 (claude-fable-5-1)" Date: Mon, 19 Oct 2026 00:39:14 +0200 Subject: [PATCH 2/8] docs: the forty decision records in the plain register Every decision record leads with the point, names an actor, keeps one idea per sentence, links each ontology term at every occurrence, and carries one rewrite note. Rewritten from the voice review of 2026-10-18 over three agent passes with a verifier after each; the verifiers' remaining judgements are in the workshop under docs/specs/2026-10-18-foundations-landing/reports/records-open-judgements.md. Co-Authored-By: Martin Zeman --- .../CORE-0001 Markdown as System Language.md | 48 ++++++--- .../CORE-0002 Metadata Inside Files.md | 42 +++++--- ...AML Frontmatter for Machine Readability.md | 40 ++++--- .../decisions/CORE-0004 Directories Direct.md | 29 +++-- ... in Markdown Instructions and Templates.md | 49 +++++---- .../CORE-0006 Open Knowledge Format Bundle.md | 58 ++++++---- .../CORE-0007 Fixture-Based Canary Testing.md | 22 ++-- docs/decisions/CORE-0008 File over App.md | 28 +++-- .../CORE-0009 Reference Citations.md | 29 +++-- ...010 Adopt Architecture Decision Records.md | 31 +++--- .../CORE-0011 ADR Template Choice.md | 29 +++-- .../CORE-0012 MADR Frontmatter Extensions.md | 51 +++++---- docs/decisions/CORE-0013 Context Economy.md | 93 ++++++++++------ .../CORE-0014 No Performance Personas.md | 34 ++++-- .../CORE-0015 Positive Instruction.md | 30 ++++-- ...E-0016 Adversarial Review over Councils.md | 32 +++--- .../decisions/CORE-0017 The Inference Turn.md | 34 +++--- .../CORE-0019 Spec-Driven Change Lifecycle.md | 38 ++++--- .../CORE-0020 YAML Configuration Files.md | 39 ++++--- .../CORE-0021 Unified Module Validation.md | 28 +++-- .../CORE-0022 Verified Remote Execution.md | 30 +++--- .../CORE-0023 Rune Fills the Tool Roles.md | 51 +++++---- .../DECK-0001 Artifact Benchmarking Skill.md | 50 ++++++--- .../DECK-0002 Benchmark Execution Ladder.md | 40 ++++--- ...Metric Verdict and Cross-Vendor Judging.md | 45 ++++---- .../DECK-0004 Temporary Adoption State.md | 41 ++++--- ... Artifact Lifecycle and Evidence Tokens.md | 56 +++++++--- ...CK-0006 State Stores and Provider Edges.md | 40 ++++--- docs/decisions/DECK-0007 Retirement Path.md | 43 +++++--- .../DECK-0008 Idea-to-Merge Flywheel.md | 54 ++++++---- ...lared Constraints over Proposed Changes.md | 48 ++++++--- .../DECK-0010 Declared World in RDF.md | 68 +++++++----- .../DECK-0011 Review Tooling Adoption.md | 43 +++++--- ...-0012 Consumer Ceremony Synchronization.md | 41 ++++--- ...CK-0013 Local Checks Before Publication.md | 35 +++--- .../DECK-0014 Vocabulary Governance.md | 53 ++++++--- .../DECK-0015 Behavior Proof per Change.md | 42 +++++--- .../DECK-0017 Sealed Review Ceremony.md | 102 +++++++++--------- ...rocedures Independent of Harness Syntax.md | 42 +++++--- ...Plays as a Cast Where a Person Reads It.md | 25 +++-- 40 files changed, 1097 insertions(+), 636 deletions(-) diff --git a/docs/decisions/CORE-0001 Markdown as System Language.md b/docs/decisions/CORE-0001 Markdown as System Language.md index 540a1963..d4412926 100644 --- a/docs/decisions/CORE-0001 Markdown as System Language.md +++ b/docs/decisions/CORE-0001 Markdown as System Language.md @@ -8,7 +8,7 @@ tags: - markdown status: accepted created: 2026-02-19 -updated: 2026-03-30 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: [] @@ -33,34 +33,50 @@ change: plain-text-first-principles ## Context and Problem Statement -One format has to serve skills, agents, rules, records, templates, and configuration. A person reads it in an editor, a model reads it through any harness, and an author edits it without special tooling. +One format has to serve the *[instructions][INSTRUCTION]* (*[skills][SKILL]*, *[agents][AGENT]*, *[rules][RULE]*), records, *[templates][TEMPLATE]*, and configuration. A person reads the file in an editor. A *[model][MODEL]* reads it through any *[harness][HARNESS]*. An author edits it without special tooling. The format also has to work in a note-taking tool where content comes first, above all [Obsidian][OBS]. Obsidian adopted YAML frontmatter in [2020][OBS-FM] and adds `[[wikilinks]]` on top. ## Decision Drivers -- No lock-in to one AI tool's plugin format -- No runtime dependency: any tool that reads files can read the content -- Authors iterate by editing prose, not by debugging code -- The same file works in an Obsidian vault, on GitHub, and in a model's context +- The owner wants no lock-in to one AI tool's plugin format. +- Any tool that reads files can read the content. The *[repository][REPOSITORY]* needs no runtime dependency. +- An author improves a file by editing prose. Nobody debugs code to improve it. +- One file works in an Obsidian vault, on GitHub, and in a [model][MODEL]'s context window. ## Considered Options -1. Code templates: executable scripts with placeholder substitution. Each provider and each kind of file needs its own runtime. -2. JSON or YAML definitions: structured data with typed fields. A machine reads it well, and a person authors and reviews it badly. -3. Markdown with YAML frontmatter: a prose body for the instruction, frontmatter for the metadata, validated by mdschema. +1. Code [templates][TEMPLATE]: an executable script fills placeholders. Each [harness][HARNESS] and each kind of file then needs its own runtime. +2. JSON or YAML definitions: structured data with typed fields. A machine reads it well. A person authors and reviews it badly. +3. Markdown with YAML frontmatter: the body is the [instruction][INSTRUCTION] as prose, and the frontmatter is the metadata. The *[validator][VALIDATOR]*, the program `mdschema`, checks the file against the *[structure schema][STRUCTURESCHEMA]* of its directory. ## Decision Outcome -Option 3. Everything is Markdown: skills, agents, rules, decision records, templates, and documentation. YAML frontmatter holds the machine-readable metadata, and the body is free-form instruction or content. The same file works in a git repository, an Obsidian vault, and a model's context window. -Every skill, agent, rule, decision record, and document MUST be a Markdown file. A file with metadata MUST keep it as YAML frontmatter ([CORE-0003](CORE-0003%20YAML%20Frontmatter%20for%20Machine%20Readability.md)). +Option 3. The author writes every [skill][SKILL], [agent][AGENT], [rule][RULE], *[decision record][DECISIONRECORD]*, [template][TEMPLATE], and document as Markdown. The YAML frontmatter stores the metadata a machine reads. The body is the [instruction][INSTRUCTION] or the content as free prose. One file works in a git [repository][REPOSITORY], an Obsidian vault, and a [model][MODEL]'s context window. +The author MUST write every [skill][SKILL], [agent][AGENT], [rule][RULE], [decision record][DECISIONRECORD], and document as a Markdown file. When a file has metadata, the author MUST keep it as YAML frontmatter ([CORE-0003](CORE-0003%20YAML%20Frontmatter%20for%20Machine%20Readability.md)). ### Consequences -- [+] No dependency: any AI tool that reads files can read the content -- [+] Authors iterate by editing prose -- [+] The same files render in Obsidian, on GitHub, and in a model's context without conversion -- [-] Nothing checks content logic at compile time. mdschema checks structure, and review checks meaning. -- [-] Free-form Markdown checks nothing on its own, so the system needs linters and validators ([CORE-0021](CORE-0021%20Unified%20Module%20Validation.md)) +- [+] Any AI tool that reads files can read the content. The [repository][REPOSITORY] needs no dependency. +- [+] An author improves a file by editing prose. +- [+] The same file renders in Obsidian, on GitHub, and in a [model][MODEL]'s context without conversion. +- [-] Nothing checks the logic of the content at compile time. The [validator][VALIDATOR] checks structure against the [structure schema][STRUCTURESCHEMA], and a reviewer checks meaning. +- [-] Free-form Markdown checks nothing on its own, so the [repository][REPOSITORY] needs *[linters][LINTER]* and [validators][VALIDATOR] to check it ([CORE-0021](CORE-0021%20Unified%20Module%20Validation.md)). + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [OBS]: https://obsidian.md/ [OBS-FM]: https://obsidian.md/changelog/2020-08-21-desktop-v0.8.5/ +[INSTRUCTION]: ../specs/glossary/instruction.md +[REPOSITORY]: ../specs/glossary/repository.md +[SKILL]: ../specs/glossary/skill.md +[AGENT]: ../specs/glossary/agent.md +[RULE]: ../specs/glossary/rule.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[MODEL]: ../specs/glossary/model.md +[HARNESS]: ../specs/glossary/harness.md +[VALIDATOR]: ../specs/glossary/validator.md +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md +[TEMPLATE]: ../specs/glossary/template.md +[LINTER]: ../specs/glossary/linter.md diff --git a/docs/decisions/CORE-0002 Metadata Inside Files.md b/docs/decisions/CORE-0002 Metadata Inside Files.md index 3216e4a5..38950183 100644 --- a/docs/decisions/CORE-0002 Metadata Inside Files.md +++ b/docs/decisions/CORE-0002 Metadata Inside Files.md @@ -9,7 +9,7 @@ tags: - portability status: accepted created: 2026-03-30 -updated: 2026-03-30 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -28,35 +28,39 @@ change: plain-text-first-principles ## Context and Problem Statement -[CORE-0001](CORE-0001%20Markdown%20as%20System%20Language.md) makes Markdown the system language. Every Markdown file has metadata: status, author, tags, relationships, dates. The question is where that metadata lives. -Knowledge tools answer it in two ways. One keeps the metadata in a database beside the files. The other keeps it inside each file. +Every Markdown file has metadata, and this record says where the author stores it. [CORE-0001](CORE-0001%20Markdown%20as%20System%20Language.md) makes Markdown the system language. The metadata of a file is its status, its author, its tags, its relationships, and its dates. +Knowledge tools store that metadata in one of two places. One tool keeps the metadata in a database beside the files. Another tool keeps it inside each file. ## Decision Drivers -- Files move between git repositories, Obsidian vaults, model contexts, CI pipelines, and shared drives -- Metadata that does not move with its file is invisible and eventually lost +- The owner moves files between git repositories, Obsidian vaults, *[model][MODEL]* contexts, continuous integration (CI) pipelines, and shared drives +- Metadata that does not move with its file is invisible to the reader, and the owner loses it in the end - Obsidian, Claude Code, GitHub, shell scripts, and CI validators all read the same metadata -- No single tool is the keeper of the metadata. The file is the source of truth. +- No tool is the keeper of the metadata. The file is the source of truth. ## Considered Options -1. An external database: [Evernote][EVERNOTE], [DEVONthink][DEVON], [TheBrain][BRAIN], and [Bear][BEAR] keep metadata in a proprietary database outside the files. Queries are fast, and git, text editors, and models cannot see the metadata. A file that moves loses it, and the database is a single point of failure and a lock-in. -2. Sidecar files: a `.yaml` or `.json` beside each `.md`. The data stays in the filesystem, the file count doubles, and a rename or move silently orphans the sidecar. -3. Metadata inside the file: the metadata moves with the file, every tool that reads the file gets it, and there is nothing to synchronize. +1. An external database. [Evernote][EVERNOTE], [DEVONthink][DEVON], [TheBrain][BRAIN], and [Bear][BEAR] keep metadata in a proprietary database outside the files. Queries are fast. Git, a text editor, and a [model][MODEL] cannot see the metadata. A file that the owner moves loses its metadata. The database is one point of failure, and it locks the owner in. +2. *[Sidecar][SIDECAR]* files. The author writes a `.yaml` or `.json` beside each `.md`. The data stays in the filesystem. The number of files doubles. A rename or a move orphans the [sidecar][SIDECAR], and no tool warns. +3. Metadata inside the file. The metadata moves with the file. Every tool that reads the file gets the metadata, and there is nothing to synchronize. ## Decision Outcome -Option 3. Metadata MUST live inside the file it describes, not in a database, a sidecar, or a separate tracking system. -The one exception is sealed provenance evidence, which goes into `.provenance/` sidecars beside deployed artifacts, because a seal attests a file at a point in time and cannot be part of the content it attests. -[Obsidian][OBS] keeps frontmatter in each file and rebuilds its metadata cache from the files, so a vault of thousands of notes moves through git with nothing but the files ([Obsidian Properties][OBS-PROPS]). -The mechanism is YAML frontmatter ([CORE-0003](CORE-0003%20YAML%20Frontmatter%20for%20Machine%20Readability.md)). +Option 3. The author MUST keep metadata inside the file it describes. A database, a [sidecar][SIDECAR], or a separate tracking system MUST NOT hold it. +The one exception is the sealed *[provenance record][PROVENANCERECORD]*. It goes into a [sidecar][SIDECAR] under `.provenance/` beside each deployed *[artifact][ARTIFACT]*. A seal attests a file at a point in time, so the seal cannot be part of the content it attests. +[Obsidian][OBS] keeps frontmatter in each file and rebuilds its metadata cache from the files. A vault of thousands of notes moves through git with nothing but the files ([Obsidian Properties][OBS-PROPS]). +The author writes the metadata as YAML frontmatter ([CORE-0003](CORE-0003%20YAML%20Frontmatter%20for%20Machine%20Readability.md)). ### Consequences -- [+] Files describe themselves and move to any system that reads text -- [+] No external dependency for metadata -- [+] A whole class of bugs disappears: orphaned metadata, stale sidecars, sync failures -- [-] Files grow slightly. Compact frontmatter keeps the cost small. +- [+] A file describes itself, and the owner moves it to any system that reads text +- [+] The metadata depends on nothing outside the file +- [+] A whole class of bugs disappears: orphaned metadata, stale [sidecars][SIDECAR], and failed synchronization +- [-] A file grows slightly. Compact frontmatter limits the growth. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [EVERNOTE]: https://evernote.com/ [DEVON]: https://www.devontechnologies.com/apps/devonthink @@ -64,3 +68,7 @@ The mechanism is YAML frontmatter ([CORE-0003](CORE-0003%20YAML%20Frontmatter%20 [BEAR]: https://bear.app/ [OBS]: https://obsidian.md/ [OBS-PROPS]: https://help.obsidian.md/properties +[MODEL]: ../specs/glossary/model.md +[PROVENANCERECORD]: ../specs/glossary/provenance-record.md +[SIDECAR]: ../specs/glossary/sidecar.md +[ARTIFACT]: ../specs/glossary/artifact.md diff --git a/docs/decisions/CORE-0003 YAML Frontmatter for Machine Readability.md b/docs/decisions/CORE-0003 YAML Frontmatter for Machine Readability.md index b55cb097..6022babd 100644 --- a/docs/decisions/CORE-0003 YAML Frontmatter for Machine Readability.md +++ b/docs/decisions/CORE-0003 YAML Frontmatter for Machine Readability.md @@ -10,7 +10,7 @@ tags: - yaml status: accepted created: 2026-03-30 -updated: 2026-10-18 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -31,44 +31,45 @@ change: plain-text-first-principles ## Context and Problem Statement [CORE-0002](CORE-0002%20Metadata%20Inside%20Files.md) puts metadata inside the file. This record picks the format: YAML frontmatter. -The ecosystem has settled on it as the metadata layer for Markdown: +Every major Markdown tool reads it as the metadata layer: -- [Claude Code][CC] reads YAML frontmatter in `CLAUDE.md` and in skill files for purpose, version, and configuration without parsing the body ([docs][CC-DOCS]). +- [Claude Code][CC] reads purpose, version, and configuration from the YAML frontmatter of `CLAUDE.md` and *[skill][SKILL]* files, without parsing the body ([docs][CC-DOCS]). - [Obsidian][OBS] built its [Properties][OBS-PROPS] panel on it, and the [Dataview][DATAVIEW] and Tasks plugins query the fields. - Static-site generators ([Jekyll][JEKYLL], [Hugo][HUGO], Astro, Eleventy) read it as the metadata of a content file. - GitHub renders it as a table at the top of a Markdown file. ## Decision Drivers -- Obsidian Properties needs YAML frontmatter, and without it the vault has nothing to query -- Claude Code parses frontmatter for skill, agent, and rule definitions -- mdschema validates typed fields that CI can check -- Structured MADR depends on a rich frontmatter convention +- The Obsidian Properties panel needs YAML frontmatter. Without it the vault has nothing to query +- Claude Code parses frontmatter for [skill][SKILL], *[agent][AGENT]*, and *[rule][RULE]* definitions +- The *[structure schema][STRUCTURESCHEMA]* check (mdschema) validates typed fields, so continuous integration can check them +- Structured MADR, a *[structured architecture decision record][STRUCTUREDARCHITECTUREDECISIONRECORD]* built on the *[Markdown architecture decision record][MARKDOWNARCHITECTUREDECISIONRECORD]* (MADR), depends on a rich frontmatter convention - Every major Markdown tool expects it ## Considered Options -1. TOML frontmatter between `+++` lines. [Hugo][HUGO] accepts it. Harder to read for nested structures, and [Obsidian][OBS] does not read it. -2. JSON frontmatter between `{` and `}`. Some static-site generators accept it. Hostile to hand authoring, poor for lists, and [Obsidian][OBS] does not read it. +1. TOML frontmatter between `+++` lines. [Hugo][HUGO] accepts it. A nested structure reads harder, and [Obsidian][OBS] does not read it. +2. JSON frontmatter between `{` and `}`. Some static-site generators accept it. An author writes it by hand with difficulty, and a list reads badly. [Obsidian][OBS] does not read it. 3. YAML frontmatter between `---` lines. Markdown renderers, [Obsidian][OBS], [Claude Code][CC], GitHub, and static-site generators all read it. ## Decision Outcome -Option 3. A Markdown file with metadata MUST keep it as YAML between `---` lines at the top of the file. A rule is a bare instruction body and may have none. Frontmatter holds the machine-readable metadata, and the body holds the instruction for people and models. The same file works in a git repository, an [Obsidian][OBS] vault, and a model's context without conversion. +Option 3. A Markdown file with metadata MUST keep it as YAML between `---` lines at the top of the file. A [rule][RULE] is a bare *[instruction][INSTRUCTION]* body and may have none. The frontmatter is the metadata a machine reads. The body contains the directions for people and *[models][MODEL]*. The same file works in a git repository, an [Obsidian][OBS] vault, and a [model][MODEL]'s context without conversion. Frontmatter is YAML and MAY nest. The [Obsidian][OBS] vault is only the viewer and search tool. A Properties panel that cannot show a nested value does not limit the file. -This decision comes before the MADR template choice. Frontmatter is the mechanism, and Structured MADR is the schema for which fields a decision record has. +This decision comes before the choice of the MADR format. Frontmatter is the mechanism. Structured MADR is the schema that says which fields a *[decision record][DECISIONRECORD]* has. ### Consequences -- [+] Every file is queryable by [Dataview][DATAVIEW], checkable by mdschema, and readable by a model without custom logic -- [+] The dominant convention, so no migration and no custom tooling +- [+] [Dataview][DATAVIEW] queries every file, and the check against the [structure schema][STRUCTURESCHEMA] validates it. A [model][MODEL] reads it without custom logic +- [+] The convention is the dominant one, so nobody migrates a file and nobody writes custom tooling - [+] Metadata moves with the file -- [-] Authors maintain frontmatter beside prose. Stale metadata is the risk, and CI validation plus the Properties panel keep it low. +- [-] An author maintains frontmatter beside the prose. Stale metadata is the risk. The continuous integration check and the Properties panel keep it low. - [-] The Properties panel does not show a nested value. A reader opens the file to see it. ## More Information -- Amended on 2026-10-18: the flatness rule is dropped, because the vault stopped being the editor. Frontmatter MAY nest. +- Amended on 2026-10-18: this record drops the flatness constraint, because the vault stopped being the editor. Frontmatter MAY nest. +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [CC]: https://claude.ai/code [CC-DOCS]: https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview @@ -77,3 +78,12 @@ This decision comes before the MADR template choice. Frontmatter is the mechanis [DATAVIEW]: https://blacksmithgu.github.io/obsidian-dataview/ [JEKYLL]: https://jekyllrb.com/docs/front-matter/ [HUGO]: https://gohugo.io/content-management/front-matter/ +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md +[STRUCTUREDARCHITECTUREDECISIONRECORD]: ../specs/glossary/structured-architecture-decision-record.md +[MARKDOWNARCHITECTUREDECISIONRECORD]: ../specs/glossary/markdown-architecture-decision-record.md +[SKILL]: ../specs/glossary/skill.md +[AGENT]: ../specs/glossary/agent.md +[RULE]: ../specs/glossary/rule.md +[MODEL]: ../specs/glossary/model.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[INSTRUCTION]: ../specs/glossary/instruction.md diff --git a/docs/decisions/CORE-0004 Directories Direct.md b/docs/decisions/CORE-0004 Directories Direct.md index 1fc55728..1848be7f 100644 --- a/docs/decisions/CORE-0004 Directories Direct.md +++ b/docs/decisions/CORE-0004 Directories Direct.md @@ -8,7 +8,7 @@ tags: - naming status: accepted created: 2026-03-19 -updated: 2026-03-30 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: [] @@ -27,24 +27,33 @@ change: plain-text-first-principles ## Context and Problem Statement -Directory names are how people and models find things. In an assembly pipeline they also decide what happens to the files, which harness gets them, and which variant applies. A directory named `claude/` says that its content is for Claude. A directory named `misc/` says nothing. +A directory name is how a person and a *[model][MODEL]* find a file. The *[assembler][ASSEMBLER]* reads the same name and acts on it. From the name, the [assembler][ASSEMBLER] picks the *[harness][HARNESS]* that loads the files inside, and the *[variant][VARIANT]* that applies. A directory named `claude/` tells the [assembler][ASSEMBLER] that its content is for Claude. A directory named `misc/` tells it nothing. ## Considered Options -1. Group by type: `config/`, `docs/`, `scripts/`. Familiar, and it says what a thing is rather than where it goes or what a tool does with it. -2. Name by function: `claude/`, `user/`, `rules/`. Every name is a decision a tool acts on. +1. Group by kind: `config/`, `docs/`, `scripts/`. The names are familiar. A name says what a thing is. It does not say where the thing goes or what a tool does with it. +2. Name by function: `claude/`, `user/`, `rules/`. Every name is a decision that a tool acts on. ## Decision Outcome -Option 2. A directory name MUST tell a tool what to do with the files inside it. A directory that only groups by type, such as `misc/`, MUST NOT be added. +Option 2. A directory name MUST tell the tool that reads it what to do with the files inside. The author MUST NOT add a directory that only groups by kind, such as `misc/`. -- Optimize for where someone would look, not for taxonomy -- A well-named directory replaces a document about where things go -- A name in a qualifier path (`claude/`, `codex/`, `user/`) is configuration, so a typo silently disables the content -- Say `directory`, not `folder`, in docs, commit messages, and conversation +- The author names a directory for the place where a person would look first. A taxonomy does not decide the name. +- A well-named directory replaces a document about where things go. +- A name in a qualifier path (`claude/`, `codex/`, `user/`) is configuration, so a typo in that name silently disables the content. +- The author writes `directory` in docs, commit messages, and conversation, never `folder`. ### Consequences - [+] The tree documents itself -- [+] No separate guide about where to put things +- [+] The owner does not need a separate guide about where to put things - [+] A qualifier directory name is configuration that a tool acts on + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[MODEL]: ../specs/glossary/model.md +[ASSEMBLER]: ../specs/glossary/assembler.md +[HARNESS]: ../specs/glossary/harness.md +[VARIANT]: ../specs/glossary/variant.md diff --git a/docs/decisions/CORE-0005 Variables in Markdown Instructions and Templates.md b/docs/decisions/CORE-0005 Variables in Markdown Instructions and Templates.md index 698d84eb..f0ab2b0d 100644 --- a/docs/decisions/CORE-0005 Variables in Markdown Instructions and Templates.md +++ b/docs/decisions/CORE-0005 Variables in Markdown Instructions and Templates.md @@ -10,7 +10,7 @@ tags: - templates status: accepted created: 2026-02-19 -updated: 2026-03-30 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -32,42 +32,55 @@ change: plain-text-first-principles ## Context and Problem Statement -Skills, agents, and rules refer to configurable values: paths, directory locations, tool names, feature flags. Templates need placeholders filled at creation time. Shell tools resolve both, and a model reads both. -Three placeholder patterns had grown on their own: inline descriptions ("Title, a short noun phrase"), `{{VARIABLE}}` from [Obsidian Templater][TEMPLATER], and `{VARIABLE}` from Structured MADR. A shell resolves none of them. +An *[instruction][INSTRUCTION]* (a *[skill][SKILL]*, an *[agent][AGENT]*, or a *[rule][RULE]*) refers to configurable values: paths, directory locations, tool names, and feature flags. A *[template][TEMPLATE]* needs placeholders that the author fills at creation time. A shell tool resolves both kinds of value, and a *[model][MODEL]* reads both. +Placeholders had appeared in three forms: an inline description ("Title, a short noun phrase"), `{{VARIABLE}}` from [Obsidian Templater][TEMPLATER], and `{VARIABLE}` from *[Structured MADR][STRUCTUREDARCHITECTUREDECISIONRECORD]*. A shell resolves none of the three. ## Decision Drivers -- A hardcoded path breaks on the next machine -- Shell commands and prose need the same value from the same source -- A template instantiates with `envsubst` and no custom parser +- A path written into an [instruction][INSTRUCTION] breaks on the next machine +- A shell command and the prose around it need the same value from one source +- The substitution tool `envsubst` fills a [template][TEMPLATE] without a custom parser - A placeholder name explains itself -- Obsidian Templater keeps its own `{{VARIABLE}}`, so the two have to coexist +- Obsidian Templater keeps its own `{{VARIABLE}}`, so both patterns have to coexist ## Considered Options -1. `{{VARIABLE}}`, the Templater syntax. Works inside Obsidian, unreadable to a shell, and collides with Templater when the file opens in the vault. -2. `{VARIABLE}`, the Structured MADR convention. Not shell syntax, so it needs custom string replacement. -3. `${VARIABLE}` with `%%` comments: standard shell parameter expansion that `envsubst` resolves, with Obsidian `%%` comments as inline documentation hidden in preview. +1. `{{VARIABLE}}`, the Templater syntax. It works inside Obsidian. A shell cannot read it. It collides with Templater when the file opens in the vault. +2. `{VARIABLE}`, the [Structured MADR][STRUCTUREDARCHITECTUREDECISIONRECORD] convention. It is not shell syntax, so it needs custom string replacement. +3. `${VARIABLE}` with `%%` comments. A shell expands the placeholder as a standard parameter, and `envsubst` resolves it. An Obsidian `%%` comment documents it inline, and Obsidian hides the comment in preview. ## Decision Outcome Option 3, as two patterns for two contexts. -An instruction MUST refer to a configurable value as `$VARIABLE`. The model reads the name and resolves it from the environment when it runs a shell command, with no build step and no placeholder replacement. -A plain-text template MUST use `${VARIABLE}` placeholders that `envsubst < template.md > output.md` resolves. Names are UPPER_SNAKE_CASE, and an end-of-line `%%` comment says what each variable holds, with enum choices separated by `|`: + +An [instruction][INSTRUCTION] MUST refer to a configurable value as `$VARIABLE`. The [model][MODEL] reads the name and resolves it from the environment when it runs a shell command. The pattern needs no build step and no placeholder replacement. + +A plain-text [template][TEMPLATE] MUST use `${VARIABLE}` placeholders. The command `envsubst < template.md > output.md` resolves them. A name is `UPPER_SNAKE_CASE`. An end-of-line `%%` comment says what each variable contains, and `|` separates the choices of an enum: ```markdown status: ${STATUS} %% proposed | accepted | deprecated | superseded %% - ${POSITIVE} %% positive outcome, what improves %% ``` -Obsidian Templater templates keep `{{VARIABLE}}`. The two patterns MUST NOT mix in one file. +An Obsidian Templater [template][TEMPLATE] keeps `{{VARIABLE}}`. The Templater pattern and the shell pattern MUST NOT mix in one file. ### Consequences -- [+] Shell commands and instructions share one configuration mechanism -- [+] Templates instantiate with `envsubst` and nothing else -- [+] `%%` comments are invisible in Obsidian preview and visible in source -- [-] The environment has to hold the values before the instruction is useful, so a configuration loading layer is required -- [-] `${VAR}` and `{{VAR}}` coexist, kept apart by the rule that they never mix in one file +- [+] A shell command and an [instruction][INSTRUCTION] share one configuration mechanism +- [+] The author fills a [template][TEMPLATE] with `envsubst` and nothing else +- [+] A `%%` comment is invisible in Obsidian preview and visible in source +- [-] The environment has to contain the values before an [instruction][INSTRUCTION] is useful, so a configuration loading layer is required +- [-] `${VAR}` and `{{VAR}}` coexist. The principle against mixing them in one file keeps them apart + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [TEMPLATER]: https://silentvoid13.github.io/Templater/ +[INSTRUCTION]: ../specs/glossary/instruction.md +[SKILL]: ../specs/glossary/skill.md +[AGENT]: ../specs/glossary/agent.md +[RULE]: ../specs/glossary/rule.md +[TEMPLATE]: ../specs/glossary/template.md +[MODEL]: ../specs/glossary/model.md +[STRUCTUREDARCHITECTUREDECISIONRECORD]: ../specs/glossary/structured-architecture-decision-record.md diff --git a/docs/decisions/CORE-0006 Open Knowledge Format Bundle.md b/docs/decisions/CORE-0006 Open Knowledge Format Bundle.md index f3a2220e..2c43d776 100644 --- a/docs/decisions/CORE-0006 Open Knowledge Format Bundle.md +++ b/docs/decisions/CORE-0006 Open Knowledge Format Bundle.md @@ -9,7 +9,7 @@ tags: - linking status: proposed created: 2026-09-23 -updated: 2026-10-18 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -36,44 +36,56 @@ change: plain-text-first-principles ## Context and Problem Statement -The repository is a directory of Markdown files. Its readers are a person in an editor or a vault, a model through a harness, and a tool that builds a graph or an index. +The *[repository][REPOSITORY]* is a directory of Markdown files. A person reads it in an editor or a vault. A *[model][MODEL]* reads it through a *[harness][HARNESS]*. A tool reads it to build a graph or an index. Either each file says what it is and what it links to, or the reader has to guess. -Three link forms compete: Markdown links in the repository, wikilinks in the vault, and `@path` references in a harness. -The repository needs one contract for the file structure and the link form, one a machine can read as a graph, and the graph has to be rebuildable from the files alone (frame 04 of the canvas). +Three link forms compete. The [repository][REPOSITORY] uses Markdown links, the vault uses wikilinks, and a [harness][HARNESS] uses `@path` references. +The [repository][REPOSITORY] needs one contract for the file structure and the link form, one that a machine can read as a graph. The files alone have to be enough to rebuild the graph (frame 04 of the canvas). ## Decision Drivers -- One optional key, so a file without it takes its type from the structure schema of its directory, and a rune may still carry it under the Agent Skills `metadata` map -- Standard Markdown links, so GitHub, lychee, rumdl, and a plain parser resolve them -- No central registry of types, so a repository adds a kind without asking anyone -- A published specification another organization maintains +- The contract has one optional key, so a file without the key takes its *[type][TYPE]* from the *[structure schema][STRUCTURESCHEMA]* of its directory. A *[rune][RUNE]* may still carry the key under the Agent Skills `metadata` map. +- Links are standard Markdown, so GitHub, the link checker lychee, the Markdown *[linter][LINTER]* rumdl, and a plain parser resolve them. +- The format does not keep a central registry of [types][TYPE], so a [repository][REPOSITORY] adds a kind without asking anyone. +- Another organization publishes and maintains the specification. ## Considered Options -1. Bespoke frontmatter: keep the fields each file kind grew. The contract is then written down nowhere, and a graph tool learns each kind by hand. -2. [MIF][MIF]: typed relationships, a JSON-LD projection, bi-temporal fields, W3C PROV. It fits provenance and records, and it asks every file for more than a type. Deferred rather than rejected: the `modeled-information-format-alignment` change decides that alignment. -3. [Open Knowledge Format 0.2][OKF]: a bundle is a directory of Markdown files with YAML frontmatter. `type` is the only required key, the identifier is the file path, links are standard Markdown, `index.md` and `log.md` are reserved, consumers tolerate unknown keys and types. +1. Bespoke frontmatter: the [repository][REPOSITORY] keeps the fields each file kind grew. Nobody writes the contract down, and a graph tool learns each kind by hand. +2. [MIF][MIF], the Modeled Information Format: typed relationships, a JSON-LD (JSON for Linked Data) projection, and bi-temporal fields. It also uses W3C PROV, the provenance standard of the World Wide Web Consortium. It fits provenance and records, and it asks every file for more than a [type][TYPE]. The owner defers this option and does not reject it. The `modeled-information-format-alignment` *[change][CHANGE]* decides that alignment. +3. [Open Knowledge Format 0.2][OKF]: a bundle is a directory of Markdown files with YAML frontmatter. The format requires one key, `type`. The file path is the identifier. Links are standard Markdown. The format reserves the names `index.md` and `log.md`. A consumer tolerates unknown keys and [types][TYPE]. ## Decision Outcome -Option 3. The repository MUST be a conformant Open Knowledge Format 0.2 bundle. A file's type comes from the structure schema of its nearest directory, and an explicit key in the file overrides it. A rune may keep its `type` under the Agent Skills `metadata` map, and the graph export fills the type in for every file. -Every Markdown file outside the reserved names MUST have a `type`. -A record, a specification, or a proposal MUST link to another file in the repository with a relative Markdown link, never a wikilink or an `@path` reference. -An instruction body MAY use the `@path` form where its harness reads it, because that text is what the harness loads. -The repository root `index.md` MUST declare `okf_version: "0.2"`. -MIF alignment continues where OKF is silent: provenance, typed relationships, and the record lifecycle. +Option 3. The [repository][REPOSITORY] MUST be a conformant Open Knowledge Format 0.2 bundle. A file takes its [type][TYPE] from the [structure schema][STRUCTURESCHEMA] of its nearest directory. An explicit key in the file overrides that [type][TYPE]. A [rune][RUNE] may keep its `type` under the Agent Skills `metadata` map. The graph export fills in the [type][TYPE] for every file. +The author MUST give every Markdown file outside the reserved names a `type`. +A record, a specification, or a proposal MUST link to another file in the [repository][REPOSITORY] with a relative Markdown link. It MUST NOT use a wikilink or an `@path` reference for that. +An *[instruction][INSTRUCTION]* body MAY use the `@path` form where its [harness][HARNESS] reads it, because the [harness][HARNESS] loads that text. +The author MUST declare `okf_version: "0.2"` in the [repository][REPOSITORY] root `index.md`. +MIF alignment continues where the Open Knowledge Format is silent: provenance, typed relationships, and the stages a record passes through. ### Consequences -- [+] A directory of files reads as a graph, and a database over it is a rebuildable index, never a second home for the facts. -- [+] Every link in the repository resolves in GitHub and in the lychee check, and Obsidian resolves relative Markdown links. Fragment conventions differ by renderer, so lychee with fragments is the repository's arbiter. -- [-] Wikilinks are kept out of the repository, so a note copied from the vault gets rewritten at the boundary. The Obsidian compatibility capability specifies that rewrite. -- [-] A strict outside reader of the format sees a file without the key as untyped, because the type lives in the directory schema and the graph export. We accept that. +- [+] A directory of files reads as a graph. A database over it is an index the owner can rebuild. It is never a second home for the facts. +- [+] Every link in the [repository][REPOSITORY] resolves in GitHub and in the link checker, lychee. Obsidian resolves relative Markdown links too. Renderers differ on fragment conventions, so the link checker with fragments on is the [repository][REPOSITORY]'s arbiter. +- [-] The [repository][REPOSITORY] keeps wikilinks out, so whoever copies a note from the vault rewrites its links at the boundary. The Obsidian compatibility *[capability][CAPABILITY]* specifies that rewrite. +- [-] A strict outside reader of the format sees a file without the key as untyped. The reader does so because the [type][TYPE] lives in the directory's [structure schema][STRUCTURESCHEMA] and the graph export. The owner accepts that. ## More Information -- Amended on 2026-10-18: the type key is optional and derived from the directory schema, so the `metadata` map is no longer an exception. -- Amended on 2026-10-18: the record says repository where it meant the container, because the Open Knowledge Format bundle is the repository and the marker file marks a deck. +- Amended on 2026-10-18: the [type][TYPE] key is optional and derived from the directory's [structure schema][STRUCTURESCHEMA], so the `metadata` map is no longer an exception. +- Amended on 2026-10-18: the record says [repository][REPOSITORY] where it meant the container. It does so because the Open Knowledge Format bundle is the [repository][REPOSITORY] and the marker file marks a *[deck][DECK]*. +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [MIF]: https://github.com/modeled-information-format/MIF [OKF]: https://github.com/GoogleCloudPlatform/open-knowledge-format-bundle/blob/main/SPEC.md +[REPOSITORY]: ../specs/glossary/repository.md +[MODEL]: ../specs/glossary/model.md +[HARNESS]: ../specs/glossary/harness.md +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md +[RUNE]: ../specs/glossary/rune.md +[INSTRUCTION]: ../specs/glossary/instruction.md +[CHANGE]: ../specs/glossary/change.md +[CAPABILITY]: ../specs/glossary/capability.md +[DECK]: ../specs/glossary/deck.md +[TYPE]: ../specs/glossary/type.md +[LINTER]: ../specs/glossary/linter.md diff --git a/docs/decisions/CORE-0007 Fixture-Based Canary Testing.md b/docs/decisions/CORE-0007 Fixture-Based Canary Testing.md index f7a1686b..929cd79c 100644 --- a/docs/decisions/CORE-0007 Fixture-Based Canary Testing.md +++ b/docs/decisions/CORE-0007 Fixture-Based Canary Testing.md @@ -9,7 +9,7 @@ tags: - fixtures status: accepted created: 2026-04-05 -updated: 2026-04-05 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -29,30 +29,34 @@ change: plain-text-first-principles ## Context and Problem Statement -A checker that passes every file looks the same as a checker that checks nothing. A rule can be deleted, a pattern can stop matching, or a tool upgrade can change a default, and the check stays green. The deck needs proof that each checker still catches what it claims to catch. +The *[repository][REPOSITORY]* needs evidence that each checker still catches what it claims to catch. A checker that passes every file looks the same as a checker that checks nothing. Someone can delete an *[analysis rule][ANALYSISRULE]*, a pattern can stop matching, or a tool upgrade can alter a default, and the check still passes. ## Considered Options -1. Trust the checker. Review catches a broken rule when someone notices. -2. A manifest that maps each fixture file to its expected result. -3. Fixture inputs that must pass and inputs that must fail, with the expected result asserted by the test that runs them. +1. Trust the checker, and let review catch a broken [analysis rule][ANALYSISRULE] when someone notices. +2. A manifest that maps each *[fixture][FIXTURE]* file to its expected result. +3. [Fixture][FIXTURE] inputs that must pass and inputs that must fail, where the test that runs them asserts the expected result. ## Decision Outcome -Option 3. A checker the deck depends on MUST have at least one input that it accepts and one input that it rejects. The test MUST assert the exact result, so that a deleted or weakened rule fails the test. Where fixtures are files, `valid/` and `invalid/` directory names state the assertion. +Option 3. A checker the [repository][REPOSITORY] depends on MUST have at least one input that it accepts and one input that it rejects. The test MUST assert the exact result, so a deleted or weakened [analysis rule][ANALYSISRULE] fails the test. Where [fixtures][FIXTURE] are files, the directory names `valid/` and `invalid/` state the assertion. -The ontology hook is the instance in the deck. It validates `ontology/smoke/instances.ttl` against the shapes and asserts the exact count of violations and warnings, so a removed shape fails the hook. +The ontology hook is the one example in the [repository][REPOSITORY]. It validates `ontology/smoke/instances.ttl` against the shapes and asserts the exact count of violations and warnings, so a removed shape fails the hook. ### Consequences - [+] A new canary is one file in the right place. -- [-] Most checkers in the deck have no canary yet. The scripts under `scripts/` that check names, record ids, record fields, and record links were each proven by hand with a passing and a failing input, and those inputs are not committed. -- [-] An exact count is brittle on purpose. A new shape changes the count, and the author updates the assertion in the same commit. +- [-] Most checkers in the [repository][REPOSITORY] have no canary yet. The scripts under `scripts/` check names, record ids, record fields, and record links. The author proved each one by hand with a passing and a failing input, and nobody committed those inputs. +- [-] An exact count is fragile on purpose. A new shape alters the count, and the author updates the assertion in the same commit. ## More Information - [JSON Schema Test Suite][JSTS] - [markdownlint][MDLINT] +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [JSTS]: https://github.com/json-schema-org/JSON-Schema-Test-Suite "JSON Schema Test Suite, the validator testing pattern" [MDLINT]: https://github.com/DavidAnson/markdownlint "markdownlint, valid and invalid fixture convention" +[REPOSITORY]: ../specs/glossary/repository.md +[ANALYSISRULE]: ../specs/glossary/analysis-rule.md +[FIXTURE]: ../specs/glossary/fixture.md diff --git a/docs/decisions/CORE-0008 File over App.md b/docs/decisions/CORE-0008 File over App.md index 7bd7858a..a1721960 100644 --- a/docs/decisions/CORE-0008 File over App.md +++ b/docs/decisions/CORE-0008 File over App.md @@ -9,7 +9,7 @@ tags: - portability status: proposed created: 2026-09-28 -updated: 2026-09-28 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -33,30 +33,38 @@ change: plain-text-first-principles ## Context and Problem Statement -Every tool we use, an editor, a vault, a harness, a review bot, is short-lived next to the work it touches. Steph Ango's essay [File over app][FOA] (2023) states the test. A person's work goes into files they own, in open formats any tool reads, and the work outlasts the app. -Plain text, Markdown, metadata inside the file, and one source for every harness are four choices with one reason. This record states the reason, so a new tool has something to be measured against. +Every tool we use is short-lived next to the work it touches: an editor, a vault, a *[harness][HARNESS]*, a review bot. Steph Ango's essay [File over app][FOA] (2023) states the test. A person keeps their work in files they own, in open formats that any tool reads. The work then outlasts the app. +Plain text, Markdown, metadata inside the file, and one source for every [harness][HARNESS] are four choices with one reason. This record states the reason, so the owner can measure a new tool against it. ## Decision Drivers -- A harness, a vault, and a review bot each come and go, and the instructions and records stay +- A [harness][HARNESS], a vault, and a review bot each come and go, and the *[instructions][INSTRUCTION]* and records stay - A file that only one vendor's app can open is readable only as long as that vendor allows - Every tool on the roster reads files, so files are the one interface they all share ## Considered Options -1. App over file: keep the work where the tool keeps it, in a memory store, a database, or a vendor format, and export when needed. Fast to start, and every export loses something. +1. App over file: the tool keeps the work in its own place (a memory store, a database, or a vendor format). The person exports when needed. It is fast to start, and every export loses something. 2. File over app: the work is a file the person owns, in an open format, and every tool is a replaceable view over it. ## Decision Outcome -Option 2. Every artifact the deck governs MUST be a file the owner holds in the repository, in a format any tool reads without that tool's vendor. A tool MAY keep its own store, and that store MUST derive from the files and MUST be rebuildable from them. A tool that cannot work from the files does not join the roster. -This record is the reason behind the others: plain text ([CORE-0001](CORE-0001%20Markdown%20as%20System%20Language.md)), metadata inside the file ([CORE-0002](CORE-0002%20Metadata%20Inside%20Files.md)), the Open Knowledge Format bundle ([CORE-0006](CORE-0006%20Open%20Knowledge%20Format%20Bundle.md)), and one source for every harness. +Option 2. Every *[artifact][ARTIFACT]* the *[repository][REPOSITORY]* governs MUST be a file the owner keeps in the [repository][REPOSITORY]. The file's format MUST be one that any tool reads without that tool's vendor. A tool MAY keep its own store. That store MUST derive from the files, and it MUST be rebuildable from them. A tool that cannot work from the files does not join the roster. +This record is the reason behind the others. Those are plain text ([CORE-0001](CORE-0001%20Markdown%20as%20System%20Language.md)), metadata inside the file ([CORE-0002](CORE-0002%20Metadata%20Inside%20Files.md)), the Open Knowledge Format bundle ([CORE-0006](CORE-0006%20Open%20Knowledge%20Format%20Bundle.md)), and one source for every [harness][HARNESS]. ### Consequences - [+] A tool change is a view change. The files, their history, and their links stay. -- [+] A new tool is judged by one question: does it work from the files. -- [-] A tool-native feature that keeps state elsewhere, memory, a session index, a comment database, is either unused or a rebuildable cache. -- [-] A file can be text and still closed in practice. A compressed drawing payload is text no person reads, so an SVG snapshot sits beside it. +- [+] The owner judges a new tool by one question: does it work from the files. +- [-] A tool-native feature that keeps state elsewhere (memory, a session index, a comment database) is either unused or a rebuildable cache. +- [-] A file can be text and still closed in practice. A compressed drawing payload is text that no person reads, so the owner keeps an SVG (Scalable Vector Graphics) snapshot beside it. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [FOA]: https://stephango.com/file-over-app +[HARNESS]: ../specs/glossary/harness.md +[ARTIFACT]: ../specs/glossary/artifact.md +[REPOSITORY]: ../specs/glossary/repository.md +[INSTRUCTION]: ../specs/glossary/instruction.md diff --git a/docs/decisions/CORE-0009 Reference Citations.md b/docs/decisions/CORE-0009 Reference Citations.md index f2e99654..2e2acebf 100644 --- a/docs/decisions/CORE-0009 Reference Citations.md +++ b/docs/decisions/CORE-0009 Reference Citations.md @@ -9,7 +9,7 @@ tags: - citations status: accepted created: 2026-08-28 -updated: 2026-09-20 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,28 +27,35 @@ change: plain-text-first-principles ## Context and Problem Statement -Decision records and change proposals cite standards, blog posts, and tool documentation. The first proposals used numbered citations with a numbered Sources list. A bare number says nothing where it is used, and one insertion renumbers every later reference. +A *[decision record][DECISIONRECORD]* and a *[change][CHANGE]* proposal cite standards, blog posts, and tool documentation. The first proposals used numbered citations with a numbered Sources list. A bare number tells the reader nothing at the place where the author cites. One insertion renumbers every later reference. ## Decision Drivers -- A citation is readable where it is used, without a jump to the list -- Adding or removing a source renumbers nothing +- The reader understands a citation at the place where the author cites, without a jump to the list +- The author adds or removes a source without renumbering the rest - Plain Markdown that GitHub and Obsidian render -- One convention for decision records, proposals, and specifications +- One convention for [decision records][DECISIONRECORD], proposals, and specifications ## Considered Options -1. Numbered citations: `[1]` markers with a numbered Sources list. Compact, and a number says nothing, and an insertion renumbers everything. -2. Inline URLs: the full URL at each use. Readable nowhere, duplicated everywhere. -3. Reference-style links: `[Obsidian Properties][OBS-PROPS]` inline with one `[OBS-PROPS]: url` definition at the end of the file. Named where it is used, defined once. +1. Numbered citations: `[1]` markers with a numbered Sources list. The form is compact. A number tells the reader nothing, and one added source renumbers every later marker. +2. Inline URLs: the full URL at each use. The reader finds it readable nowhere, and the author duplicates it everywhere. +3. Reference-style links: `[Obsidian Properties][OBS-PROPS]` inline with one `[OBS-PROPS]: url` definition at the end of the file. The tag identifies the source where the author cites, and the author defines it once. ## Decision Outcome -Option 3. A document MUST cite an external source with a reference-style link: a short uppercase tag inline and one link definition at the end of the file. A change proposal keeps a Sources section that lists each source once with the same tags. +Option 3. The author MUST cite an external source with a reference-style link. The link is a short tag in upper case at the citation and one link definition at the end of the file. A [change][CHANGE] proposal keeps a Sources section. That section lists each source once with the same tags. ### Consequences -- [+] A citation names its source where it is used +- [+] The reader sees the source of a citation at the place where the author cites - [+] Sources come and go without renumbering - [+] Plain Markdown: GitHub, Obsidian, and lychee all read it -- [-] Tags have to stay unique within a file, and review checks that until a checker exists +- [-] A tag has to stay unique within a file. The reviewer checks that until a checker exists + +## More Information + +Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[CHANGE]: ../specs/glossary/change.md diff --git a/docs/decisions/CORE-0010 Adopt Architecture Decision Records.md b/docs/decisions/CORE-0010 Adopt Architecture Decision Records.md index 5071f794..6685f71b 100644 --- a/docs/decisions/CORE-0010 Adopt Architecture Decision Records.md +++ b/docs/decisions/CORE-0010 Adopt Architecture Decision Records.md @@ -8,7 +8,7 @@ tags: - process status: accepted created: 2026-02-19 -updated: 2026-09-20 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: [] @@ -24,34 +24,41 @@ change: keep-architecture-decision-records ## Context and Problem Statement -Decisions are made in conversations, threads, and commit messages. None of those is structured or durable. As a system grows, the reason for a structural choice becomes hard to recover. People and models then propose changes that contradict an earlier decision without knowing it. A model session also loses its reasoning at context compaction, so a decision must live outside the session. +People decide things in conversations, threads, and commit messages. None of those places gives a decision a structure or keeps it. As a system grows, the reason for a structural choice becomes hard to recover. A person or a *[model][MODEL]* then proposes a *[change][CHANGE]* that contradicts an earlier decision without knowing it. A [model][MODEL] session also loses its reasoning when the context compacts, so a decision must live outside the session. ## Decision Drivers -- An accepted decision must not be contradicted silently. -- Decision history must survive a move between repositories and tools, which commit messages do not. -- A record must be readable by a person, a model, and a note tool without conversion (CORE-0001 Markdown as System Language). +- A [change][CHANGE] must not contradict an accepted decision silently. +- The decision history must stay readable after a move between repositories and tools. Commit messages are lost in such a move. +- A person, a [model][MODEL], and a note tool must read a record without conversion ([CORE-0001 Markdown as System Language](CORE-0001%20Markdown%20as%20System%20Language.md)). ## Considered Options -1. Commit messages only. They are searchable, and they have no structure and no status. -2. A Decisions section inside project documents. It keeps context close, and it has no schema and grows without limit. -3. One Markdown file for each decision in `docs/decisions/`, with frontmatter, a status, and a numbered filename. +1. Commit messages only. A person can search them, and they have no structure and no *[status][STATUS]*. +2. A Decisions section inside each project document. It keeps the context close, and it has no schema and grows without limit. +3. One Markdown file for each decision in `docs/decisions/`, with frontmatter, a [status][STATUS], and a numbered filename. ## Decision Outcome -Option 3. A decision with alternatives MUST be one record in `docs/decisions/`. A record MUST carry a status from proposed to accepted to superseded. The directory schema MUST validate the record shape. +Option 3. The author MUST write a decision with alternatives as one *[decision record][DECISIONRECORD]* in `docs/decisions/`. A record MUST state a [status][STATUS] that moves from proposed to accepted to superseded. The *[validator][VALIDATOR]* MUST check each record against the directory's *[structure schema][STRUCTURESCHEMA]*. ### Consequences -- [+] One directory listing shows every decision. -- [+] The status field makes a contradiction of an accepted decision visible at review. -- [-] Each record costs authoring time. A change that weighed no alternative still writes a short record, because the record states what the change wants. +- [+] One listing of the directory shows every decision. +- [+] A reviewer sees from the [status][STATUS] field when a [change][CHANGE] contradicts an accepted decision. +- [-] Each record costs authoring time. A [change][CHANGE] that weighed no alternative still writes a short record, because the record states what the [change][CHANGE] is for. ## More Information - [Documenting Architecture Decisions][NYGARD] - [MADR][MADR] +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [NYGARD]: https://www.cognitect.com/blog/2011/11/15/documenting-architecture-decisions "Michael Nygard, Documenting Architecture Decisions, 2011" [MADR]: https://adr.github.io/madr/ "Markdown Architectural Decision Records" +[MODEL]: ../specs/glossary/model.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[STATUS]: ../specs/glossary/status.md +[VALIDATOR]: ../specs/glossary/validator.md +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md +[CHANGE]: ../specs/glossary/change.md diff --git a/docs/decisions/CORE-0011 ADR Template Choice.md b/docs/decisions/CORE-0011 ADR Template Choice.md index 3757f0f1..c82b205a 100644 --- a/docs/decisions/CORE-0011 ADR Template Choice.md +++ b/docs/decisions/CORE-0011 ADR Template Choice.md @@ -9,7 +9,7 @@ tags: - structured-madr status: accepted created: 2026-02-19 -updated: 2026-09-22 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -26,36 +26,43 @@ change: ["keep-architecture-decision-records", "decision-records-match-template" ## Context and Problem Statement -[CORE-0010](CORE-0010%20Adopt%20Architecture%20Decision%20Records.md) adopts records and leaves the template open. Several formats exist: the original Nygard record, MADR, Y-Statements, and structured MADR. The template must be short enough for a small decision and complete enough for a large one. Its frontmatter must be checked by a machine. +[CORE-0010](CORE-0010%20Adopt%20Architecture%20Decision%20Records.md) adopts *[decision records][DECISIONRECORD]* and leaves the template open. Several formats exist: the original Nygard *[architecture decision record][ARCHITECTUREDECISIONRECORD]*, MADR (the *[markdown architecture decision record][MARKDOWNARCHITECTUREDECISIONRECORD]*), Y-Statements, and structured MADR. The template has to be short enough for a small decision and complete enough for a large one. A machine has to check its frontmatter. ## Decision Drivers -- The considered options must be recorded. The Nygard record omits them. -- Frontmatter must be machine-readable and validated before a commit. +- The author must record the considered options. The Nygard record omits them. +- A machine must read the frontmatter, and the *[validator][VALIDATOR]* must check it before a commit. - A record must name who approves the decision. ## Considered Options 1. The Nygard record: context, decision, status, consequences. It has no options list and no frontmatter. -2. Full MADR: adds decision drivers, options with pros and cons, and confirmation. It is thorough and heavy. +2. Full MADR: it adds decision drivers and a confirmation section, and each option lists its pros and cons. It is thorough and heavy. 3. Y-Statements: one sentence for each decision. A complex decision does not fit. -4. [Structured MADR][SMADR]: the MADR body with machine-readable YAML frontmatter. +4. [Structured MADR][SMADR]: the *[structured architecture decision record][STRUCTUREDARCHITECTUREDECISIONRECORD]*, the MADR body with YAML frontmatter that a machine reads. ## Decision Outcome -Option 4. A record MUST have these sections in order: Context and Problem Statement, Considered Options, and Decision Outcome, with Consequences as a subsection of Decision Outcome. Decision Drivers and More Information are optional. The `docs/decisions/.mdschema` file MUST validate the frontmatter fields and the heading order. [CORE-0012](CORE-0012%20MADR%20Frontmatter%20Extensions.md) states the added fields. +Option 4. A record MUST have these sections in this order: Context and Problem Statement, Considered Options, and Decision Outcome. Consequences is a subsection of Decision Outcome. Decision Drivers and More Information are optional. The [validator][VALIDATOR] MUST check the frontmatter fields and the heading order against the *[structure schema][STRUCTURESCHEMA]* at `docs/decisions/.mdschema`. [CORE-0012](CORE-0012%20MADR%20Frontmatter%20Extensions.md) states the added fields. ### Consequences -- [+] The schema rejects a record with a missing field before it reaches review. -- [-] The template is heavier than the Nygard record. A small decision still fills each required section. -- [-] The deck validates with its own schema file, not with the upstream JSON Schema, so an upstream field change does not reach the deck by itself. +- [+] The [validator][VALIDATOR] rejects a record with a missing field before the record reaches review. +- [-] The template is heavier than the Nygard record. The author of a small decision still fills each required section. +- [-] The [validator][VALIDATOR] reads the repository's own schema file instead of the upstream JSON Schema. An upstream field change does not reach the repository by itself. ## More Information - [Structured MADR][SMADR] - [MADR Template Primer][PRIMER] -- 2026-09-22: the outcome listed Consequences as a fourth top-level section. The runeADR template puts it under Decision Outcome, and 31 records had followed the wrong sentence. The decision-records-match-template change corrected the sentence and the records. +- 2026-09-22: the outcome listed Consequences as a fourth top-level section. The runeADR template puts it under Decision Outcome. Thirty-one records had matched the wrong sentence. The decision-records-match-template change corrected the sentence and the records. +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [SMADR]: https://github.com/zircote/structured-madr "structured-madr, schema and validator" [PRIMER]: https://www.ozimmer.ch/practices/2022/11/22/MADRTemplatePrimer.html "Olaf Zimmermann, MADR Template Primer, 2022" +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[ARCHITECTUREDECISIONRECORD]: ../specs/glossary/architecture-decision-record.md +[MARKDOWNARCHITECTUREDECISIONRECORD]: ../specs/glossary/markdown-architecture-decision-record.md +[STRUCTUREDARCHITECTUREDECISIONRECORD]: ../specs/glossary/structured-architecture-decision-record.md +[VALIDATOR]: ../specs/glossary/validator.md +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md diff --git a/docs/decisions/CORE-0012 MADR Frontmatter Extensions.md b/docs/decisions/CORE-0012 MADR Frontmatter Extensions.md index cf78abff..da7db3d1 100644 --- a/docs/decisions/CORE-0012 MADR Frontmatter Extensions.md +++ b/docs/decisions/CORE-0012 MADR Frontmatter Extensions.md @@ -9,7 +9,7 @@ tags: - frontmatter status: accepted created: 2026-03-30 -updated: 2026-09-20 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,40 +27,53 @@ change: keep-architecture-decision-records ## Context and Problem Statement -[CORE-0011](CORE-0011%20ADR%20Template%20Choice.md) adopts structured MADR. Its frontmatter does not say who approves a decision, where an adapted decision came from, or which change delivered it. Structured MADR permits added fields when they carry an `x-` prefix, and a strict validator rejects an added field without one. +A *[decision record][DECISIONRECORD]* needs facts that *[structured MADR][STRUCTUREDARCHITECTUREDECISIONRECORD]* does not define. MADR is short for *[Markdown Architecture Decision Record][MARKDOWNARCHITECTUREDECISIONRECORD]*. [CORE-0011](CORE-0011%20ADR%20Template%20Choice.md) adopts [structured MADR][STRUCTUREDARCHITECTUREDECISIONRECORD]. Its frontmatter does not say who approves a decision, where an adapted decision came from, or which *[change][CHANGE]* delivered it. [Structured MADR][STRUCTUREDARCHITECTUREDECISIONRECORD] permits an added field when the field has an `x-` prefix. A strict *[validator][VALIDATOR]* of that format rejects an added field without one. ## Considered Options -1. No added fields. Accountability and provenance stay in the body as prose, where no check reads them. -2. Prefixed fields only, such as `x-rune-responsible`. A strict validator accepts them, and every record becomes harder to read. -3. Bare field names only. Records read well, and a strict structured MADR validator rejects them. -4. Both forms: a bare canonical name and a compliant `x-rune-` long form for each added field. +1. No added fields. The author keeps accountability and provenance in the body as prose, where no check reads them. +2. Prefixed fields only, such as `x-rune-responsible`. A strict [validator][VALIDATOR] accepts them, and every record becomes harder to read. +3. Bare field names only. A record reads well, and a strict [validator][VALIDATOR] of [structured MADR][STRUCTUREDARCHITECTUREDECISIONRECORD] rejects it. +4. Both forms. Each added field has a bare canonical name and a compliant `x-rune-` long form. ## Decision Outcome -Option 4. The bare name is canonical, and records in the deck use it. The long form exists so that a record can pass a strict structured MADR validator without a mapping step. +Option 4. The bare name is canonical, and the records in the *[repository][REPOSITORY]* use it. The long form exists so that a record can pass a strict [validator][VALIDATOR] of [structured MADR][STRUCTUREDARCHITECTUREDECISIONRECORD] without a mapping step. -| Canonical | Long form | Type | Meaning | +| Canonical | Long form | Kind | Meaning | |---|---|---|---| | `responsible` | `x-rune-responsible` | list | who does the work | | `accountable` | `x-rune-accountable` | list | who approves the decision | -| `consulted` | `x-rune-consulted` | list | whose input was used, people and models | -| `informed` | `x-rune-informed` | list | who is told the outcome | -| `upstream` | `x-rune-upstream` | list | sources the decision was adapted from | +| `consulted` | `x-rune-consulted` | list | whose input the author used, people and *[models][MODEL]* | +| `informed` | `x-rune-informed` | list | who hears the outcome | +| `upstream` | `x-rune-upstream` | list | the sources the author adapted the decision from | | `related` | `x-rune-related` | list | neighbor records by id and title | -| `change` | `x-rune-change` | string or list | the change id, or ids, that delivered the decision | +| `change` | `x-rune-change` | string or list | the [change][CHANGE] id, or ids, that delivered the decision | -- A record MUST carry `responsible`, `accountable`, and `upstream`, each in one form. An empty list is a statement, never an omission. +- A record MUST have `responsible`, `accountable`, and `upstream`, each in one form. An empty list is a statement and never an omission. - A record MUST NOT set both forms of one field. - `x-rune-` is the only accepted prefix. A field with another `x-` prefix MUST fail the check. -- A record MAY carry other fields without an `x-` prefix when they do not conflict with these. A check MUST NOT reject them. +- A record MAY have other fields without an `x-` prefix when they do not conflict with these. A check MUST NOT reject them. - The frontmatter MUST be enough to rebuild the record's location: `type`, `project`, and the id in the title give the path. -`scripts/check-decision-fields` enforces the first three rules. The directory schema marks the three required fields optional, because a schema cannot state that either form satisfies a field. +The field check, `scripts/check-decision-fields`, enforces the first three constraints. The *[structure schema][STRUCTURESCHEMA]* of the directory marks the three required fields optional, because a schema cannot state that either form satisfies a field. ### Consequences -- [-] A record written with bare names does not pass a strict structured MADR validator. A record written with the long form does. -- [-] Two forms for one field are two ways to write the same fact. The check forbids both at once, and the deck's own records stay on the bare form. -- [+] The `change` field makes the link from a record to its change a fact a check can read. -- [-] The public format home states the same rule in its README, and its JSON Schema still requires the bare names. A long-form record fails that schema until the home changes it. +- [-] A record written with bare names does not pass a strict [validator][VALIDATOR] of [structured MADR][STRUCTUREDARCHITECTUREDECISIONRECORD]. A record written with the long form does. +- [-] Two forms for one field are two ways to write the same fact. The check rejects a record that sets both, and the [repository][REPOSITORY]'s own records stay on the bare form. +- [+] The `change` field makes the link from a record to its [change][CHANGE] a fact a check can read. +- [-] The public format home states the same constraint in its README, and its JSON Schema still requires the bare names. A long-form record fails that schema until the home revises it. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[CHANGE]: ../specs/glossary/change.md +[VALIDATOR]: ../specs/glossary/validator.md +[REPOSITORY]: ../specs/glossary/repository.md +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md +[MARKDOWNARCHITECTUREDECISIONRECORD]: ../specs/glossary/markdown-architecture-decision-record.md +[STRUCTUREDARCHITECTUREDECISIONRECORD]: ../specs/glossary/structured-architecture-decision-record.md +[MODEL]: ../specs/glossary/model.md diff --git a/docs/decisions/CORE-0013 Context Economy.md b/docs/decisions/CORE-0013 Context Economy.md index 0cf33df9..3a8f4889 100644 --- a/docs/decisions/CORE-0013 Context Economy.md +++ b/docs/decisions/CORE-0013 Context Economy.md @@ -9,7 +9,7 @@ tags: - rules status: accepted created: 2026-08-27 -updated: 2026-08-27 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -28,29 +28,29 @@ change: economy-measure-context-efficiency ## Context and Problem Statement -Anthropic removed over 80 percent of the Claude Code system prompt for the Claude 5 generation with no measured loss. The deck was authored under the older assumption that more instruction means more control. The audit at main `34b08e54` counts 2232 words across sixteen core rules and 101 duplicated sentences across runes. Every deployed session pays for this text, and every duplicate is a future conflict. +Anthropic removed over 80 percent of the Claude Code system prompt for the Claude 5 generation with no measured loss. The owner wrote the *[deck][DECK]* under the older assumption that more guidance means more control. The audit at main `34b08e54` counts 2232 words across sixteen core *[rules][RULE]* and 101 duplicated sentences across *[runes][RUNE]*. Every deployed session pays for this text, and every duplicate is a future conflict. ## Considered Options -1. Keep long rules and rely on review to prune them. -2. Adopt context economy as a decided norm with deterministic gates. +1. Keep long [rules][RULE] and rely on a reviewer to prune them. +2. Adopt context economy as a decided norm, with deterministic checks that refuse an overrun. ## Decision Outcome -Chosen option: context economy as a decided norm. +Option 2, context economy as a decided norm. -- Every instruction earns its resident tokens. A rule states one imperative instruction within 50 words. -- One home per fact. Enforcement lives in hooks and CI. Knowledge lives in skills. Constraints live in rules. Identity and gotchas live in the repository brief. Transient state lives in memory. A fact in two layers is a defect. -- Judgment over rulebooks. Trust the model with context and intent. Reserve hard constraints for invariants and safety. -- Progressive disclosure. Situational content loads on demand through skills and companions, never up front. -- Interfaces over examples. An expressive tool or schema teaches use better than a usage example. -- Verification beats instruction. A way to check output outranks another paragraph of guidance. +- Each piece of guidance stays only when its effect is worth the tokens it occupies in every session. A [rule][RULE] states one imperative statement within 50 words. +- Each fact has one home. Enforcement goes in *[hooks][HOOK]* and continuous integration, knowledge in *[skills][SKILL]*, and constraints in [rules][RULE]. Identity and gotchas go in the *[repository][REPOSITORY]* brief, and transient state in memory. A fact in two layers is a defect. +- The author trusts the judgment of the *[model][MODEL]* over a rulebook. The author gives it context and intent, and reserves a hard constraint for an invariant or for safety. +- Progressive disclosure: situational content loads on demand, through [skills][SKILL] and *[companions][COMPANION]*, and never up front. +- Interfaces over examples: an expressive tool or schema shows correct use better than a usage example. +- A way to check the output is worth more than another paragraph of guidance, so the author adds a check instead of a paragraph. ### Consequences -- [+] The rule corpus shrinks toward a 900-word budget with gates that keep it there. +- [+] The [rule][RULE] corpus shrinks toward a 900-word budget, and checks that refuse an overrun keep it there. - [+] Duplicated guidance retires instead of drifting. -- [-] Compression can drop a real constraint, so displaced content moves to a skill before deletion. +- [-] Compression can drop a real constraint, so the author moves displaced content to a [skill][SKILL] before deleting it. ## More Information @@ -58,20 +58,53 @@ Compatibility of the forge-core decisions with this norm: | Record | Verdict | Note | | --- | --- | --- | -| CORE-0001 Markdown as System Language | affirmed | unchanged | -| CORE-0002 Metadata Inside Files | affirmed | deck sidecars for sealed evidence stay a recorded exception | -| CORE-0003 YAML Frontmatter | affirmed | unchanged | -| CORE-0004 Adopt ADRs | affirmed | this pass exercises it | -| CORE-0005 ADR Template Choice | affirmed | compact bodies preferred under this norm | -| CORE-0006 Directories Direct | affirmed | naming as configuration matches interfaces over examples | -| CORE-0007 Forge MADR Extensions | affirmed | unchanged | -| CORE-0008 Variables in Markdown | affirmed | unchanged | -| CORE-0009 YAML Configuration | affirmed | unchanged | -| CORE-0010 Unified Module Validation | amended | gains the chosen linter set on migration: one linter per surface, single binary, offline on the commit path, changes by record | -| CORE-0011 Verified Remote Execution | affirmed | deck CI pins action references by commit and verifies release binaries by SHA-256. Reusable workflow references and package installs remain compliance gaps | -| CORE-0012 Fixture-Based Canary Testing | affirmed | the ontology smoke fixture follows it | -| ARCH-0001 Skills Agents and Rules | amended | agents are containers, never personas, per CORE-0014 | -| ARCH-0002 Skills Companion Files | affirmed | progressive disclosure in practice | -| ARCH-0004 Rules as Shared Conventions | amended | shared stays, shape changes to one instruction within 50 words | -| ARCH-0011 Hook Design Principles | affirmed | strengthened: prose demotes to hooks wherever a check can be code | -| ARCH-0014 Per-Tool Memory Files | diverged | the deck authors once and assembles per tool, so accepted duplication ends | +| [CORE-0001 Markdown as System Language][FC-CORE-0001] | affirmed | unchanged | +| [CORE-0002 Metadata Inside Files][FC-CORE-0002] | affirmed | [deck][DECK] *[sidecars][SIDECAR]* for sealed evidence stay a recorded exception | +| [CORE-0003 YAML Frontmatter][FC-CORE-0003] | affirmed | unchanged | +| [CORE-0004 Adopt ADRs][FC-CORE-0004] | affirmed | this pass exercises it | +| [CORE-0005 ADR Template Choice][FC-CORE-0005] | affirmed | compact bodies preferred under this norm | +| [CORE-0006 Directories Direct][FC-CORE-0006] | affirmed | naming as configuration matches interfaces over examples | +| [CORE-0007 Forge MADR Extensions][FC-CORE-0007] | affirmed | unchanged | +| [CORE-0008 Variables in Markdown][FC-CORE-0008] | affirmed | unchanged | +| [CORE-0009 YAML Configuration][FC-CORE-0009] | affirmed | unchanged | +| [CORE-0010 Unified Module Validation][FC-CORE-0010] | amended | gains the chosen *[linter][LINTER]* set on migration: one [linter][LINTER] for each concern, one binary, offline on the commit path, revisions by record | +| [CORE-0011 Verified Remote Execution][FC-CORE-0011] | affirmed | the [repository][REPOSITORY]'s CI pins action references by commit and verifies release binaries by SHA-256. Reusable workflow references and package installs remain compliance gaps | +| [CORE-0012 Fixture-Based Canary Testing][FC-CORE-0012] | affirmed | the ontology smoke *[fixture][FIXTURE]* follows it | +| [ARCH-0001 Skills Agents and Rules][FC-ARCH-0001] | amended | *[agents][AGENT]* are containers, never personas, per CORE-0014 | +| [ARCH-0002 Skills Companion Files][FC-ARCH-0002] | affirmed | progressive disclosure in practice | +| [ARCH-0004 Rules as Shared Conventions][FC-ARCH-0004] | amended | shared stays, the shape becomes one imperative statement within 50 words | +| [ARCH-0011 Hook Design Principles][FC-ARCH-0011] | affirmed | strengthened: prose demotes to [hooks][HOOK] wherever a check can be code | +| [ARCH-0014 Per-Tool Memory Files][FC-ARCH-0014] | diverged | the owner authors the [deck][DECK] once and the *[assembler][ASSEMBLER]* assembles it per tool, so accepted duplication ends | + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[DECK]: ../specs/glossary/deck.md +[RULE]: ../specs/glossary/rule.md +[RUNE]: ../specs/glossary/rune.md +[HOOK]: ../specs/glossary/hook.md +[SKILL]: ../specs/glossary/skill.md +[REPOSITORY]: ../specs/glossary/repository.md +[MODEL]: ../specs/glossary/model.md +[COMPANION]: ../specs/glossary/companion.md +[ASSEMBLER]: ../specs/glossary/assembler.md +[SIDECAR]: ../specs/glossary/sidecar.md +[LINTER]: ../specs/glossary/linter.md +[FIXTURE]: ../specs/glossary/fixture.md +[AGENT]: ../specs/glossary/agent.md +[FC-CORE-0001]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0001%20Markdown%20as%20System%20Language.md +[FC-CORE-0002]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0002%20Metadata%20Inside%20Files.md +[FC-CORE-0003]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0003%20YAML%20Frontmatter%20for%20Machine%20Readability.md +[FC-CORE-0004]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0004%20Adopt%20Architecture%20Decision%20Records.md +[FC-CORE-0005]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0005%20ADR%20Template%20Choice.md +[FC-CORE-0006]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0006%20Directories%20Direct.md +[FC-CORE-0007]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0007%20Forge%20MADR%20Extensions.md +[FC-CORE-0008]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0008%20Variables%20in%20Markdown%20Instructions%20and%20Templates.md +[FC-CORE-0009]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0009%20YAML%20Configuration%20Files.md +[FC-CORE-0010]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0010%20Unified%20Module%20Validation.md +[FC-CORE-0011]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0011%20Verified%20Remote%20Execution.md +[FC-CORE-0012]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/CORE-0012%20Fixture-Based%20Canary%20Testing.md +[FC-ARCH-0001]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/ARCH-0001%20Skills%20Agents%20and%20Rules.md +[FC-ARCH-0002]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/ARCH-0002%20Skills%20Companion%20Files.md +[FC-ARCH-0004]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/ARCH-0004%20Rules%20as%20Shared%20Ecosystem%20Conventions.md +[FC-ARCH-0011]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/ARCH-0011%20Hook%20Design%20Principles.md +[FC-ARCH-0014]: https://github.com/N4M3Z/forge-core/blob/main/docs/decisions/ARCH-0014%20Per-Tool%20Memory%20Files.md diff --git a/docs/decisions/CORE-0014 No Performance Personas.md b/docs/decisions/CORE-0014 No Performance Personas.md index e2aff77c..93fa229e 100644 --- a/docs/decisions/CORE-0014 No Performance Personas.md +++ b/docs/decisions/CORE-0014 No Performance Personas.md @@ -9,7 +9,7 @@ tags: - personas status: accepted created: 2026-08-27 -updated: 2026-08-27 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,24 +27,36 @@ change: economy-measure-context-efficiency ## Context and Problem Statement -A controlled study (Zheng et al., EMNLP 2024 Findings, 162 personas, 4 model families, 2410 questions) shows personas in system prompts do not improve performance over a no-persona control. The best role per question is idiosyncratic, and follow-up work finds persona framing can reduce factual accuracy. Forge-core ARCH-0001 defines agents as specialist personas, so the record needs an amendment for the deck. +A controlled study shows that personas in system prompts do not improve performance over a no-persona control. The study is Zheng et al., Empirical Methods in Natural Language Processing (EMNLP) 2024 Findings, with 162 personas, 4 *[model][MODEL]* families, and 2410 questions. The best role per question is idiosyncratic, and follow-up work finds that persona framing can reduce factual accuracy. Forge-core ARCH-0001 defines *[agents][AGENT]* as specialist personas, so the record needs an amendment for the *[deck][DECK]*. ## Considered Options -1. Keep persona framing in agents and skills as harmless flavor. -2. Ban performance personas and define agents as execution containers. +1. Keep persona framing in [agents][AGENT] and *[skills][SKILL]* as harmless flavor. +2. Ban performance personas and define [agents][AGENT] as execution containers. ## Decision Outcome Chosen option: ban performance personas. -- A rune frames capability through context, tools, and verifiable procedure. A rune does not claim expertise as capability. -- An agent is an execution container: a fresh context window, a tool allowlist, and a model choice. The container is the value. The persona text is not. -- Voice guidance for outward-facing text is a register choice and stays permitted. It carries no capability claim. -- An exception needs a bench verdict, never a preference. +- A *[rune][RUNE]* frames capability through context, tools, and verifiable procedure. It does not claim expertise as capability. +- An [agent][AGENT] is an execution container: a fresh context window, a tool allowlist, and a [model][MODEL] choice. The container is the value. The persona text adds none. +- Voice guidance for outward-facing text is a register choice and remains permitted. It makes no capability claim. +- An exception needs a bench *[verdict][VERDICT]*. A preference never justifies one. ### Consequences -- [+] The persona lint becomes a deterministic gate at warning severity. -- [+] Agent definitions shrink to the fields that change behavior. -- [-] Existing avatar and voice artifacts need a review against the carve-out. +- [+] The persona lint becomes a deterministic check at warning severity. +- [+] [Agent][AGENT] definitions shrink to the fields that alter behavior. +- [-] Existing avatar and voice *[artifacts][ARTIFACT]* need a review against the carve-out. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[MODEL]: ../specs/glossary/model.md +[AGENT]: ../specs/glossary/agent.md +[DECK]: ../specs/glossary/deck.md +[SKILL]: ../specs/glossary/skill.md +[RUNE]: ../specs/glossary/rune.md +[VERDICT]: ../specs/glossary/verdict.md +[ARTIFACT]: ../specs/glossary/artifact.md diff --git a/docs/decisions/CORE-0015 Positive Instruction.md b/docs/decisions/CORE-0015 Positive Instruction.md index d6da2d23..e2e4a54c 100644 --- a/docs/decisions/CORE-0015 Positive Instruction.md +++ b/docs/decisions/CORE-0015 Positive Instruction.md @@ -9,7 +9,7 @@ tags: - prompting status: accepted created: 2026-08-27 -updated: 2026-08-27 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,23 +27,33 @@ change: economy-measure-context-efficiency ## Context and Problem Statement -Anthropic guidance recommends stating the wanted behavior, because a model continues the conversation it is given, and a forceful prohibition can prime the behavior it names. The deck audit counts twenty negative constructions across the sixteen core rules, many of which restate a preference rather than a prohibition. +Anthropic's guidance recommends stating the wanted behavior. It does so because a *[model][MODEL]* continues the conversation in front of it, and a forceful prohibition can prime the behavior it names. An audit of the *[deck][DECK]* counted twenty negative constructions across its sixteen core *[rules][RULE]*. Many of them restate a preference and prohibit nothing. ## Considered Options 1. Leave phrasing to each author's taste. -2. Decide positive instruction as the norm with a negation-density gate. +2. Make positive instruction the norm, and add a lint that measures negation density. ## Decision Outcome -Chosen option: positive instruction as the norm. +Option 2. Positive instruction is the norm. -- An instruction states the wanted behavior: `write plain prose` rather than a markdown prohibition. -- Negation stays reserved for true prohibitions: safety boundaries, security invariants, and destructive actions. -- The negation-density lint fires at warning severity when a changed rune exceeds the threshold, so real prohibitions survive and habitual negation retires. +- Each directive states the wanted behavior, as in `write plain prose`, in place of a prohibition of markdown. +- The author reserves negation for a true prohibition: a safety boundary, a security invariant, or a destructive action. +- The negation-density lint reports a warning when a changed *[rune][RUNE]* exceeds the threshold, so a real prohibition survives and habitual negation leaves the [rune][RUNE]. ### Consequences -- [+] Rules read as direction instead of a minefield. -- [+] The rewrite in the economy-measure-context-efficiency change has a measurable target. -- [-] Some prohibitions need careful positive rewording to keep their exact scope. +- [+] A [rule][RULE] tells the [model][MODEL] what to do instead of listing what to avoid. +- [+] The rewrite in the economy-measure-context-efficiency *[change][CHANGE]* has a measurable goal. +- [-] Some prohibitions need a careful rewording in positive form to keep their exact scope. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[MODEL]: ../specs/glossary/model.md +[DECK]: ../specs/glossary/deck.md +[RULE]: ../specs/glossary/rule.md +[RUNE]: ../specs/glossary/rune.md +[CHANGE]: ../specs/glossary/change.md diff --git a/docs/decisions/CORE-0016 Adversarial Review over Councils.md b/docs/decisions/CORE-0016 Adversarial Review over Councils.md index 7b9b7ffd..2c964119 100644 --- a/docs/decisions/CORE-0016 Adversarial Review over Councils.md +++ b/docs/decisions/CORE-0016 Adversarial Review over Councils.md @@ -1,6 +1,6 @@ --- title: Adversarial Review over Councils -description: Refutation against the source replaces consensus panels as the verification gate, and cross-provider fan-out stays a research instrument +description: Refutation against the source replaces consensus panels as the verification check, and cross-provider fan-out stays a research instrument type: adr category: architecture tags: @@ -9,7 +9,7 @@ tags: - agents status: accepted created: 2026-08-27 -updated: 2026-08-27 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -26,26 +26,32 @@ change: economy-measure-context-efficiency ## Context and Problem Statement -The council pattern fans one question to several providers and synthesizes by agreement. A consensus panel averages independent guesses and rewards fluent agreement, so a plausible but wrong finding can survive on style. Verification needs a mechanism that kills wrong findings on evidence. +The council pattern sends one question to several providers and combines the answers by agreement. A consensus panel averages independent guesses and rewards fluent agreement, so a plausible but wrong finding can survive on style. Verification needs a mechanism that kills a wrong finding on evidence. ## Considered Options -1. Keep councils as the verification mechanism. -2. Replace councils with an adversarial reviewer that attempts to refute each finding. +1. The owner keeps councils as the verification mechanism. +2. The owner replaces councils with an adversarial reviewer that tries to refute each finding. ## Decision Outcome Chosen option: adversarial review. - An adversarial reviewer attacks one claim against the source. -- The reviewer accepts the claim only after a completed review finds no counterexample. -- A timeout, crash, cancellation, or missing result is a reviewer fault. A reviewer fault never accepts the claim. -- A consensus council never gates a decision. -- Cross-provider fan-out stays available for research questions that want breadth. -- The AdversarialReviewer agent owns refutation. +- The reviewer accepts the claim only after it completes the review and fails to find a counterexample. +- A timeout, a crash, a cancellation, or a missing result is a reviewer fault. A reviewer fault never accepts the claim. +- A consensus council never admits or refuses a decision. +- The owner keeps cross-provider fan-out for a research question that wants breadth. +- The AdversarialReviewer *[agent][AGENT]* owns refutation. ### Consequences -- [+] Plausible but wrong findings die on evidence instead of surviving on agreement. -- [+] Verification cost concentrates on claims that matter. -- [-] A single reviewer can miss a failure mode a diverse panel would catch, so hard claims may get several adversarial lenses, never a vote. +- [+] A plausible but wrong finding dies on evidence instead of surviving on agreement. +- [+] The reviewer spends its effort on the claims that matter. +- [-] One reviewer can miss a failure mode that a diverse panel would catch, so a hard claim may get several adversarial reviewers with different angles. It never gets a vote. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[AGENT]: ../specs/glossary/agent.md diff --git a/docs/decisions/CORE-0017 The Inference Turn.md b/docs/decisions/CORE-0017 The Inference Turn.md index bbd1515e..17fecbe0 100644 --- a/docs/decisions/CORE-0017 The Inference Turn.md +++ b/docs/decisions/CORE-0017 The Inference Turn.md @@ -9,7 +9,7 @@ tags: - pipeline status: accepted created: 2026-08-27 -updated: 2026-08-27 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -26,7 +26,7 @@ change: economy-measure-context-efficiency ## Context and Problem Statement -Authoring decisions argue about token cost without a shared model of when each artifact reaches the model. The deck needs one record of the inference pipeline, so every artifact discussion can name the layer it changes and the price it pays. +The *[repository][REPOSITORY]* needs one record of the inference pipeline, so every *[artifact][ARTIFACT]* discussion can state the layer it alters and the price it pays. Authoring decisions argue about token cost without a shared account of when each [artifact][ARTIFACT] reaches the *[model][MODEL]*. ## Considered Options @@ -37,20 +37,30 @@ Authoring decisions argue about token cost without a shared model of when each a Chosen option: record the pipeline. -Session start loads the resident set once: the harness system prompt, the settings cascade as behavior, the memory chain (user memory, user rules, the repository brief, deployed rules), the auto-memory index, and the skill listing as names with descriptions. Deferred tools cost nothing until a search loads them. +Session start loads the resident set once. The resident set contains the *[harness][HARNESS]* system prompt, the settings cascade as behavior, and the memory chain. The memory chain is user memory, user *[rules][RULE]*, the [repository][REPOSITORY] brief, and deployed [rules][RULE]. The resident set also contains the auto-memory index and the *[skill][SKILL]* listing as *[names][NAME]* with *[descriptions][DESCRIPTION]*. Deferred tools cost nothing until a search loads them. -Each turn can invoke the event layer: prompt-submit hooks, pre-tool gates, sandbox enforcement, post-tool feedback, and stop hooks. +Each turn can invoke the event layer: prompt-submit *[hooks][HOOK]*, pre-tool checks, sandbox enforcement, post-tool feedback, and stop [hooks][HOOK]. Prompt-submit [hooks][HOOK] and post-tool feedback can add payloads that the [model][MODEL] sees. The other entries perform runtime work and add tokens only when they emit a diagnostic that the [model][MODEL] sees. -Prompt-submit hooks and post-tool feedback can add model-visible payloads. The other entries perform runtime work and add tokens only when they emit a model-visible diagnostic. +Compaction rebuilds the context from a summary. Critical constraints need re-injection at that boundary, and a PostCompact [hook][HOOK] is the mechanism. -Compaction rebuilds the context from a summary. Critical constraints need re-injection at that boundary, and a PostCompact hook is the mechanism. - -The layer map from CORE-0013 assigns each fact its home. The resident set adds tokens to every inference turn. - -The event layer always pays its runtime cost. It adds input tokens only for model-visible payloads. The on-demand layer adds cost only when loaded. +The layer map from [CORE-0013 Context Economy](CORE-0013%20Context%20Economy.md) assigns each fact its home. The resident set adds tokens to every inference turn. The event layer always incurs its runtime cost, and it adds input tokens only for payloads that the [model][MODEL] sees. The on-demand layer adds cost only when the session loads it. ### Consequences -- [+] Every authoring debate can name the layer and its price. +- [+] Every authoring debate can state the layer and its price. - [+] The compaction boundary is a recorded gap with a named mechanism. -- [-] The record needs an update when the harness changes its loading model. +- [-] The record needs an update when the [harness][HARNESS] alters its loading behavior. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[ARTIFACT]: ../specs/glossary/artifact.md +[MODEL]: ../specs/glossary/model.md +[HARNESS]: ../specs/glossary/harness.md +[RULE]: ../specs/glossary/rule.md +[REPOSITORY]: ../specs/glossary/repository.md +[SKILL]: ../specs/glossary/skill.md +[HOOK]: ../specs/glossary/hook.md +[NAME]: ../specs/glossary/name.md +[DESCRIPTION]: ../specs/glossary/description.md diff --git a/docs/decisions/CORE-0019 Spec-Driven Change Lifecycle.md b/docs/decisions/CORE-0019 Spec-Driven Change Lifecycle.md index b717c1f3..897a46dd 100644 --- a/docs/decisions/CORE-0019 Spec-Driven Change Lifecycle.md +++ b/docs/decisions/CORE-0019 Spec-Driven Change Lifecycle.md @@ -9,7 +9,7 @@ tags: - openspec status: accepted created: 2026-08-27 -updated: 2026-09-20 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -28,29 +28,43 @@ change: lifecycle-of-a-change ## Context and Problem Statement -Decision records capture why a choice stands, and nothing captured what must hold. Behavior lived in prose, drifted with it, and review had no target to test. The deck needs one change lifecycle in which requirements are stated, validated, and merged into one canonical truth. +The *[repository][REPOSITORY]* needs one *[change][CHANGE]* *[lifecycle][LIFECYCLE]*. In it, an author states each *[requirement][REQUIREMENT]*, a check validates it, and an archive merges it into one canonical tree. *[Decision records][DECISIONRECORD]* capture why a choice stands, and nothing captured what must hold. Behavior sat in prose, drifted with it, and review had nothing to test. ## Considered Options -1. A free-form design document for each change. -2. The [OpenSpec][OPENSPEC] lifecycle: a proposal with Why and What Changes, a delta specification for each capability, and an archive into one canonical spec tree. +1. A free-form design document for each [change][CHANGE]. +2. The [OpenSpec][OPENSPEC] [lifecycle][LIFECYCLE]: a proposal with the `Why` and `What Changes` sections, a *[delta specification][DELTASPECIFICATION]* for each *[capability][CAPABILITY]*, and an archive into one canonical spec tree. ## Decision Outcome Option 2. -- A change MUST live under `docs/changes//` with a proposal, a task list, and a delta specification for each capability. -- A requirement MUST use MUST language with at least one WHEN and THEN scenario. -- Archive MUST merge the delta into `docs/specs/` and MUST move the change directory to `docs/changes/archive/`. The canonical tree is the one truth. -- Archive is the act that accepts a change. OpenSpec has no status field and no accepted state, and the deck adds none. Archive MUST NOT run for a change without a record. +- A [change][CHANGE] MUST live under `docs/changes//` with a proposal, a task list, and a [delta specification][DELTASPECIFICATION] for each [capability][CAPABILITY]. +- A [requirement][REQUIREMENT] MUST use MUST language with at least one WHEN and THEN *[scenario][SCENARIO]*. +- Archive MUST merge the delta into `docs/specs/` and MUST move the [change][CHANGE] directory to `docs/changes/archive/`. The canonical tree is the only authoritative specification. +- Archive is the act that accepts a [change][CHANGE]. OpenSpec has no *[status][STATUS]* field and no accepted state, and the [repository][REPOSITORY] adds none. Archive MUST NOT run for a [change][CHANGE] without a record. - `rune spec` is the native implementation over `docs/`, and the OpenSpec CLI operates on the same tree. One tree serves both, with no mirror. -- Validation has layers: the spec grammar through the spec tooling, the document frame through mdschema, and the prose through the chosen linters. +- Validation has layers: the spec grammar through the spec tooling, the document frame through mdschema, and the prose through the chosen *[linters][LINTER]*. ### Consequences -- [+] Review has a target it can test, and each requirement can name the check that enforces it. -- [-] A small change carries the full ceremony: a proposal, a delta, and a record. The owner chose that cost, because a record states what the change wants. +- [+] Review has a [requirement][REQUIREMENT] it can test, and each [requirement][REQUIREMENT] can state the check that enforces it. +- [-] A small [change][CHANGE] needs the full ceremony: a proposal, a delta, and a record. The owner chose that cost, because a record states the intent of the [change][CHANGE]. - [-] The `status` field in a proposal is a note for readers. The tools read task checkboxes and the archive location, so the field can disagree with them. -- [-] `rune spec archive` matches requirement headings by exact text. A canonical heading that was recased by hand duplicates on the next archive. It also does not move a record or assign an id. Both are open tasks in the cli repository. +- [-] `rune spec archive` matches [requirement][REQUIREMENT] headings by exact text. A canonical heading that someone recased by hand duplicates on the next archive. It also does not move a record or assign an id. Both are open tasks in the cli clone. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [OPENSPEC]: https://github.com/Fission-AI/OpenSpec "OpenSpec, the spec-driven change lifecycle" +[REPOSITORY]: ../specs/glossary/repository.md +[CHANGE]: ../specs/glossary/change.md +[LIFECYCLE]: ../specs/glossary/lifecycle.md +[REQUIREMENT]: ../specs/glossary/requirement.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[DELTASPECIFICATION]: ../specs/glossary/delta-specification.md +[CAPABILITY]: ../specs/glossary/capability.md +[SCENARIO]: ../specs/glossary/scenario.md +[STATUS]: ../specs/glossary/status.md +[LINTER]: ../specs/glossary/linter.md diff --git a/docs/decisions/CORE-0020 YAML Configuration Files.md b/docs/decisions/CORE-0020 YAML Configuration Files.md index 6d92b4d5..9bd0e9f8 100644 --- a/docs/decisions/CORE-0020 YAML Configuration Files.md +++ b/docs/decisions/CORE-0020 YAML Configuration Files.md @@ -8,7 +8,7 @@ tags: - config status: accepted created: 2026-02-19 -updated: 2026-03-30 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,30 +27,39 @@ change: one-domain-one-plugin ## Context and Problem Statement -ENV vars in markdown instructions ([CORE-0005](CORE-0005%20Variables%20in%20Markdown%20Instructions%20and%20Templates.md)) need a source. Each module has configurable values (paths, feature flags, skill rosters) that differ between default and user-specific setups. The configuration system must support committed defaults, user overrides, and ENV var export without requiring users to manage shell profiles. +An environment variable in a Markdown *[instruction][INSTRUCTION]* ([CORE-0005](CORE-0005%20Variables%20in%20Markdown%20Instructions%20and%20Templates.md)) needs a source. Each *[module][MODULE]* has configurable values (paths, feature flags, *[skill][SKILL]* rosters) that differ between the default setup and a user's own setup. The configuration system must hold committed defaults and accept user overrides. It must also export the environment variables, so that the user manages no shell profile. ## Decision Drivers -- Defaults are committed and version-controlled, so the module works out of the box -- User overrides are gitignored, so personal paths and preferences stay out of the repository -- Override granularity is per-field, not per-file: users change one path, not the entire config -- ENV variable export is automatic: `yq` can read individual values, but the export of an entire config subtree as prefixed ENV vars requires a multi-step pipeline +- The author commits the defaults to version control. The [module][MODULE] then works without setup +- The user keeps an override in a git-ignored file, so a personal path or preference stays out of the *[repository][REPOSITORY]* +- A user overrides one field, such as one path, and keeps the rest of the configuration +- The configuration loader exports the environment variables without a manual step. `yq` can read individual values, but the export of a whole configuration subtree as prefixed environment variables needs a multi-step pipeline ## Considered Options -1. **Flat ENV files**: `.env` with `KEY=VALUE` pairs. No nesting, no merge, no defaults mechanism. -2. **JSON config**: structured and mergeable, but hard to author by hand, with no comment support. -3. **YAML with deep merge**: a committed `defaults.yaml` merged with a gitignored `config.yaml`, recursive so users override only the fields they need. +1. **Flat ENV files**: a `.env` file with `KEY=VALUE` pairs. A flat file cannot nest, cannot merge, and has no defaults mechanism. +2. **JSON config**: a structured file that merges. A person finds it hard to author by hand, and the format has no comment syntax. +3. **YAML with deep merge**: the loader merges a committed `defaults.yaml` with a git-ignored `config.yaml` recursively. A user overrides only the fields they need. ## Decision Outcome -Chosen option: **YAML with deep merge**. Each module includes `defaults.yaml` with the full schema and working defaults. Users create a gitignored `config.yaml` with only the fields they override. The config loading exports the merged result as ENV vars with a configurable prefix. +Chosen option: **YAML with deep merge**. Each [module][MODULE] includes a `defaults.yaml` with the full schema and working defaults. A user creates a git-ignored `config.yaml` with only the fields they override. The configuration loader exports the merged result as environment variables with a configurable prefix. -A module MUST include a committed `defaults.yaml` that works without user input. A user override MUST live in a git-ignored `config.yaml` and MUST merge over the defaults field by field. +A [module][MODULE] MUST include a committed `defaults.yaml` that works without user input. A user override MUST live in a git-ignored `config.yaml` and MUST merge over the defaults field by field. ### Consequences -- [+] The module works out of the box with zero user configuration -- [+] Users override one field without knowing the full schema -- [+] The config loading bridges config to ENV vars consumed by skills and shell commands -- [-] Deep merge semantics can surprise: array replacement versus append, null versus missing +- [+] The [module][MODULE] works before the user configures anything +- [+] A user overrides one field without knowing the full schema +- [+] The configuration loader turns the configuration into environment variables for [skills][SKILL] and shell commands +- [-] Deep merge can surprise a user on two questions: whether an array replaces or appends, and whether null differs from missing + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[INSTRUCTION]: ../specs/glossary/instruction.md +[MODULE]: ../specs/glossary/module.md +[SKILL]: ../specs/glossary/skill.md +[REPOSITORY]: ../specs/glossary/repository.md diff --git a/docs/decisions/CORE-0021 Unified Module Validation.md b/docs/decisions/CORE-0021 Unified Module Validation.md index 72bf3066..1d4fae25 100644 --- a/docs/decisions/CORE-0021 Unified Module Validation.md +++ b/docs/decisions/CORE-0021 Unified Module Validation.md @@ -9,7 +9,7 @@ tags: - pre-commit status: accepted created: 2026-04-02 -updated: 2026-04-17 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -30,30 +30,38 @@ change: one-domain-one-plugin ## Context and Problem Statement -A check that exists in one place and not in another gives a false pass. When CI, the commit hook, and a session each run their own list of checks, a change can be clean in one and broken in the next. The deck needs one list that every path runs. +The *[repository][REPOSITORY]* needs one list of checks that every path runs. A check that exists in one place and not in another gives a false pass. The paths are continuous integration (CI), the commit hook, and a session. When each path runs its own list of checks, a commit can be clean in one path and broken in the next. ## Decision Drivers -- One list of checks. No copy in a Makefile, a workflow, and a hook script. +- The [repository][REPOSITORY] keeps one list of checks, with no copy in a Makefile, a workflow, or a hook script. - A missing tool must not look like a passed check. -- One tool for each concern, so two tools never disagree about the same rule. +- The [repository][REPOSITORY] keeps one tool for each concern, so two tools never disagree about the same *[analysis rule][ANALYSISRULE]*. ## Considered Options 1. Each path keeps its own checks: a workflow file, a hook script, and a Makefile target. -2. One declarative list in `.pre-commit-config.yaml`, run by prek in every path. +2. One declarative list in `.pre-commit-config.yaml`, which prek runs in every path. ## Decision Outcome Option 2. `.pre-commit-config.yaml` MUST be the only list of checks. The commit hook, the push hook, `make validate`, and the CI `quality` job MUST each run that list through prek. -A guarded hook skips when its tool is absent, so a fresh clone stays usable. CI MUST set `REQUIRE_GATES=1`, which turns an absent tool into a failure. A session that reports a stage as clean MUST have every tool present ([DECK-0013](DECK-0013%20Local%20Checks%20Before%20Publication.md)). +A guarded hook skips when its tool is absent, so a fresh clone remains usable. CI MUST set `REQUIRE_GATES=1`, which turns an absent tool into a failure. A session that reports a stage as clean MUST have every tool present ([DECK-0013 Local Checks Before Publication](DECK-0013%20Local%20Checks%20Before%20Publication.md)). -The deck keeps one checker for each concern: rumdl for Markdown structure, Vale for prose style, typos for spelling, lychee for links, gitleaks for secrets, actionlint and zizmor for workflows, mdschema for document frames, shellcheck for shell, ruff for Python, and rudof for the declared graph. A tool MUST be a single binary that works offline on the commit path. A tool change MUST arrive through a decision record. +The [repository][REPOSITORY] keeps one checker for each concern: rumdl for Markdown structure, Vale for prose style, typos for spelling, and lychee for links. It keeps gitleaks for secrets, actionlint and zizmor for workflows, mdschema for document frames, and shellcheck for shell. It keeps ruff for Python and rudof for the declared graph. A tool MUST be one binary that works offline on the commit path. A revision of the tool set MUST arrive through a *[decision record][DECISIONRECORD]*. ### Consequences -- [+] The check list is one file, so a new check is one edit and reaches every path. +- [+] The check list is one file, so a new check is one edit that every path runs. - [-] CI runs checks that a local run can skip. A local pass with skipped hooks is not a pass, and the hook output is the only place that shows the skip. -- [-] CI builds the rune CLI from source for the `contract` job, so CI can run a check that the installed CLI lacks. A local pass does not prove that job. -- [-] The commit hook has one fallback: when prek is absent it runs gitleaks alone and says so. +- [-] CI builds the rune command-line interface (CLI) from source for the `contract` job, so CI can run a check that the installed CLI lacks. A local pass does not prove that job. +- [-] The commit hook has one fallback: when prek is absent, it runs gitleaks alone and says so. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[REPOSITORY]: ../specs/glossary/repository.md +[ANALYSISRULE]: ../specs/glossary/analysis-rule.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md diff --git a/docs/decisions/CORE-0022 Verified Remote Execution.md b/docs/decisions/CORE-0022 Verified Remote Execution.md index eeeda541..c6633e32 100644 --- a/docs/decisions/CORE-0022 Verified Remote Execution.md +++ b/docs/decisions/CORE-0022 Verified Remote Execution.md @@ -9,7 +9,7 @@ tags: - supply-chain status: accepted created: 2026-04-02 -updated: 2026-04-02 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,37 +27,39 @@ change: one-domain-one-plugin ## Context and Problem Statement -CI and hooks fetch tools from the network. A plain `curl | bash` trusts the remote server on every run, so a compromised upstream changes what executes and nothing reports it. The fetch is not the problem. Execution without verification is. +Continuous integration (CI) and hooks fetch tools from the network. A plain `curl | bash` runs whatever the remote server sends, on every run. So a compromised upstream alters what executes, and nothing reports it. The fetch is not the problem. The problem is execution without verification. ## Decision Drivers -- A fetched tool is current and costs no local maintenance. -- A committed digest is a trust anchor: the upstream can change, and execution then needs an explicit digest update. -- Supply chain attacks target the gap between fetched and verified ([SLSA threats][SLSA-THREATS]). +- A fetched tool is current, and nobody maintains a local copy. +- A committed digest is a trust anchor. The upstream can alter a file, and execution of that file then needs an explicit digest update. +- A supply chain attack targets the gap between a fetched file and a verified one. The [SLSA threats][SLSA-THREATS] page lists those attacks, where SLSA is Supply-chain Levels for Software Artifacts. ## Considered Options -1. Fetch and run with no verification. -2. No network fetch. Commit every tool and update by hand. -3. Fetch, compare the SHA-256 with a committed digest, and run only on a match. +1. Fetch the file and run it without verification. +2. Do not fetch at all. Commit every tool to the *[repository][REPOSITORY]* and update it by hand. +3. Fetch the file, compare its SHA-256 digest (Secure Hash Algorithm, 256 bits) with a committed digest, and run it only when they match. ## Decision Outcome -Option 3. A file that CI or a hook fetches from the network MUST be verified against a SHA-256 digest committed to the repository before it runs. A mismatch MUST stop the run. +Option 3. A file that CI or a hook fetches from the network MUST match a SHA-256 digest committed to the [repository][REPOSITORY] before it runs. A mismatch MUST stop the run. -`scripts/install-tools` is the instance in the deck. `scripts/tool-versions` holds one digest for each tool, system, and machine. The installer downloads the archive, checks the digest, and installs only on a match. +The installer in the [repository][REPOSITORY] is `scripts/install-tools`. The file `scripts/tool-versions` stores one digest for each tool, system, and machine. The installer downloads the archive and checks the digest. It installs only when the digest matches. -A digest update is an explicit act: read the upstream change, update the committed digest, and commit. Each change to executed code then passes through review. +A digest update is an explicit act. The author reads the upstream revision, updates the committed digest, and commits. Each edit to executed code then passes through review. ### Consequences -- [+] A compromised upstream is found at once, because the digest does not match. -- [-] Each tool upgrade needs a digest for every system and machine the deck supports. That friction is intended. -- [-] A network failure stops the install. The deck keeps no committed copy of the tools to fall back on. +- [+] The installer finds a compromised upstream at once, because the digest does not match. +- [-] Each tool upgrade needs a new digest for every system and machine the [repository][REPOSITORY] supports. The owner wants that friction. +- [-] A network failure stops the install. The [repository][REPOSITORY] does not keep a committed copy of the tools as a fallback. ## More Information - [SLSA levels][SLSA-LEVELS] +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [SLSA-THREATS]: https://slsa.dev/spec/v1.0/threats "SLSA v1.0, Threats and mitigations" [SLSA-LEVELS]: https://slsa.dev/spec/v1.0/levels "SLSA v1.0, Security levels" +[REPOSITORY]: ../specs/glossary/repository.md diff --git a/docs/decisions/CORE-0023 Rune Fills the Tool Roles.md b/docs/decisions/CORE-0023 Rune Fills the Tool Roles.md index 1346996a..420284cc 100644 --- a/docs/decisions/CORE-0023 Rune Fills the Tool Roles.md +++ b/docs/decisions/CORE-0023 Rune Fills the Tool Roles.md @@ -10,7 +10,7 @@ tags: - rune status: proposed created: 2026-10-18 -updated: 2026-10-18 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -28,49 +28,51 @@ change: plain-text-first-principles ## Context and Problem Statement -The foundation specifications describe how an *[instruction][INSTRUCTION]* is kept as an *[artifact][ARTIFACT]*. It is checked, assembled for each *[harness][HARNESS]*, generated from its source, deployed, repaired, signed, and archived. -A *[requirement][REQUIREMENT]* names the *[tool role][TOOLROLE]* that does each job. It names no command and no flag, because a command gets renamed while the behavior stays. The foundations are a philosophy for treating [instructions][INSTRUCTION] as [artifacts][ARTIFACT]. A manual for one tool is a different document. +This record says which program fills the *[tool roles][TOOLROLE]*, and what it has to do for each one. The specifications leave that question open. The foundation specifications describe an *[instruction][INSTRUCTION]* kept as an *[artifact][ARTIFACT]*. The [tool roles][TOOLROLE] check it, assemble it for each *[harness][HARNESS]*, generate it from its source, deploy it, repair it, sign it, and archive it. + +A *[requirement][REQUIREMENT]* states the [tool role][TOOLROLE] that does each job. It states no command and no flag, because the owner renames a command while the behavior stays the same. The foundations are a philosophy for treating [instructions][INSTRUCTION] as [artifacts][ARTIFACT]. A manual for one tool is a different document. + The roles are ontology terms: the *[validator][VALIDATOR]*, the *[assembler][ASSEMBLER]*, the *[generator][GENERATOR]*, the *[deployer][DEPLOYER]*, the *[fixer][FIXER]*, the *[signer][SIGNER]*, and the *[archiver][ARCHIVER]*. -This record answers the question the specifications leave open: which program fills the roles, and what it has to do for each. ## Decision Drivers -- A [requirement][REQUIREMENT] outlives a command name, so the specifications stay true when a command is renamed -- One install and one version keep the commit hook, the push hook, and continuous integration on one path ([CORE-0021](CORE-0021%20Unified%20Module%20Validation.md)) -- Every role reads the same ontology, the same structure schemas, and the same manifest, so one program needs one parser for each -- A role stays replaceable: another program that passes the same scenarios may fill it +- A [requirement][REQUIREMENT] outlives a command name, so the specifications stay true when the owner renames a command +- One install and one version put the commit hook, the push hook, and continuous integration on one path ([CORE-0021](CORE-0021%20Unified%20Module%20Validation.md)) +- Each role reads the same ontology, the same *[structure schemas][STRUCTURESCHEMA]*, and the same manifest, so one program needs one parser for each +- A role is replaceable. Another program that passes the same *[scenarios][SCENARIO]* may fill it ## Considered Options -1. One program per role: a separate [validator][VALIDATOR], [assembler][ASSEMBLER], [generator][GENERATOR], [deployer][DEPLOYER], [fixer][FIXER], [signer][SIGNER], and [archiver][ARCHIVER], each installed and versioned alone. Seven tools agree on one ontology and one manifest only by discipline. -2. Rune fills every role: one program fills each role as a capability, and a [requirement][REQUIREMENT] is met when rune's behavior matches it. -3. A plugin per [harness][HARNESS]: each harness's own extension mechanism fills the roles for that harness, with no shared program. Every harness then carries its own copy of the rules. +1. One program per role. The owner installs and versions a separate [validator][VALIDATOR], [assembler][ASSEMBLER], [generator][GENERATOR], [deployer][DEPLOYER], [fixer][FIXER], [signer][SIGNER], and [archiver][ARCHIVER]. Seven tools then agree on one ontology and one manifest only by discipline. +2. Rune fills every role. One program fills each role as a capability, and rune meets a [requirement][REQUIREMENT] when its behavior matches it. +3. A plugin per [harness][HARNESS]. The extension mechanism of each [harness][HARNESS] fills the roles for that [harness][HARNESS], with no shared program. Each [harness][HARNESS] then has its own copy of the constraints. ## Decision Outcome -Option 2. Rune fills every role. The specifications keep naming roles, and this record binds each role to the capabilities rune MUST have: +Option 2. Rune fills every role. The specifications keep stating roles, and this record binds each role to the capabilities rune MUST have: -- The [validator][VALIDATOR] reads [artifacts][ARTIFACT] against the structure schemas, the ontology, the link rules, and the deployment manifest, and reports findings. It never edits. +- The [validator][VALIDATOR] reads [artifacts][ARTIFACT] against the [structure schemas][STRUCTURESCHEMA], the ontology, the link constraints, and the deployment manifest, and reports findings. It never edits. - The [assembler][ASSEMBLER] turns a *[canon][CANON]* and its *[sidecars][SIDECAR]* into one *[instance][INSTANCE]* for each [harness][HARNESS] and a *[variant][VARIANT]* for each *[model][MODEL]* it knows. It translates includes and term links for each [harness][HARNESS] and seals the *[provenance record][PROVENANCERECORD]*. - The [generator][GENERATOR] writes derived files from their source: the root index, the directory indexes, the glossary pages, and the changelog. It regenerates them for the drift check. -- The [deployer][DEPLOYER] copies [instances][INSTANCE] into the harness trees and records the deployment manifest. -- The [fixer][FIXER] edits [artifacts][ARTIFACT] to repair a reported finding, as a separate step the author invokes or a hook runs before the validator. Its first job is a draft term in the ontology. +- The [deployer][DEPLOYER] copies [instances][INSTANCE] into the [harness][HARNESS] trees and records the deployment manifest. +- The [fixer][FIXER] edits [artifacts][ARTIFACT] to repair a reported finding, as a separate step the author invokes or a hook runs before the [validator][VALIDATOR]. Its first job is a draft term in the ontology. - The [signer][SIGNER] binds a head to the owner's key. -- The [archiver][ARCHIVER] moves an accepted *[change][CHANGE]* into the canonical specifications and refuses a change that links to a draft term. +- The [archiver][ARCHIVER] moves an accepted *[change][CHANGE]* into the canonical specifications and refuses a [change][CHANGE] that links to a draft term. -A role stays replaceable. Another program MAY fill a role when it passes the same scenarios, and a [requirement][REQUIREMENT] never names rune. +A role remains replaceable. Another program MAY fill a role when it passes the same [scenarios][SCENARIO]. A [requirement][REQUIREMENT] never states rune by name. ### Consequences -- [+] A requirement stays true when a rune command is renamed, because the specification names the role -- [+] One install, one version, and one path through the hooks and continuous integration -- [+] Every role shares one reading of the ontology, the structure schemas, and the manifests -- [-] Rune is the one dependency for every role. A bug in rune blocks checking, assembly, and deployment at once. The fallback scripts under `scripts/fallback/` cover only the term check, the term fixer, and the glossary generator today. -- [-] The capability list in this record grows with every new role. A role a specification adds without a line here is a gap this record owns. +- [+] A [requirement][REQUIREMENT] stays true when the owner renames a rune command, because the specification states the role +- [+] The owner installs one program at one version, with one path through the hooks and continuous integration +- [+] All roles share one reading of the ontology, the [structure schemas][STRUCTURESCHEMA], and the manifests +- [-] Rune is the one dependency for every role. A bug in rune blocks checking, *[assembly][ASSEMBLY]*, and deployment at once. The fallback scripts under `scripts/fallback/` cover only the term check, the term [fixer][FIXER], and the glossary [generator][GENERATOR] today. +- [-] The capability list in this record grows with every new role. A role that a specification adds without a line here is a gap that this record owns. ## More Information -- The voice review of 17 and 18 October 2026 decided that specifications name roles and no program. It named the validator, the assembler, the generator, the deployer, and the fixer. The signer and the archiver joined when the ontology gained the roles. +- The voice review of 17 and 18 October 2026 decided that specifications state roles and no program. It listed the [validator][VALIDATOR], the [assembler][ASSEMBLER], the [generator][GENERATOR], the [deployer][DEPLOYER], and the [fixer][FIXER]. The [signer][SIGNER] and the [archiver][ARCHIVER] joined when the ontology gained the roles. +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [INSTRUCTION]: ../specs/glossary/instruction.md [ARTIFACT]: ../specs/glossary/artifact.md @@ -91,3 +93,6 @@ A role stays replaceable. Another program MAY fill a role when it passes the sam [CHANGE]: ../specs/glossary/change.md [TOOLROLE]: ../specs/glossary/tool-role.md [VARIANT]: ../specs/glossary/variant.md +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md +[SCENARIO]: ../specs/glossary/scenario.md +[ASSEMBLY]: ../specs/glossary/assembly.md diff --git a/docs/decisions/DECK-0001 Artifact Benchmarking Skill.md b/docs/decisions/DECK-0001 Artifact Benchmarking Skill.md index 95ccfa53..c08090fa 100644 --- a/docs/decisions/DECK-0001 Artifact Benchmarking Skill.md +++ b/docs/decisions/DECK-0001 Artifact Benchmarking Skill.md @@ -6,7 +6,7 @@ category: "architecture" tags: ["benchmarking", "evaluation", "skills"] status: accepted created: 2026-08-13 -updated: 2026-08-13 +updated: 2026-10-19 author: "Martin Zeman" project: "deck" related: [] @@ -22,31 +22,49 @@ change: benchmark-instructions-per-model ## Context and Problem Statement -The evaluation loop lives inside BuildSkill: test cases, with-artifact and baseline runs, grading, aggregation, and a review viewer. It measures skills only, and it reports one aggregate per configuration with no model dimension. The deck now adopts rules and agents through the same reviewed pipeline as skills, and every adoption raises the same question the loop answers for skills: does this artifact change model behavior enough to earn its context cost? A rule adoption today lands with no measurement at all. +The evaluation loop is inside the BuildSkill *[skill][SKILL]*: test cases, with-artifact and baseline runs, grading, aggregation, and a review viewer. It measures [skills][SKILL] only. It reports one aggregate per configuration, with no *[model][MODEL]* dimension. The owner now adopts *[rules][RULE]* and *[agents][AGENT]* into the *[deck][DECK]* through the same reviewed pipeline as [skills][SKILL]. Every adoption asks the question the loop answers for [skills][SKILL]: does this *[artifact][ARTIFACT]* alter [model][MODEL] behavior enough to justify its context cost? Today the owner adopts a [rule][RULE] with no measurement at all. ## Decision Drivers -- Every artifact kind needs the same evidence: behavior with the artifact against behavior without it. -- Rules are always in context, so their cost is paid on every turn. The case for measuring them is stronger than for skills. -- Model behavior differs: a rule a frontier model does not need may still earn its place for smaller models. One aggregate across models hides exactly this. -- The comparison must be reviewable by a human in one artifact, not assembled from JSON by hand. -- The rune CLI will eventually own benchmarking, but coupling that migration to this extraction would stall both. +- Every [artifact][ARTIFACT] kind needs the same evidence: behavior with the [artifact][ARTIFACT] against behavior without it. +- A [rule][RULE] is always in context, so the session pays its cost on every turn. The case for measuring a [rule][RULE] is stronger than for a [skill][SKILL]. +- [Model][MODEL] behavior differs: a [rule][RULE] a frontier [model][MODEL] does not need may still help a smaller [model][MODEL]. One aggregate across [models][MODEL] hides exactly this. +- A person must be able to review the comparison in one [artifact][ARTIFACT]. A comparison built from JSON by hand does not count. +- The `rune` command-line interface (CLI) will own benchmarking one day, but tying that migration to this extraction would stall both. ## Considered Options -- Keep the loop inside BuildSkill and teach it about rules and agents. -- Extract the loop into a standalone BenchArtifact skill that BuildSkill defers to. -- Implement benchmarking in the rune CLI now and skip the skill stage. +- Keep the loop inside BuildSkill and extend it to [rules][RULE] and [agents][AGENT]. +- Extract the loop into a standalone BenchArtifact [skill][SKILL] that BuildSkill defers to. +- Build benchmarking in the `rune` CLI now and skip the [skill][SKILL] stage. ## Decision Outcome -Extract the loop into a standalone BenchArtifact skill in core. BuildSkill keeps a short pointer where its evaluation companion stood. The scripts, agent templates, and viewer move with the loop. BenchArtifact benchmarks any rune: a skill runs with and without the skill loaded, a rule runs with and without the rule text in context, an agent runs against a general-purpose baseline. Runs carry a model dimension, aggregation reports per configuration and model with no cross-model averaging, and the report is one self-contained HTML in the pattern of proton-ai-security's review reports: structured data inlined into a template, vendored stylesheet, client-side rendering, no external requests. +The owner extracts the loop into a standalone BenchArtifact [skill][SKILL] in core. BuildSkill keeps a short pointer where its evaluation *[companion][COMPANION]* stood. The scripts, [agent][AGENT] *[templates][TEMPLATE]*, and viewer move with the loop. -Keeping the loop inside BuildSkill would grow a skill-authoring guide into a benchmarking product and leave rules and agents reaching into another skill's internals. Starting in the rune CLI would gate a working measurement loop behind a Rust port. The skill form keeps the loop editable while its shape settles, and the CLI import follows once it has. +BenchArtifact benchmarks any *[rune][RUNE]*. A [skill][SKILL] runs with and without the [skill][SKILL] loaded. A [rule][RULE] runs with and without the [rule][RULE] text in context. An [agent][AGENT] runs against a general-purpose baseline. Each run records a [model][MODEL] dimension. Aggregation reports per configuration and [model][MODEL], with no cross-model averaging. + +The report is one self-contained HTML (HyperText Markup Language) file in the pattern of proton-ai-security's review reports. That pattern inlines structured data into a [template][TEMPLATE], vendors the stylesheet, renders on the client, and makes no external request. + +Keeping the loop inside BuildSkill would grow a skill-authoring guide into a benchmarking product. [Rules][RULE] and [agents][AGENT] would then reach into another [skill][SKILL]'s internals. Starting in the `rune` CLI would block a working measurement loop behind a Rust port. The [skill][SKILL] form keeps the loop editable while its structure settles. The CLI import comes after that. ### Consequences -- [+] Rule and agent adoptions can demand measurement before they land. The ReviewMarkers rule is the first candidate. -- [-] BuildSkill shrinks and gains a dependency on a sibling skill for its evaluation step. -- [-] The per-model matrix multiplies run counts. Benchmarks stay affordable only while test sets stay small. -- [-] The viewer and aggregator fork from BuildSkill's copies at extraction. Divergence between the two is resolved by the CLI import, which retires the skill-side scripts. +- [+] Adopting a [rule][RULE] or an [agent][AGENT] can demand a measurement before the merge. The ReviewMarkers [rule][RULE] is the first candidate. +- [-] BuildSkill shrinks and gains a dependency on a sibling [skill][SKILL] for its evaluation step. +- [-] The per-model matrix multiplies the run count. A benchmark is affordable only while the test set is small. +- [-] The viewer and the aggregator fork from BuildSkill's copies at extraction. The CLI import resolves the divergence and retires the skill-side scripts. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[SKILL]: ../specs/glossary/skill.md +[MODEL]: ../specs/glossary/model.md +[RULE]: ../specs/glossary/rule.md +[AGENT]: ../specs/glossary/agent.md +[DECK]: ../specs/glossary/deck.md +[ARTIFACT]: ../specs/glossary/artifact.md +[COMPANION]: ../specs/glossary/companion.md +[RUNE]: ../specs/glossary/rune.md +[TEMPLATE]: ../specs/glossary/template.md diff --git a/docs/decisions/DECK-0002 Benchmark Execution Ladder.md b/docs/decisions/DECK-0002 Benchmark Execution Ladder.md index ce8c36b4..00c4b0a6 100644 --- a/docs/decisions/DECK-0002 Benchmark Execution Ladder.md +++ b/docs/decisions/DECK-0002 Benchmark Execution Ladder.md @@ -9,7 +9,7 @@ tags: - harness status: accepted created: 2026-08-19 -updated: 2026-08-19 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: ["DECK-0001 Artifact Benchmarking Skill"] @@ -25,40 +25,46 @@ change: benchmark-instructions-per-model ## Context and Problem Statement -The Simplified Technical English benchmark exposed a spread of execution costs. A full cross-harness matrix with six models, clean harness state, and blind judging takes hours and demands provider credentials, sandbox carve-outs, and a local proxy. An author who wants a first signal on a new rule needs an answer in minutes, inside the harness that is already running. One fixed procedure cannot serve both, and an unordered menu of procedures invites incomparable results. +One fixed procedure cannot serve both a quick first signal and a full cross-harness matrix, and an unordered menu of procedures invites incomparable results. The Simplified Technical English benchmark exposed a spread of execution costs. A full cross-harness matrix with six *[models][MODEL]*, clean *[harness][HARNESS]* state, and blind judging takes hours. It demands provider credentials, sandbox carve-outs, and a local proxy. An author who wants a first signal on a new *[rule][RULE]* needs an answer in minutes, inside the [harness][HARNESS] that is already running. ## Decision Drivers -- The method must stay constant while the execution mechanics vary, or results stop being comparable across setups -- The calibration targets are Claude and Codex. Other harnesses are secondary evidence -- Weaker harnesses confuse easily and need tailored treatment. That work must not block the primary loop -- The eventual home for heavyweight evaluation is runedeck/bench, which does not exist yet +- The method must remain constant while the execution mechanics vary, or results stop being comparable across setups. +- The benchmark calibrates against Claude and Codex. Other [harnesses][HARNESS] are secondary evidence. +- Weaker [harnesses][HARNESS] confuse easily and need tailored treatment. That work must not block the primary loop. +- The eventual home for heavyweight evaluation is runedeck/bench, which does not exist yet. ## Considered Options -1. **Always maximal**: every benchmark runs the full cross-harness matrix, hours per iteration -2. **Always native**: every benchmark runs inside the current harness, single-vendor evidence only -3. **Execution ladder**: ordered setups that share one manifest, one grading path, and one verdict rule +1. **Always maximal**: every benchmark runs the full cross-harness matrix, hours per iteration. +2. **Always native**: every benchmark runs inside the current [harness][HARNESS], single-vendor evidence only. +3. **Execution ladder**: ordered setups that share one manifest, one grading path, and one [verdict rule](../BenchArtifact%20Anatomy.md). ## Decision Outcome -Chosen option: **Execution ladder**. Each rung buys more isolation and more models for more time, and every rung reads the same manifest, the same assertions, the same checker, and the same verdict rule. +Chosen option: **Execution ladder**. Each rung gives more isolation and more [models][MODEL] in exchange for more time. Every rung reads the same manifest, the same assertions, the same checker, and the same [verdict rule](../BenchArtifact%20Anatomy.md). -1. **Minimal.** The current harness runs the cases through its own agent tool. No subprocesses, no route registry. This is the NativeBench procedure and the default. +1. **Minimal.** The current [harness][HARNESS] runs the cases through its own subagent tool, with no subprocess and no route registry. This rung is the NativeBench procedure and the default. 2. **Value for time.** Claude and Codex run through `claude -p` and `codex exec`, through `rune run` when it is available. A scratch run on two cases with grading only returns a first table in about five minutes. -3. **Maximal.** The explicit cross-harness matrix: every configured harness, clean state, context canaries, raw output retention, and blind cross-vendor judging. This produced the iteration-4 verdict. +3. **Maximal.** The explicit cross-harness matrix runs every configured [harness][HARNESS] with clean state, context canaries, raw output retention, and blind cross-vendor judging. This rung produced the iteration-4 *[verdict][VERDICT]*. 4. **runedeck/bench.** The best evaluations will run from runedeck/bench once it exists. The configuration files are the migration unit. The interim wrapper retires. ### Consequences -- [+] A new rule gets a five-minute first table without any infrastructure -- [+] The cross-harness rung stays behind an explicit flag, so nobody pays its cost by accident -- [+] The ladder gives runedeck/bench a defined intake: configurations, not scripts -- [-] A quick scratch table is low confidence and must label itself as such -- [-] Harness-specific treatment prompts for weaker models remain future work on the maximal rung +- [+] A new [rule][RULE] gets a five-minute first table without any infrastructure. +- [+] An explicit flag guards the cross-harness rung, so nobody incurs its cost by accident. +- [+] The ladder gives runedeck/bench a defined intake of configurations rather than scripts. +- [-] A quick scratch table is low confidence and must label itself as such. +- [-] Harness-specific treatment prompts for weaker [models][MODEL] remain future work on the maximal rung. ## More Information - [DECK-0001 Artifact Benchmarking Skill](DECK-0001%20Artifact%20Benchmarking%20Skill.md): the extraction this ladder executes - `runes/core/skills/BenchArtifact/NativeBench.md`: the minimal rung - `runes/core/skills/BenchArtifact/RuneBench.md`: the maximal rung +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[MODEL]: ../specs/glossary/model.md +[HARNESS]: ../specs/glossary/harness.md +[RULE]: ../specs/glossary/rule.md +[VERDICT]: ../specs/glossary/verdict.md diff --git a/docs/decisions/DECK-0003 Three-Metric Verdict and Cross-Vendor Judging.md b/docs/decisions/DECK-0003 Three-Metric Verdict and Cross-Vendor Judging.md index 88eb17fa..929f6940 100644 --- a/docs/decisions/DECK-0003 Three-Metric Verdict and Cross-Vendor Judging.md +++ b/docs/decisions/DECK-0003 Three-Metric Verdict and Cross-Vendor Judging.md @@ -9,7 +9,7 @@ tags: - judging status: accepted created: 2026-08-19 -updated: 2026-08-19 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: ["DECK-0001 Artifact Benchmarking Skill", "DECK-0002 Benchmark Execution Ladder"] @@ -25,39 +25,46 @@ change: benchmark-instructions-per-model ## Context and Problem Statement -A single score hides the failure modes that matter. A model can drive a style checker to zero by dropping facts, or keep every fact in prose no reader would accept. Early Simplified Technical English iterations showed both failures, and they also showed judge self-preference: a judge model preferred baselines written by itself. The benchmark needs a verdict rule that catches each failure separately and a judging scheme that removes the self-preference confound. +One score hides the failure modes that matter. A *[model][MODEL]* can bring a style checker to zero by dropping facts, or keep every fact in prose that no reader accepts. Early iterations of Simplified Technical English (STE) showed both failures. They also showed judge self-preference: a judge [model][MODEL] preferred the baselines it had written itself. The benchmark needs a *[verdict][VERDICT]* rule that catches each failure separately, and a judging scheme that removes the self-preference confound. ## Decision Drivers -- Meaning loss, mechanical compliance, and prose damage are independent failure modes -- Blind judgment is the only defense against outputs that satisfy the checker and repel readers -- A judge that grades its own vendor's output measures taste, not quality -- Each artifact claims a different measurable behavior, so the middle metric must be pluggable +- Meaning loss, mechanical compliance, and prose damage fail independently of each other +- Only a blind judge catches an output that satisfies the checker and repels readers +- A judge that grades its own vendor's output measures its own taste instead of quality +- Each *[artifact][ARTIFACT]* claims a different measurable behavior, so the bench has to accept a different middle metric per [artifact][ARTIFACT] ## Considered Options -1. **One combined score**: a single number per run, simple to rank, hides every trade-off -2. **Checker density with validity filter**: assertions gate validity, density decides, prose damage invisible -3. **Three metrics, cross-vendor judging**: separate signals read together, judges assigned across vendors +1. **One combined score**: one number per run. It ranks simply and hides every trade-off. +2. **Checker density with validity filter**: the assertions decide validity, the density decides the [verdict][VERDICT], and prose damage stays invisible. +3. **Three metrics, cross-vendor judging**: the bench reads separate signals together and assigns the judges across vendors. ## Decision Outcome -Chosen option: **Three metrics, cross-vendor judging**, read together within one model and never combined into one number. +Chosen option: **Three metrics, cross-vendor judging**. The bench reads the three metrics together within one [model][MODEL] and never combines them into one number. -- **Assertions guard meaning.** Frozen, objectively checkable statements about facts, figures, and restrictions. A drop is a regression regardless of the other metrics. -- **The checker measures the claimed behavior.** The artifact brings its checker: a domain linter for a large claim, or a generic pattern-density configuration for a small one. Density uses checker findings per 100 checker words from the same text. -- **Blind preferences guard prose.** A judge compares shuffled pairs for clarity, fluency, and directness separately, with reasons recorded per pair. +- **Assertions guard meaning.** An assertion is a frozen statement about a fact, a figure, or a restriction, and a checker tests it objectively. A dropped assertion is a regression whatever the other metrics say. +- **The checker measures the claimed behavior.** The [artifact][ARTIFACT] brings its own checker, a domain *[linter][LINTER]* for a large claim or a generic pattern-density configuration for a small one. The density metric counts checker findings per 100 checker words of the same text. +- **Blind preferences guard prose.** A judge compares shuffled pairs for clarity, fluency, and directness separately, and the bench records the reasons for each pair. -An artifact earns its place when its claimed behavior improves, assertions hold, and blind preference stays acceptable. Judging is cross-vendor: one judge covers the models from other vendors, and a second vendor's judge covers the first judge's own model. The report withholds a verdict when fewer than half of the planned pairs are valid. +The bench accepts an [artifact][ARTIFACT] when its claimed behavior improves, its assertions hold, and the blind preference stays acceptable. The judging is cross-vendor. One judge covers the [models][MODEL] from other vendors. A judge from a second vendor covers the first judge's own [model][MODEL]. The report withholds a [verdict][VERDICT] when fewer than half of the planned pairs are valid. ### Consequences -- [+] Iteration-4 could state a split verdict: the STE skill earns its place on Claude Opus and Sonnet, does nothing for GPT-5.6 Sol, and degrades grok-4.6 and Lumo Max -- [+] Pair review is a first-class report feature, because numbers alone cannot show a model answering with a plan instead of a deliverable -- [-] Judging costs one provider call per pair. The quick rung skips it and must say so -- [-] Custom judge dimensions per artifact are a planned manifest extension, not yet built +- [+] Iteration-4 could state a split [verdict][VERDICT]. The STE *[skill][SKILL]* passes on Claude Opus and Sonnet, does nothing for GPT-5.6 Sol, and degrades grok-4.6 and Lumo Max +- [+] The report includes the pair review, because numbers alone cannot show a [model][MODEL] that answers with a plan instead of a deliverable +- [-] The judging costs one provider call per pair. The quick rung skips the judging and must say so +- [-] The bench does not yet accept custom judge dimensions per [artifact][ARTIFACT]. That is a planned manifest extension ## More Information - [DECK-0002 Benchmark Execution Ladder](DECK-0002%20Benchmark%20Execution%20Ladder.md): where each metric runs -- `runes/core/skills/BenchArtifact/references/schemas.md`: the judgment record shape +- `runes/core/skills/BenchArtifact/references/schemas.md`: the structure of the judgment record +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[MODEL]: ../specs/glossary/model.md +[VERDICT]: ../specs/glossary/verdict.md +[ARTIFACT]: ../specs/glossary/artifact.md +[LINTER]: ../specs/glossary/linter.md +[SKILL]: ../specs/glossary/skill.md diff --git a/docs/decisions/DECK-0004 Temporary Adoption State.md b/docs/decisions/DECK-0004 Temporary Adoption State.md index 67b75d9f..53edf8b0 100644 --- a/docs/decisions/DECK-0004 Temporary Adoption State.md +++ b/docs/decisions/DECK-0004 Temporary Adoption State.md @@ -6,7 +6,7 @@ category: "architecture" tags: ["adoption", "provenance", "review"] status: accepted created: 2026-08-13 -updated: 2026-08-13 +updated: 2026-10-19 author: "Martin Zeman" project: "deck" related: [] @@ -23,30 +23,41 @@ upstream: ## Context and Problem Statement -AdoptArtifact reviews imported content block by block. The first CLI implementation stored every block, verdict, note, and timestamp in a tracked review ledger. Large imports produced thousands of provenance lines even though deployment already relies on each adopted file's source pin and final digest. +The AdoptArtifact *[skill][SKILL]* reviews imported content block by block. The first version of the rune command-line interface (CLI) stored every block, verdict, note, and timestamp in a tracked review ledger. A large import produced thousands of provenance lines, even though deployment already relies on each adopted file's source pin and final digest. ## Decision Drivers -- Every imported block must receive a verdict before finalization. -- Workflow details must survive interrupted CLI sessions. -- Published deck artifacts need source attribution and final integrity, not a review transcript. -- A repository must not need local ignore rules to use the CLI safely. +- Every imported block must have a verdict before the *[importer][IMPORTER]* seals the adoption. +- Workflow details must survive an interrupted CLI session. +- A published *[artifact][ARTIFACT]* in the *[repository][REPOSITORY]* needs its source attribution and its final digest. It does not need a review transcript. +- A [repository][REPOSITORY] must not need local ignore rules to use the CLI safely. ## Considered Options -- Commit the complete review ledger beside each artifact. -- Compress the ledger but keep it as permanent provenance. -- Keep the ledger as temporary CLI state and commit only source-level sidecars. +- The [importer][IMPORTER] commits the complete review ledger beside each [artifact][ARTIFACT]. +- The [importer][IMPORTER] compresses the ledger and keeps it as permanent provenance. +- The CLI keeps the ledger as temporary state, and the [importer][IMPORTER] commits only source-level *[sidecars][SIDECAR]*. ## Decision Outcome -Keep block text, verdicts, notes, flags, and timestamps in temporary CLI session state. Finalization enforces the complete session, updates each adopt sidecar with its final file digest and reviewed state, then removes the session. The deck commits the artifact and those sidecars only. +The CLI keeps block text, verdicts, notes, flags, and timestamps in temporary session state. At finalization, the [importer][IMPORTER] checks that the session is complete. It then writes the final file digest and the reviewed state into each adoption [sidecar][SIDECAR], and removes the session. The author commits only the [artifact][ARTIFACT] and those [sidecars][SIDECAR] to the [repository][REPOSITORY]. -The repository rejects tracked `.provenance/review.yaml` and `.provenance/*.review.yaml` files. Existing ledgers are removed after their ordinary provenance sidecars are verified. The rune CLI owns session placement, crash recovery, doctor checks, and reseal behavior under runedeck/cli#21. +The [repository][REPOSITORY] rejects a tracked `.provenance/review.yaml` or `.provenance/*.review.yaml` file. The author removes an existing ledger after the *[validator][VALIDATOR]* verifies its ordinary provenance [sidecars][SIDECAR]. The rune CLI owns session placement, crash recovery, doctor checks, and reseal behavior under runedeck/cli#21. ### Consequences -- [+] Git history stays concise while block review remains mandatory. -- [+] Source URLs, upstream digests, transforms, reviewed state, and final file digests remain durable. -- [+] Review notes are process data and do not become a second provenance authority. -- [-] Legacy ledgers require removal, but their reviewed sidecars remain valid. +- [+] The git history contains no ledger, and block review remains mandatory. +- [+] Each [sidecar][SIDECAR] keeps the source URL (its Uniform Resource Locator), the upstream digest, the transforms, the reviewed state, and the final file digest. +- [+] Review notes are process data. They never become a second provenance authority. +- [-] The author has to remove each legacy ledger. The reviewed [sidecars][SIDECAR] beside it remain valid. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[SKILL]: ../specs/glossary/skill.md +[IMPORTER]: ../specs/glossary/importer.md +[ARTIFACT]: ../specs/glossary/artifact.md +[SIDECAR]: ../specs/glossary/sidecar.md +[REPOSITORY]: ../specs/glossary/repository.md +[VALIDATOR]: ../specs/glossary/validator.md diff --git a/docs/decisions/DECK-0005 Artifact Lifecycle and Evidence Tokens.md b/docs/decisions/DECK-0005 Artifact Lifecycle and Evidence Tokens.md index 6789f5ff..ef2b2b33 100644 --- a/docs/decisions/DECK-0005 Artifact Lifecycle and Evidence Tokens.md +++ b/docs/decisions/DECK-0005 Artifact Lifecycle and Evidence Tokens.md @@ -9,7 +9,7 @@ tags: - evidence status: accepted created: 2026-08-19 -updated: 2026-08-19 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -28,28 +28,56 @@ change: lifecycle-stages-and-evidence ## Context and Problem Statement -The stack grew one tool at a time: build skills, an adoption state machine, a bench harness, review lanes, an installer, and provider routines. Each tool works, but nothing names the pipeline they form, so the seams between them belong to nobody. The drift report in issue #45 is the symptom: hand-carried steps between tools fail without any check noticing. The stack needs one named lifecycle so that every seam has an owner and every promotion has a condition. +The stack needs one named sequence of stages, so that each hand-off between tools has an owner and each promotion has a condition. The stack grew one tool at a time: build *[skills][SKILL]*, an adoption state machine, the bench, *[agentic review lanes][AGENTICREVIEWLANE]*, an installer, and *[harness][HARNESS]* *[routines][ROUTINE]*. Each tool works, but no document states the pipeline they form, so the hand-offs between the tools belong to nobody. The drift report in issue #45 is the symptom. A step that a person carries by hand from one tool to the next fails, and no check notices. ## Decision Drivers -- Every artifact kind (skill, rule, agent, hook, routine) moves through the same states, and only the tools differ. -- A promotion without evidence is an assertion, and the ceremony already rejects assertions everywhere else. -- Rules sit in context on every turn, so their promotion bar must be the highest and the most explicit. -- The feedback loop from operation back to capture exists informally and loses lessons when sessions end. +- Every *[artifact][ARTIFACT]* kind ([skill][SKILL], *[rule][RULE]*, *[agent][AGENT]*, *[hook][HOOK]*, [routine][ROUTINE]) moves through the same states. Only the tools differ. +- A promotion without evidence is an assertion. The ceremony already rejects an assertion everywhere else. +- A [rule][RULE] is in context on every turn, so its promotion condition must be the highest and the most explicit. +- The feedback loop from operation back to capture exists informally, and it loses a lesson when a session ends. ## Considered Options -1. **Tool-centric documentation**: describe each tool well and leave the pipeline implicit. -2. **One monolithic pipeline tool**: build a single orchestrator that owns every step. -3. **Named stages with evidence tokens**: keep the tools, name the stages, and define one token per stage as the promotion condition. +1. Tool-centric documentation. The owner describes each tool well and leaves the pipeline implicit. +2. One monolithic pipeline tool. The owner builds one program that runs every step. +3. Named stages with evidence tokens. The owner keeps the tools, names each stage, and defines one token for each stage as its promotion condition. ## Decision Outcome -Option 3. The lifecycle is Capture, Author, Prove, Measure, Review, Ship, Operate. Each stage issues one evidence token: a dated capture note, a schema-validation pass, a provenance identity, a per-model bench verdict, an exact-head clean review, a deploy-manifest digest, and routine coverage. A stage MUST NOT consume an artifact without the previous token. Evidence is append-only and survives retirement. Operate feeds Capture: drift reports, audit findings, and bench regressions are captured notes. The workshop leverage ladder selects the stage that owns a repeated problem: architecture into Author, lints into Review, rules into Author with Measure paying their rent, human review as the floor. +Option 3. The stages are Capture, Author, Prove, Measure, Review, Ship, Operate. Each stage issues one evidence token: + +- Capture: a dated capture note. +- Author: a schema-validation pass. +- Prove: a provenance identity. +- Measure: a bench *[verdict][VERDICT]* for each *[model][MODEL]*. +- Review: an exact-head clean review. +- Ship: a deploy-manifest digest. +- Operate: [routine][ROUTINE] coverage. + +A stage MUST NOT consume an [artifact][ARTIFACT] without the token of the previous stage. Evidence is append-only, and it survives retirement. Operate feeds Capture: a drift report, an audit finding, or a bench regression becomes a captured note. + +The workshop's order of repairs, from architecture down to human review, selects the stage that takes a repeated problem. An architecture repair goes into Author. A lint goes into Review. A [rule][RULE] goes into Author, and Measure makes the [rule][RULE] prove its value. Human review is the floor. ### Consequences -- [+] Existing tools become stage instances, not the definition. A replacement tool moves no contract. -- [+] The gap register in the lifecycle-stages-and-evidence change lists every hop that is manual today. Each is now a named defect, not background noise. -- [+] Future gates (a rule without a verdict, an adoption without a sealed record) have a specification to cite. -- [+] The token model gives retirement a clean rule: remove the artifact, keep the tokens. +- [+] An existing tool fills a stage, and the stage, never the tool, is the definition. The owner replaces a tool without moving a contract. +- [+] The gap register in the lifecycle-stages-and-evidence *[change][CHANGE]* lists every hand-off that is manual today. Each one is now a named defect instead of background noise. +- [+] A future check that blocks a promotion (a [rule][RULE] without a [verdict][VERDICT], an adoption without a sealed record) has a specification to cite. +- [+] The evidence tokens give retirement a clean principle: the owner removes the [artifact][ARTIFACT] and keeps the tokens. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[ROUTINE]: ../specs/glossary/routine.md +[ARTIFACT]: ../specs/glossary/artifact.md +[SKILL]: ../specs/glossary/skill.md +[RULE]: ../specs/glossary/rule.md +[AGENT]: ../specs/glossary/agent.md +[HOOK]: ../specs/glossary/hook.md +[VERDICT]: ../specs/glossary/verdict.md +[AGENTICREVIEWLANE]: ../specs/glossary/agentic-review-lane.md +[HARNESS]: ../specs/glossary/harness.md +[MODEL]: ../specs/glossary/model.md +[CHANGE]: ../specs/glossary/change.md diff --git a/docs/decisions/DECK-0006 State Stores and Provider Edges.md b/docs/decisions/DECK-0006 State Stores and Provider Edges.md index 73e4d965..8e79dc11 100644 --- a/docs/decisions/DECK-0006 State Stores and Provider Edges.md +++ b/docs/decisions/DECK-0006 State Stores and Provider Edges.md @@ -9,7 +9,7 @@ tags: - providers status: accepted created: 2026-08-19 -updated: 2026-08-19 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -26,28 +26,44 @@ change: lifecycle-stages-and-evidence ## Context and Problem Statement -Artifact state lives in four places: workshop directories under `~/Agents//`, the deck repositories, the consumer checkout with its manifest and rendered private prompts, and the provider accounts. Nothing states what each place may hold, so content leaks across the boundaries: canonical text edited in provider trees, personal values almost committed to repositories, and provider accounts accumulating configuration nobody can rebuild. Providers also differ in what they accept, and every difference tempts a fork of the canonical artifact. +*[Artifact][ARTIFACT]* state is in four places. The first is the workshop directories under `~/Agents//`. The second is the *[repositories][REPOSITORY]* that contain the *[decks][DECK]*. The third is the consumer checkout with its manifest and rendered private prompts. The fourth is the provider accounts. +Nothing states what each place may contain, so content leaks across the boundaries. An author edits canonical text in a provider tree. A personal value almost reaches a [repository][REPOSITORY]. A provider account accumulates configuration that nobody can rebuild. Providers also differ in what they accept, and every difference tempts a fork of the canonical [artifact][ARTIFACT]. ## Decision Drivers -- A fresh machine must be able to rebuild every store below the workshop from repositories alone. -- Personal values and rendered prompts must never enter a repository. -- Canonical artifacts must stay provider-independent, or every provider change multiplies across the deck. +- A fresh machine must be able to rebuild every store below the workshop from the [repositories][REPOSITORY] alone. +- Personal values and rendered prompts must never enter a [repository][REPOSITORY]. +- A canonical [artifact][ARTIFACT] must stay provider-independent. Otherwise every provider change multiplies across the [deck][DECK]. - The environment matrix (DECK-0004) already bounds provider sessions by data sensitivity. The store contract must compose with it. ## Considered Options -1. **One store**: everything in the deck, providers read it directly. +1. **One store**: everything is in the [repository][REPOSITORY], and providers read it directly. 2. **Free placement**: any state anywhere, conventions only. -3. **Four stores with content contracts and two provider edges**: each store has a MUST and a MUST NOT list. Providers plug in at Ship and Operate only. +3. **Four stores with content contracts and two provider edges**: each store has a MUST list and a MUST NOT list. A provider plugs in at the Ship stage and the Operate stage only. ## Decision Outcome -Option 3. The workshop holds anything, and nothing installs from it. The deck holds only reviewed, proven, schema-valid artifacts and their sidecars, and never review transcripts or personal values. The consumer holds the manifest, deploy manifests, and rendered private prompts, and never canonical content. The provider account holds only configuration rendered from shipped templates, and MUST stay reproducible from the deck plus the consumer. Providers enter at two edges: Ship-side through assembly configuration (`targets`, keep-fields, casing rules, overlays) and Operate-side through adapted routine variants under the DECK-0004 matrix. Canonical frontmatter carries Agent Skills fields plus the three assembly directives, and nothing else. +Option 3. The workshop holds anything, and nothing installs from it. The [repository][REPOSITORY] contains only reviewed, proven, schema-valid [artifacts][ARTIFACT] and their *[sidecars][SIDECAR]*. It never contains a review transcript or a personal value. +The consumer contains the manifest, the deploy manifests, and the rendered private prompts. It never contains canonical content. The provider account contains only configuration rendered from the deployed *[templates][TEMPLATE]*, and it MUST stay reproducible from the [repository][REPOSITORY] plus the consumer. +A provider enters at two edges. On the Ship side it enters through *[assembly][ASSEMBLY]* configuration (`targets`, keep-fields, casing rules, overlays). On the Operate side it enters through adapted *[routine][ROUTINE]* *[variants][VARIANT]* under the DECK-0004 matrix. Canonical frontmatter has the Agent Skills fields plus the three [assembly][ASSEMBLY] directives. It has no other field. ### Consequences -- [+] Removing a provider changes zero canonical artifacts. Adding one needs a provider configuration, optional overlays, and routine variants. -- [+] Reproducibility becomes testable: rebuild a provider account from templates and compare. -- [+] The store contracts give the audit routines concrete assertions: canonical content outside the deck, or personal values inside it, are violations, not judgment calls. -- [+] The workshop stays the one free surface, which keeps capture cheap. +- [+] Removing a provider changes no canonical [artifact][ARTIFACT]. Adding one needs a provider configuration, optional overlays, and [routine][ROUTINE] [variants][VARIANT]. +- [+] Reproducibility becomes testable: the owner rebuilds a provider account from the [templates][TEMPLATE] and compares. +- [+] The store contracts give the audit [routines][ROUTINE] concrete assertions. Canonical content outside the [repository][REPOSITORY] is a violation, and so is a personal value inside it. Neither is a judgment call. +- [+] The workshop stays the one place with no content contract, so capture stays cheap. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[REPOSITORY]: ../specs/glossary/repository.md +[DECK]: ../specs/glossary/deck.md +[ARTIFACT]: ../specs/glossary/artifact.md +[SIDECAR]: ../specs/glossary/sidecar.md +[TEMPLATE]: ../specs/glossary/template.md +[ROUTINE]: ../specs/glossary/routine.md +[VARIANT]: ../specs/glossary/variant.md +[ASSEMBLY]: ../specs/glossary/assembly.md diff --git a/docs/decisions/DECK-0007 Retirement Path.md b/docs/decisions/DECK-0007 Retirement Path.md index 9f25c52f..8af3c3db 100644 --- a/docs/decisions/DECK-0007 Retirement Path.md +++ b/docs/decisions/DECK-0007 Retirement Path.md @@ -9,7 +9,7 @@ tags: - retirement status: accepted created: 2026-08-19 -updated: 2026-08-19 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -28,28 +28,45 @@ change: lifecycle-stages-and-evidence ## Context and Problem Statement -The stack defines how artifacts arrive and nothing about how they leave. The first first-party module teardown is live, and it exposed three loose ends. Deck removal, consumer manifest cleanup, and provider teardown are separate manual acts. Nobody has stated what happens to the retired artifact's evidence. And first-party provenance sidecars raised the question of what provenance is for at all. +The stack defines how *[artifacts][ARTIFACT]* arrive and nothing about how they leave. The first first-party *[module][MODULE]* teardown is live, and it exposed three loose ends. *[Deck][DECK]* removal, consumer manifest cleanup, and provider teardown are separate manual acts. Nobody has stated what happens to the evidence of the retired [artifact][ARTIFACT]. First-party provenance *[sidecars][SIDECAR]* raised the question of what provenance is for at all. ## Decision Drivers -- An artifact that leaves must leave everywhere, or stale copies keep loading context. -- Evidence has historical value after the artifact is gone: verdicts and reviews explain past behavior. -- Provenance sidecars cost maintenance (the check-provenance hook now enforces digest freshness), so they must earn that cost. +- An [artifact][ARTIFACT] that leaves must leave everywhere, or stale copies keep loading context. +- Evidence has historical value after the [artifact][ARTIFACT] leaves: *[verdicts][VERDICT]* and reviews explain past behavior. +- Provenance [sidecars][SIDECAR] cost maintenance (the check-provenance hook now enforces digest freshness), so they must justify that cost. - Deletion must be as reviewable as addition, because a quiet removal is a governance hole. ## Considered Options -1. **Ad-hoc deletion**: remove files where noticed, keep no rule. -2. **Tombstones everywhere**: keep a marker for every removed artifact in every store. -3. **Reviewed reverse path with evidence retention**: removal follows the review ceremony, evidence stays, provenance is scoped to ownership boundaries. +1. **Ad-hoc deletion**: remove files where someone notices them, with no stated principle. +2. **Tombstones everywhere**: keep a marker for every removed [artifact][ARTIFACT] in every store. +3. **Reviewed reverse path with evidence retention**: removal goes through the review ceremony, evidence stays, and provenance is scoped to ownership boundaries. ## Decision Outcome -Option 3. Retirement runs the flow in reverse, per store. The provider account drops its rendered configuration. The consumer drops the manifest entry, and pruning removes the deployed files the deploy manifest tracks. The deck removes the artifact and its sidecar in a reviewed change that records the reason. The workshop ledger captures the retirement decision. Evidence records (bench verdicts, review history, decision records) stay. The teardown also sets the provenance principle: sidecars exist to import trust across an ownership boundary. First-party artifacts carry their trust in the authorship ceremony, so removing first-party sidecars is a correction, not a loss. +Option 3. Retirement runs the flow in reverse, per store. The provider account drops its rendered configuration. The consumer drops the manifest entry, and pruning removes the deployed files that the deploy manifest tracks. + +The [deck][DECK] removes the [artifact][ARTIFACT] and its [sidecar][SIDECAR] in a reviewed *[change][CHANGE]* that records the reason. The workshop ledger captures the retirement decision. Evidence records (bench [verdicts][VERDICT], review history, *[decision records][DECISIONRECORD]*) stay. + +The teardown also sets the provenance principle: [sidecars][SIDECAR] exist to import trust across an ownership boundary. First-party [artifacts][ARTIFACT] hold their trust in the authorship ceremony, so removing first-party [sidecars][SIDECAR] corrects a mistake and loses nothing. ### Consequences -- [+] Retirement becomes auditable: a reviewed change per removal, and drift routines can flag artifacts present in one store and absent upstream. -- [+] The provenance rule shrinks the sidecar population to adopted artifacts, which shrinks the check-provenance surface. -- [+] A future `rune retire` command has a defined transaction to implement: one artifact, three stores, one review. -- [+] Evidence retention means benchmarks of retired artifacts remain citable when a successor claims the same behavior. +- [+] Retirement becomes auditable: each removal is a reviewed [change][CHANGE], and drift *[routines][ROUTINE]* can flag [artifacts][ARTIFACT] present in one store and absent upstream. +- [+] The provenance principle shrinks the [sidecar][SIDECAR] population to adopted [artifacts][ARTIFACT], which shrinks what the check-provenance hook has to check. +- [+] A future `rune retire` command has a defined transaction to implement: one [artifact][ARTIFACT], three stores, one review. +- [+] Evidence retention means that benchmarks of retired [artifacts][ARTIFACT] remain citable when a successor claims the same behavior. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[ARTIFACT]: ../specs/glossary/artifact.md +[MODULE]: ../specs/glossary/module.md +[DECK]: ../specs/glossary/deck.md +[SIDECAR]: ../specs/glossary/sidecar.md +[VERDICT]: ../specs/glossary/verdict.md +[CHANGE]: ../specs/glossary/change.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[ROUTINE]: ../specs/glossary/routine.md diff --git a/docs/decisions/DECK-0008 Idea-to-Merge Flywheel.md b/docs/decisions/DECK-0008 Idea-to-Merge Flywheel.md index 021d22ce..2cb8b94c 100644 --- a/docs/decisions/DECK-0008 Idea-to-Merge Flywheel.md +++ b/docs/decisions/DECK-0008 Idea-to-Merge Flywheel.md @@ -9,7 +9,7 @@ tags: - extraction status: accepted created: 2026-08-19 -updated: 2026-09-10 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,39 +27,57 @@ change: agentic-first-pipeline-rules ## Context and Problem Statement -Agents are the owner's primary computer interface, across terminals, phone remote control, remote desktops, and local infrastructure. Ideas arrive in any medium and enter the stack ad hoc: some become specifications, some become direct edits, and many evaporate. Finished work leaves the same way: a change merges and the lessons inside it stay unmined. The stack has every middle stage (specification, isolation, swarm delegation, local gates, review lanes, babysitting). No named loop connects a raw thought to a merged change and back into context. +The owner uses *[harnesses][HARNESS]* as the primary computer interface: in terminals, through remote control from a phone, on remote desktops, and on local infrastructure. An *[idea][IDEA]* arrives in any medium and enters the stack without a procedure. Some [ideas][IDEA] become specifications, some become direct edits, and many evaporate. Finished work leaves the same way: a *[change][CHANGE]* merges, and the lessons inside it stay unmined. The stack has every middle stage: specification, isolation, swarm delegation, local checks, each *[agentic review lane][AGENTICREVIEWLANE]*, and babysitting. No named loop connects a raw thought to a merged [change][CHANGE] and back into context. ## Decision Drivers -- The entry seam and the exit seam are the two unowned seams. Everything between them already runs. -- An idea transcribed without challenge produces specification noise. Pushback against existing contracts must precede scaffolding. -- Deliveries that extract nothing keep the next prompt as expensive as the last one. The loop must compound. -- The pipeline must stay cross-harness: the future pi host is an edge, not a rewrite target. -- Rules extracted without measurement fill every later context with unpaid cost. +- The entry step and the exit step have no owner. Everything between them already runs. +- An [idea][IDEA] transcribed without challenge produces specification noise. The intake must challenge the [idea][IDEA] against the existing contracts before it creates the [change][CHANGE]. +- A delivery that extracts nothing keeps the next prompt as expensive as the last one. Each pass must leave the next pass cheaper. +- The pipeline must work on every [harness][HARNESS]. The future pi host joins as one more edge and forces no rewrite. +- A *[rule][RULE]* extracted without measurement adds cost to every later context without a measured benefit. ## Considered Options -1. **A linear pipeline**: prompt to merge, extraction stays a habit. +1. **A linear pipeline**: from prompt to merge, with extraction left as a habit. 2. **A monolithic orchestrator**: one tool owns intake through merge. -3. **A named loop over existing instances**: contracts for intake and extraction, existing tools for every middle stage, enforcement added later. +3. **A named loop over existing implementations**: a contract for intake, a contract for extraction, the existing tools for every middle stage, and enforcement added later. ## Decision Outcome -Option 3. The loop is: prompt, pushback, specify, isolate, swarm, local gates, human skim, CI and review lanes, babysit, approve, extract, recycle. Intake challenges every idea against specifications, decision records, and advisory memory before scaffolding, and sizes the change by blast radius. Every pass closes with the extraction question. Extracted artifacts rejoin the standard lifecycle, rules pay rent through a bench verdict, and shipped extractions reach later passes through the normal install path. Four placement decisions complete the loop. Memory stays advisory and never authoritative. Obsidian leaves the flow and the vault stays an archive. pi joins as a provider edge, and the eventual pi integration is a thin extension that wraps the rune binary. Copy-on-write workspaces are an acceptable isolation instance because they copy whole trees and never touch git refs, while bookmark and push discipline stay with jj. +Option 3. The loop is: prompt, pushback, specify, isolate, swarm, local checks, human skim, continuous integration and each [agentic review lane][AGENTICREVIEWLANE], babysit, approve, extract, recycle. The intake challenges every [idea][IDEA] against the specifications, the *[decision records][DECISIONRECORD]*, and the advisory memory before it creates the [change][CHANGE]. It sizes the [change][CHANGE] by how much the [change][CHANGE] can break. + +Every pass closes with the extraction question. An extracted *[artifact][ARTIFACT]* rejoins the standard *[lifecycle][LIFECYCLE]*. A [rule][RULE] pays for its context cost with a bench *[verdict][VERDICT]*. A released extraction enters a later pass through the normal install path. + +Four placement decisions complete the loop. Memory stays advisory and never authoritative. Obsidian leaves the flow, and the vault stays an archive. pi joins as a provider edge, and the eventual pi integration is a thin extension that wraps the rune binary. A copy-on-write workspace is an acceptable isolation method, because it copies a whole tree and never touches a git ref. Bookmark and push discipline stay with jj. ### Consequences -- [+] The IntakeIdea skill instantiates the entry seam now. The extraction gate is a named follow-up, not folklore. -- [+] The flywheel gives the deck a growth mechanism: the stack's own development mines the artifacts that improve the stack. -- [+] The pi decision keeps rune harness-independent while leaving a concrete integration path. -- [-] The loop adds one more standing question to every pass, which costs a sentence and pays a rule. +- [+] The IntakeIdea *[skill][SKILL]* fills the entry step now. The extraction check is a named follow-up task. +- [+] The loop gives the *[repository][REPOSITORY]* a growth mechanism: the development of the stack mines the [artifacts][ARTIFACT] that improve the stack. +- [+] The pi decision keeps rune independent of any [harness][HARNESS] and leaves a concrete integration path. +- [-] The loop adds one more standing question to every pass. The question costs a sentence and yields a [rule][RULE]. ## More Information -Pull request delivery amendment. The middle stages need observable completion evidence, not only named tools and procedures. +Amendment on pull request delivery. The middle stages need observable evidence of completion beyond named tools and procedures. The [pull-request delivery contract](../changes/merge-ready-needs-evidence/specs/merge-ready-needs-evidence/spec.md) defines that evidence for the existing babysitting stage. This amendment keeps VersionControl as the procedure owner and preserves the existing publication-authorization contract. -It adds no pipeline stage, orchestration service, or council-based acceptance rule. +It adds no pipeline stage, no orchestration service, and no acceptance step that a council decides. + +The [design](../changes/merge-ready-needs-evidence/design.md) assigns source contradictions to [skill][SKILL] repairs and mechanical defects to their existing tooling owners. +This separation stops a repeated review failure from producing a duplicate rulebook or an unrelated edit in a pull request. + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. -The [design](../changes/merge-ready-needs-evidence/design.md) assigns source contradictions to skill repairs and mechanical defects to their existing tooling owners. -This separation prevents repeated review failures from producing duplicate rulebooks or unrelated changes in a PR. +[IDEA]: ../specs/glossary/idea.md +[CHANGE]: ../specs/glossary/change.md +[HARNESS]: ../specs/glossary/harness.md +[RULE]: ../specs/glossary/rule.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[ARTIFACT]: ../specs/glossary/artifact.md +[VERDICT]: ../specs/glossary/verdict.md +[SKILL]: ../specs/glossary/skill.md +[REPOSITORY]: ../specs/glossary/repository.md +[AGENTICREVIEWLANE]: ../specs/glossary/agentic-review-lane.md +[LIFECYCLE]: ../specs/glossary/lifecycle.md diff --git a/docs/decisions/DECK-0009 Declared Constraints over Proposed Changes.md b/docs/decisions/DECK-0009 Declared Constraints over Proposed Changes.md index 3476a12a..3d0892a8 100644 --- a/docs/decisions/DECK-0009 Declared Constraints over Proposed Changes.md +++ b/docs/decisions/DECK-0009 Declared Constraints over Proposed Changes.md @@ -9,7 +9,7 @@ tags: - verification status: accepted created: 2026-08-19 -updated: 2026-08-19 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,29 +27,47 @@ change: agentic-first-pipeline-rules ## Context and Problem Statement -Agents hallucinate entities and then everyone verifies by hand. Comments go out of date, documentation duplicates code, and unmeasured Markdown sits in every context window. The stack already has the primitive answer: mdschema constrains structure, JSON schemas constrain fields, the ceremony constrains process. But the constraints stop at single files. Nothing declares the permitted relations between artifacts, stages, and evidence. Lifecycle rules like "a rule ships only with a bench verdict" therefore live as folklore that only review rounds enforce. +A *[model][MODEL]* hallucinates an entity, and then a person verifies every reference by hand. Comments go out of date. Documentation duplicates code. Unmeasured Markdown sits in every context window. + +The stack already has the basic answer. The *[structure schema][STRUCTURESCHEMA]* constrains the structure of a file, a JSON schema constrains its fields, and the ceremony constrains the process. But those constraints stop at single files. No declaration states the permitted relations between *[artifacts][ARTIFACT]*, their stages, and evidence. A stage constraint such as "a *[rule][RULE]* enters the *[deck][DECK]* only with a bench *[verdict][VERDICT]*" therefore lives as folklore. Only a review round enforces it. ## Decision Drivers -- The neurosymbolic split is the right division of labor: probabilistic agents propose, symbolic declarations permit, deterministic checkers decide. -- Code is truth and prose drifts. A constraint written as prose decays, and the same constraint written as a checkable declaration does not. -- Public vocabularies (schema.org, Dublin Core, SKOS, FOAF) already name most metadata concepts. Parallel invented terms create translation debt. -- Inference and constraint are different tools: one suggests missing facts, the other rejects invalid states. The stack needs the rejecting kind first. -- Every checker added below review (the ladder's lint rung) removes a class of review rounds. +- The neurosymbolic split is the right division of labor. A probabilistic [model][MODEL] proposes, a symbolic declaration permits, and a deterministic checker decides. +- A constraint written as prose decays. The same constraint written as a checkable declaration does not. +- Public vocabularies already name most metadata concepts: schema.org, Dublin Core, SKOS (the Simple Knowledge Organization System), and FOAF (Friend of a Friend). An invented parallel term creates translation debt. +- Inference and constraint are different tools. Inference suggests a missing fact, and a constraint rejects an invalid state. The stack needs the rejecting kind first. +- Every checker the owner adds below review, on the lint rung of the ladder, removes a class of review rounds. ## Considered Options -1. **Prose contracts only**: keep lifecycle rules in specifications and enforce them in review. -2. **Full semantic-web stack now**: RDF store, OWL ontology, reasoner, from day one. -3. **Declared constraints, grown from existing schemas**: state the artifact graph and its axioms in the deck, map metadata to public vocabularies, and build small deterministic checkers per constraint. Adopt heavier reasoning only when a constraint needs it. +1. **Prose contracts only**: the owner keeps stage constraints in specifications, and a reviewer enforces them in review. +2. **Full semantic-web stack now**: an RDF (Resource Description Framework) store, an OWL (Web Ontology Language) ontology, and a reasoner, from day one. +3. **Declared constraints, grown from existing schemas**: the owner states the [artifact][ARTIFACT] graph and its axioms in the *[repository][REPOSITORY]* and maps the metadata to public vocabularies. Each constraint gets a small deterministic checker. The owner adopts heavier reasoning only when a constraint needs it. ## Decision Outcome -Option 3. The deck declares its world: artifact kinds, lifecycle stages, evidence tokens, and the permitted relations between them, with public vocabulary terms reused where they exist. Lifecycle rules become axioms in the description-logic style (functional properties for one-time facts, domain and range for who may hold what), and each axiom gets a deterministic checker. A deterministic check verifies every named entity in a proposal against the declared world, so a hallucinated reference fails a check instead of surviving until review. The existing schemas remain the ontology's first layer, and the graph layer grows from them without a platform rewrite. +Option 3. The [repository][REPOSITORY] declares its world. The declaration lists the [artifact][ARTIFACT] kinds, their stages, the evidence tokens, and the permitted relations between them. It reuses a public vocabulary term where one exists. Each stage constraint becomes an axiom in the description-logic style. A functional property states a one-time fact, and a domain and a range state who may hold what. + +Each axiom gets a deterministic checker. A deterministic check verifies every named entity in a proposal against the declared world. A hallucinated reference therefore fails a check instead of surviving until review. The existing schemas remain the ontology's first layer. The graph layer grows from them without a platform rewrite. ### Consequences -- [+] The evidence-token gates from DECK-0005 acquire an enforcement mechanism that is neither prose nor review labor. -- [+] Intake pushback gains a deterministic ground: a check, not an argument, rejects an idea that references nonexistent capabilities. -- [+] The vocabulary mapping keeps deck metadata legible to external tooling at low cost. -- [+] The heavier machinery (RDF serialization, OWL reasoners) stays optional until a constraint needs inference, which keeps complexity matched to the task. +- [+] The evidence-token conditions from [DECK-0005](DECK-0005%20Artifact%20Lifecycle%20and%20Evidence%20Tokens.md) get an enforcement mechanism that is neither prose nor review labor. +- [+] Intake pushback gains a deterministic ground. A check rejects an *[idea][IDEA]* that references a *[capability][CAPABILITY]* that does not exist, and nobody has to argue the point. +- [+] The vocabulary mapping lets external tooling read the [repository][REPOSITORY]'s metadata at low cost. +- [+] The heavier machinery, RDF serialization and OWL reasoners, remains optional until a constraint needs inference. Complexity then matches the task. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md +[MODEL]: ../specs/glossary/model.md +[ARTIFACT]: ../specs/glossary/artifact.md +[DECK]: ../specs/glossary/deck.md +[VERDICT]: ../specs/glossary/verdict.md +[REPOSITORY]: ../specs/glossary/repository.md +[RULE]: ../specs/glossary/rule.md +[IDEA]: ../specs/glossary/idea.md +[CAPABILITY]: ../specs/glossary/capability.md diff --git a/docs/decisions/DECK-0010 Declared World in RDF.md b/docs/decisions/DECK-0010 Declared World in RDF.md index 4c4a972c..906119dc 100644 --- a/docs/decisions/DECK-0010 Declared World in RDF.md +++ b/docs/decisions/DECK-0010 Declared World in RDF.md @@ -1,6 +1,6 @@ --- title: Declared World in RDF -description: The deck declares its artifact world in RDF with established vocabularies, and SHACL shapes validate the extracted graph as a deterministic gate +description: The deck declares its artifact world in RDF with established vocabularies, and SHACL shapes validate the extracted graph as a deterministic check type: adr category: architecture tags: @@ -9,7 +9,7 @@ tags: - validation status: accepted created: 2026-08-26 -updated: 2026-09-23 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,42 +27,62 @@ change: ontology-keeps-agents-honest ## Context and Problem Statement -Agents propose artifacts, and the deck needs a deterministic layer that decides what is permitted. The first draft was a custom checker with a custom world file. Custom checkers grow into a private standard that nobody else maintains. The semantic-web stack already solves this problem: established vocabularies name the concepts, RDF stores the facts, and SHACL validates them. The deck needs a decision on namespaces, vocabulary reuse, the validation semantics, and the toolchain. +The *[repository][REPOSITORY]* needs a deterministic layer that decides which proposed *[artifact][ARTIFACT]* it permits, and this record chooses the semantic-web stack for that layer. *[Models][MODEL]* propose [artifacts][ARTIFACT]. The first draft was a custom checker with a custom world file. A custom checker grows into a private standard that nobody else maintains. + +The semantic-web stack already solves this problem. Established vocabularies define the concepts, the Resource Description Framework (RDF) stores the facts, and the Shapes Constraint Language (SHACL) validates them. The [repository][REPOSITORY] needs a decision on namespaces, vocabulary reuse, the validation semantics, and the toolchain. ## Decision Drivers -- Reuse established vocabularies. Do not invent terms that schema.org, PROV-O, Dublin Core, SKOS, FOAF, or DOAP already define. -- The gate must be deterministic: one boolean conformance result with a machine-readable report. -- The toolchain must run locally as single binaries. Prefer Rust. Exclude Java. Exclude Python from the gate path. -- Namespace choices are permanent. A rename later is as costly as a history rewrite. -- The gate must grow one shape at a time from observed failures, after the OBO Foundry practice. +- The [repository][REPOSITORY] reuses established vocabularies. It invents no term that schema.org, PROV-O, Dublin Core, SKOS, FOAF, or DOAP already defines. PROV-O is the provenance ontology of the World Wide Web Consortium (W3C). The other abbreviations are the Simple Knowledge Organization System (SKOS), Friend of a Friend (FOAF), and Description of a Project (DOAP). +- The check that admits or blocks an [artifact][ARTIFACT] must be deterministic: one boolean conformance result with a machine-readable report. +- The toolchain must run locally as single binaries. The owner prefers Rust, excludes Java, and excludes Python from the path of the blocking check. +- A namespace choice is permanent. A later rename costs as much as a history rewrite. +- The blocking check must grow one shape at a time from observed failures, as the OBO (Open Biological and Biomedical Ontologies) Foundry does. ## Considered Options -1. **Custom checker with a custom world file.** Full control, zero standards, and a private format to maintain forever. -2. **OWL constraints with a reasoner.** Standard, but OWL operates under the open-world assumption. A cardinality axiom infers that two refunds are one refund. It never fails the data. -3. **RDF ontology for description plus SHACL shapes for validation.** RDFS and OWL describe and infer. SHACL validates under the closed-world assumption and fails deterministically. +1. A custom checker with a custom world file. The owner has full control, no standard, and a private format to maintain forever. +2. OWL (Web Ontology Language) constraints with a reasoner. OWL is a standard, but it operates under the open-world assumption. A cardinality axiom makes the reasoner infer that two refunds are one refund. It never fails the data. +3. An RDF ontology for description plus SHACL shapes for validation. RDFS (RDF Schema) and OWL describe and infer. SHACL validates under the closed-world assumption and fails deterministically. ## Decision Outcome Option 3. The decisions in detail: -- **Namespaces.** The product vocabulary lives at `https://runedeck.ai/ns#` with prefix `rune:`. The owner's personal vocabulary lives at `https://martinzeman.net/ns#` with prefix `n4m3z:`. Entities get IRIs under `/id/`, separate from terms under `/ns#`. The personal graph imports the product graph, never the reverse. -- **Vocabulary reuse.** PROV-O is the backbone: artifacts are `prov:Entity`, work is `prov:Activity`, people and models are `prov:Agent`. Dublin Core terms carry generic metadata. The deck mints a term only where verified search finds no established term, and every minted class declares `rdfs:subClassOf` to an established class. The first minted terms are `rune:DecisionRecord`, `rune:Rule`, and `rune:Verdict`. -- **Kinds from the first-generation survey.** The taxonomy says "kind", never "ontology", because the first generation spent that word on filesystem roots. `rune:Rune` is the umbrella class over the five kinds. Canon, Sidecar, Companion, Template, Variant, and StructureSchema are classes, because the first generation proved each is load-bearing. Provider and Target stay separate. Drift states are queries over digests, never stored facts. -- **Identity.** A record identifier such as DECK-0010 is the identity. The extractor mints the IRI from the identifier, not from the file path. Two files that claim one identifier merge into one node, and the shapes report the collision as a cardinality violation. -- **Validation semantics.** OWL and RDFS never gate. SHACL shapes in `ontology/shapes.ttl` are the gate. The first scope is three shapes: unique record identifiers, resolvable references, and a verdict pointer on every rule. -- **Toolchain.** `rudof shacl-validate` (Rust) validates the extracted graph. The extractor is a `rune` subcommand in the cli repository. The prose and hygiene row is rumdl, typos, Vale, lychee, actionlint, and zizmor. -- **Change ceremony.** A new class or a new namespace requires a decision record. A new shape on existing classes is a normal reviewed change. Shape severity starts at Warning and moves to Violation in a recorded follow-up. +- **Namespaces.** The product vocabulary has the namespace `https://runedeck.ai/ns#` and the prefix `rune:`. The owner's personal vocabulary has the namespace `https://martinzeman.net/ns#` and the prefix `n4m3z:`. An entity gets an IRI (Internationalized Resource Identifier) under `/id/`, apart from the terms under `/ns#`. The personal graph imports the product graph, and never the reverse. +- **Vocabulary reuse.** PROV-O is the backbone: an [artifact][ARTIFACT] is a `prov:Entity`, work is a `prov:Activity`, and a person or a [model][MODEL] is a `prov:Agent`. Dublin Core terms hold the generic metadata. The [repository][REPOSITORY] creates a term only where a verified search does not find an established term. Every created class declares `rdfs:subClassOf` to an established class. The first created terms are `rune:DecisionRecord`, `rune:Rule`, and `rune:Verdict`. +- **Kinds from the first-generation survey.** The taxonomy says "kind" and never "ontology", because the first generation used that word for filesystem roots. `rune:Rune` is the umbrella class over the five kinds. *[Canon][CANON]*, *[Sidecar][SIDECAR]*, *[Companion][COMPANION]*, *[Template][TEMPLATE]*, *[Variant][VARIANT]*, and *[StructureSchema][STRUCTURESCHEMA]* are classes, because the first generation proved that each one is necessary. Provider and *[Target][TARGET]* remain separate. A drift state is a query over digests and never a stored fact. +- **Identity.** A record identifier such as DECK-0010 is the identity. The extractor creates the IRI from the identifier and never from the file path. When two files claim one identifier, they merge into one node, and the shapes report the collision as a cardinality violation. +- **Validation semantics.** OWL and RDFS never block an [artifact][ARTIFACT]. The SHACL shapes in `ontology/shapes.ttl` are the check that blocks. The first scope is three shapes: a unique identifier on each record, a resolvable reference, and a *[verdict][VERDICT]* pointer on every *[rule][RULE]*. +- **Toolchain.** `rudof shacl-validate`, a Rust program, validates the extracted graph. The extractor is a `rune` subcommand in the cli clone. The prose and hygiene checks are rumdl, typos, Vale, lychee, actionlint, and zizmor. +- **Change ceremony.** A new class or a new namespace requires a *[decision record][DECISIONRECORD]*. A new shape on an existing class is a normal reviewed *[change][CHANGE]*. A shape's severity starts at Warning, and the owner moves it to Violation in a recorded follow-up. ### Consequences -- [+] The world file format is W3C-standard Turtle. Any RDF tool reads it, and no custom parser exists to maintain. -- [+] The gate grows through the extraction flywheel: an observed failure becomes a permanent shape. -- [+] The instance graph gives provenance queries for free once commits and verdicts enter it. -- [-] The rudof dependency is young (0.x). Mitigation: the shapes are standard SHACL, so pySHACL or Apache Jena can cross-check the same files off the gate path. -- [-] The deck learns nomenclature debt: contributors must learn five terms (triple, ontology, shape, IRI, closed world). The design document in the ontology-keeps-agents-honest change carries the primer. +- [+] The world file is in Turtle, a W3C standard. Any RDF tool reads it, and nobody maintains a custom parser. +- [+] The blocking check grows through the extraction flywheel: an observed failure becomes a permanent shape. +- [+] The instance graph answers provenance queries at no extra cost once commits and [verdicts][VERDICT] enter it. +- [-] The rudof dependency is young (0.x). The shapes are standard SHACL, so pySHACL or Apache Jena can cross-check the same files outside the blocking path. +- [-] The [repository][REPOSITORY] takes on nomenclature debt. A contributor must learn five terms: *[triple][TRIPLE]*, ontology, shape, IRI, and closed world. The design document in the ontology-keeps-agents-honest [change][CHANGE] contains the primer. ## More Information -- 2026-09-23: the namespace moved from `runedeck.dev` to `runedeck.ai`, the domain the project owns. No external data referenced the old IRIs, so this was the last cheap moment. The cli exporter, the two ontology files, and the smoke fixture changed together. +- 2026-09-23: the owner moved the namespace from `runedeck.dev` to `runedeck.ai`, the domain the project owns. No external data referenced the old IRIs, so this was the last cheap moment. The owner updated the cli exporter, the two ontology files, and the smoke *[fixture][FIXTURE]* together. +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[REPOSITORY]: ../specs/glossary/repository.md +[ARTIFACT]: ../specs/glossary/artifact.md +[MODEL]: ../specs/glossary/model.md +[CANON]: ../specs/glossary/canon.md +[SIDECAR]: ../specs/glossary/sidecar.md +[COMPANION]: ../specs/glossary/companion.md +[TEMPLATE]: ../specs/glossary/template.md +[VARIANT]: ../specs/glossary/variant.md +[STRUCTURESCHEMA]: ../specs/glossary/structure-schema.md +[TARGET]: ../specs/glossary/target.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[CHANGE]: ../specs/glossary/change.md +[TRIPLE]: ../specs/glossary/triple.md +[VERDICT]: ../specs/glossary/verdict.md +[RULE]: ../specs/glossary/rule.md +[FIXTURE]: ../specs/glossary/fixture.md diff --git a/docs/decisions/DECK-0011 Review Tooling Adoption.md b/docs/decisions/DECK-0011 Review Tooling Adoption.md index 9b6c072c..3482477b 100644 --- a/docs/decisions/DECK-0011 Review Tooling Adoption.md +++ b/docs/decisions/DECK-0011 Review Tooling Adoption.md @@ -9,7 +9,7 @@ tags: - tooling status: accepted created: 2026-09-01 -updated: 2026-09-20 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -26,39 +26,44 @@ change: review-tooling ## Context and Problem Statement -The seer review funnel holds every review semantic in bespoke workflow shell and Python. Plumbing that widely used packages already standardize still costs custom lines: posting findings inline, driving the API, provisioning labels, updating a status comment. Every finding in the funnel comes from a hosted vendor or the paid correctness lane. The goal is a small stack. Custom lines cover only the ceremony: instruction pinning, the write and approve identity split, the head-bound verdict. Everything else comes from maintained open source. +Seer, the review bot, keeps every review semantic in bespoke workflow shell and Python. Plumbing that widely used packages already standardize still costs custom lines. That plumbing posts findings inline, drives the API (application programming interface), provisions labels, and updates a status comment. Every finding in the *[review funnel][REVIEWFUNNEL]* comes from a hosted vendor or from the paid correctness *[agentic review lane][AGENTICREVIEWLANE]*. +The goal is a small stack. Custom lines cover only the ceremony. The ceremony pins the instructions, splits the identity that writes from the identity that approves, and binds the verdict to a head. Everything else comes from maintained open source. ## Decision Drivers - Fewer custom lines: each bespoke segment is a maintenance and audit liability -- Vendor independence: at least one reviewer lane must run on open code against a model the org picks +- Vendor independence: at least one [agentic review lane][AGENTICREVIEWLANE] must run on open code against a *[model][MODEL]* the organization picks - Maturity: the candidate ranking uses stars, age, release cadence, and open issues, measured on 2026-09-01 ([candidate table][TABLE]) ## Considered Options -1. **Keep the bespoke stack**: no new dependencies, every segment stays hand-written +1. **Keep the bespoke stack**: no new dependency, and every segment remains hand-written 2. **Hosted vendors only**: Cursor, Macroscope, and CodeRabbit supply findings, and the funnel adjudicates -3. **Adopt the open-source set**: standard packages for plumbing, one open-source reviewer lane, the ceremony stays custom +3. **Adopt the open-source set**: standard packages do the plumbing, one lane runs on open code, and the ceremony remains custom ## Decision Outcome -Option 3. The review funnel MUST use a maintained open-source package for plumbing, and MUST keep custom code only for the ceremony: instruction pinning, the split between the identity that writes and the identity that approves, and the verdict bound to a head. At least one reviewer lane MUST run on open code against a model the organization picks. Five tools, one role each: +Option 3. The [review funnel][REVIEWFUNNEL] MUST use a maintained open-source package for plumbing. It MUST keep custom code only for the ceremony. The ceremony pins the instructions, splits the identity that writes from the identity that approves, and binds the verdict to a head. At least one lane MUST run on open code against a [model][MODEL] the organization picks. Five tools follow, and each has one role: -- [reviewdog][REVIEWDOG] posts findings as review comments from the rdjsonl the adjudicator writes. Seer PR #35 adopted it. +- [reviewdog][REVIEWDOG] posts findings as review comments from the rdjsonl file the adjudicator writes. Seer pull request #35 adopted it. - [github-script][GHSCRIPT] drives the GitHub API where shell would parse JSON by hand (already adopted). -- [labeler][LABELER] provisions the per-lane label set from the lane table (already adopted for path labels). -- [create-or-update-comment][UPSERT] with [find-comment][FIND] upserts every workflow-authored status comment by header marker. -- [PR-Agent][PRAGENT] runs as the pr-agent lane in command mode from a pinned image: one `review` call per summon, findings posted under the workflow token, adjudicated like every other lane. +- [labeler][LABELER] provisions one label per lane from the *[lane table][LANETABLE]* (already adopted for path labels). +- [create-or-update-comment][UPSERT] with [find-comment][FIND] updates or creates every workflow-authored status comment by its header marker. +- [PR-Agent][PRAGENT] runs as the pr-agent lane in command mode from a pinned image. Each summon makes one `review` call. The findings post under the workflow token, and the adjudicator treats them like every other lane's. -PR-Agent runs in command mode, never in its GitHub Action mode. The Action reviews on pull request events, and the ceremony allows a lane to run only on a maintainer's label. The lane needs one org secret, a model API key. When the key is absent, the lane stops with a clear fault. +PR-Agent runs in command mode and never in its GitHub Action mode. The Action reviews on pull request events, and the ceremony allows a lane to run only on a maintainer's label. The pr-agent lane needs one organization secret, a [model][MODEL] API key. When the key is absent, it stops with a clear fault. ### Consequences -- [+] The comment publish paths, the lane waits, and the findings posting stop being custom code -- [+] The funnel keeps a reviewer that runs on open code against a model the org chooses -- [-] One more org secret and one more container image to pin and refresh -- [-] The change lives in the skeleton repository as `review-tooling`, so this record names a change that the deck does not hold. -- [-] A fourth lane lengthens the funnel by one model call, and `skip:pr-agent` stands it down +- [+] The comment publish paths, the lane waits, and the findings posting are no longer custom code +- [+] The funnel keeps a reviewer that runs on open code against a [model][MODEL] the organization chooses +- [-] One more organization secret and one more container image to pin and refresh +- [-] The *[change][CHANGE]* is in the skeleton *[repository][REPOSITORY]* as `review-tooling`, so this record cites a [change][CHANGE] that this [repository][REPOSITORY] does not contain. +- [-] A fourth lane lengthens the funnel by one [model][MODEL] call, and `skip:pr-agent` stands it down + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. [TABLE]: https://github.com/runedeck/skeleton/blob/main/docs/changes/review-tooling/design.md [REVIEWDOG]: https://github.com/reviewdog/reviewdog @@ -67,3 +72,9 @@ PR-Agent runs in command mode, never in its GitHub Action mode. The Action revie [UPSERT]: https://github.com/peter-evans/create-or-update-comment [FIND]: https://github.com/peter-evans/find-comment [PRAGENT]: https://github.com/The-PR-Agent/pr-agent +[REVIEWFUNNEL]: ../specs/glossary/review-funnel.md +[AGENTICREVIEWLANE]: ../specs/glossary/agentic-review-lane.md +[MODEL]: ../specs/glossary/model.md +[LANETABLE]: ../specs/glossary/lane-table.md +[CHANGE]: ../specs/glossary/change.md +[REPOSITORY]: ../specs/glossary/repository.md diff --git a/docs/decisions/DECK-0012 Consumer Ceremony Synchronization.md b/docs/decisions/DECK-0012 Consumer Ceremony Synchronization.md index cee34aed..67b34db3 100644 --- a/docs/decisions/DECK-0012 Consumer Ceremony Synchronization.md +++ b/docs/decisions/DECK-0012 Consumer Ceremony Synchronization.md @@ -9,7 +9,7 @@ tags: - provenance status: accepted created: 2026-09-11 -updated: 2026-09-11 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,31 +27,44 @@ change: use-single-repository-skeleton ## Context and Problem Statement -The deck adopted six prose and workflow linters and the jj push check before the skeleton template carried them. Its checks and the template disagreed in every shared file, its `answers.yaml` pinned a skeleton commit two months old, and the weekly ceremony audit reported the same intentional rows each week (deck#45). The cli pinned a tag that never existed on skeleton, so Copier could not update it at all. Skeleton now carries the linters, the push check, the divergence register, and seed-once files, so the consumers can converge. The open question was how to bring a diverged consumer back under the template without losing what it added. +The deck *[repository][REPOSITORY]* adopted six prose and workflow *[linters][LINTER]* and the push check for `jj` before the skeleton *[template][TEMPLATE]* had them. The [repository][REPOSITORY]'s checks and the [template][TEMPLATE] disagreed in every shared file. Its `answers.yaml` pinned a skeleton commit two months old. The weekly ceremony audit reported the same intentional rows each week (deck#45). + +The cli clone pinned a tag that never existed on skeleton, so Copier could not update the cli clone at all. Skeleton now has the [linters][LINTER], the push check, the divergence register, and seed-once files, so the consumers can converge on it. The open question was how the owner brings a diverged consumer back under the [template][TEMPLATE] without losing what the consumer added. ## Decision Drivers - Copier must do the merge, so the next update has a real baseline to diff from. -- Deck-only checks (mdschema skills, Stable shell schemas, ontology shapes, openspec, provenance digests) must survive the merge. -- Sealed adoption sidecars must stay valid after prose corrections, through the reviewed reseal path and never a regex rewrite. +- The merge must keep the deck [repository][REPOSITORY]'s own checks (mdschema skills, Stable shell schemas, ontology shapes, openspec, provenance digests). +- A sealed adoption *[sidecar][SIDECAR]* must remain valid after a prose correction. The author must reseal it through the reviewed reseal path and never through a regex rewrite. ## Considered Options -1. Trust `copier update` alone from the recorded pin and hand-resolve its conflicts. -2. Recover a historical baseline for each consumer and replay its additions. -3. Reset the recorded reference, compare a fresh render, and reconcile by hand. +1. The owner trusts `copier update` alone from the recorded pin and resolves its conflicts by hand. +2. The owner recovers a historical baseline for each consumer and replays its additions. +3. The owner resets the recorded reference and compares a fresh render. The owner then reconciles by hand. ## Decision Outcome -Chosen option: option 1 for the deck, whose `482ea1f` pin is a real skeleton commit, and option 1 for the cli after rewriting its phantom `v0.5.0` pin to the same baseline. Copier merges from the baseline to the new skeleton commit. Every file with both template and consumer edits is resolved by taking the template side and re-adding the consumer's own hooks, targets, excludes, and workflow steps on top. Files the template seeds once (`CHANGELOG.md`, `AGENTS.md`, `CONTRIBUTING.md`, `INSTALL.md`, `CODEOWNERS`, `.gitignore`) stay the consumer's. The result records the new full skeleton commit in `answers.yaml`. +The owner chose option 1 for both consumers. The deck [repository][REPOSITORY]'s `482ea1f` pin is a real skeleton commit. The cli clone gets option 1 after the owner rewrites its phantom `v0.5.0` pin to the same baseline. + +Copier merges from the baseline to the new skeleton commit. For every file with both [template][TEMPLATE] and consumer edits, the owner takes the [template][TEMPLATE] side. The owner then re-adds the consumer's own hooks, targets, excludes, and workflow steps on top. Files the [template][TEMPLATE] seeds once (`CHANGELOG.md`, `AGENTS.md`, `CONTRIBUTING.md`, `INSTALL.md`, `CODEOWNERS`, `.gitignore`) remain the consumer's. The result records the new full skeleton commit in `answers.yaml`. -Prose the generated Vale style rejects is corrected at the reported position, and every sealed file the corrections touch is resealed with `rune adopt reseal`, which preserves the source and review facts and rewrites only the subject digest. The Simplified Technical English example and reference files keep their deliberate errors through a path-scoped rule override in `.vale.ini`, not through edits. +The author corrects prose that the generated Vale style rejects at the reported position. The author reseals every sealed file the corrections touch with the reseal step, `rune adopt reseal`. The reseal preserves the source and review facts and rewrites only the subject digest. The Simplified Technical English example and reference files keep their deliberate errors. A path-scoped Vale override in `.vale.ini` protects them, and the author does not edit them. -Option 2 has no baseline to recover: the deck's additions arrived across dozens of commits. Option 3 leaves Copier with two equal references and applies nothing, so the next update would conflict on every line again. +Option 2 has no baseline to recover: the deck [repository][REPOSITORY]'s additions arrived across dozens of commits. Option 3 leaves Copier with two equal references and applies nothing, so the next update would conflict on every line again. ### Consequences -- [+] Copier updates from the recorded commit again, and the weekly parity audit compares against a real baseline. -- [+] Deck-only checks live as additions on top of the template's hook list. A future template change to a shared hook merges cleanly. A change to a deck-only hook is the deck's to make. -- [-] Every prose edit in a sealed file cost a reseal. The sidecars record the new digests with the original review facts intact. -- [-] The `.vale.ini` path overrides are deck-specific and will conflict on the next template change to that file. The divergence register does not cover it yet, because the register only approves whole-file differences. +- [+] Copier updates from the recorded commit again. The weekly parity audit compares against a real baseline. +- [+] The checks that only the deck [repository][REPOSITORY] runs sit as additions on top of the [template][TEMPLATE]'s hook list. A future [template][TEMPLATE] edit of a shared hook merges cleanly. An edit of one of those additions is the deck [repository][REPOSITORY]'s to make. +- [-] Every prose edit in a sealed file cost a reseal. The [sidecars][SIDECAR] record the new digests with the original review facts intact. +- [-] The `.vale.ini` path overrides belong to the deck [repository][REPOSITORY] alone, and they will conflict on the next [template][TEMPLATE] edit of that file. The divergence register does not cover them yet, because the register only approves whole-file differences. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[REPOSITORY]: ../specs/glossary/repository.md +[LINTER]: ../specs/glossary/linter.md +[TEMPLATE]: ../specs/glossary/template.md +[SIDECAR]: ../specs/glossary/sidecar.md diff --git a/docs/decisions/DECK-0013 Local Checks Before Publication.md b/docs/decisions/DECK-0013 Local Checks Before Publication.md index 400d11b4..b3ce8fd6 100644 --- a/docs/decisions/DECK-0013 Local Checks Before Publication.md +++ b/docs/decisions/DECK-0013 Local Checks Before Publication.md @@ -9,7 +9,7 @@ tags: - publication status: accepted created: 2026-09-14 -updated: 2026-09-14 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -26,30 +26,41 @@ change: all-checks-before-push ## Context and Problem Statement -Every runedeck repository declares its checks in `.pre-commit-config.yaml`. CI runs that file with every tool required, and the checked push hook replays it in an isolated checkout before `jj git push`. Sessions ran a hand-picked subset by hand, reported it as clean, and met the full set only at push time. On 2026-09-14 a `typos` finding in an adopted decision record and a base change with no proposal stopped two pushes while the owner waited at the signing key. The checks were right. The procedure ran them last instead of first. +Every runedeck *[repository][REPOSITORY]* declares its checks in `.pre-commit-config.yaml`. Continuous integration runs that file with every tool required. The checked push hook replays the file in an isolated checkout before `jj git push`. A session ran a hand-picked subset by hand, reported it as clean, and met the full set only at push time. On 2026-09-14 two pushes stopped while the owner waited at the signing key. One stopped on a `typos` finding in an adopted *[decision record][DECISIONRECORD]*, the other on a base *[change][CHANGE]* with no proposal. The checks were right. The procedure ran them last instead of first. ## Decision Drivers -- The check set is declared once. A second, hand-picked list drifts from it. -- The owner's presence at the key is the scarce step. Nothing that can fail should run after it starts. -- A skipped tool must fail, not pass silently. +- The [repository][REPOSITORY] declares the check set once. A second, hand-picked list drifts from it. +- The owner's presence at the key is the scarce step. Nothing that can fail belongs after that step starts. +- A skipped tool has to fail instead of passing silently. ## Considered Options -1. A skill that mandates the commit stage after each edit batch, the push stage on the frozen head, and a receipt per stage. The same mandate follows as a rule and as a specification requirement in the cli and skeleton. -2. Rely on the push hook and CI alone. +1. A *[skill][SKILL]* that mandates the commit stage after each edit batch, the push stage on the frozen head, and a receipt for each stage. The same mandate then becomes a *[rule][RULE]* and a specification *[requirement][REQUIREMENT]* in the cli and the skeleton. +2. Rely on the push hook and continuous integration alone. 3. Run the checks only at push time, as today, and tolerate the wait. ## Decision Outcome -Chosen option: the skill, with the rule and the specifications to follow. +Option 1: the [skill][SKILL] now, with the [rule][RULE] and the specifications to come later. -ContinuousIntegration runs `REQUIRE_GATES=1 make validate` after every edit batch and `REQUIRE_GATES=1 bash .githooks/jj-push -b --dry-run` on the frozen head. Both use the repository's configuration and require every tool. Each stage writes one log that ends with the check's own exit status, beside the candidate commit id. A finding is fixed and the sequence restarts. The signing step comes last. +The ContinuousIntegration [skill][SKILL] runs `REQUIRE_GATES=1 make validate` after every edit batch. It runs `REQUIRE_GATES=1 bash .githooks/jj-push -b --dry-run` on the frozen head. Both commands use the [repository][REPOSITORY]'s configuration and require every tool. Each stage writes one log that ends with the check's own exit status, beside the id of the candidate commit. The session fixes a finding and restarts the sequence. The signing step comes last. Option 2 finds the same defects, one push later, with the owner waiting. Option 3 is the state this decision replaces. ### Consequences -- [-] A session spends a few minutes per edit batch on checks it would otherwise meet once at the end, and the push reaches the key without a validation stop. -- [+] The skill names `make validate` and the checked push hook. A repository without either falls back to direct `prek` invocations, which the skill also gives. -- [-] The mandate is skill-level until the rule and the cli and skeleton specifications carry it. +- [-] A session spends a few minutes per edit batch on checks it would otherwise meet once at the end. In return, the push arrives at the key without a validation stop. +- [+] The [skill][SKILL] names `make validate` and the checked push hook. In a [repository][REPOSITORY] without either, the session calls `prek` directly, and the [skill][SKILL] gives those calls too. +- [-] The mandate lives only in the [skill][SKILL] until the [rule][RULE] and the cli and skeleton specifications state it. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[REPOSITORY]: ../specs/glossary/repository.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[CHANGE]: ../specs/glossary/change.md +[SKILL]: ../specs/glossary/skill.md +[RULE]: ../specs/glossary/rule.md +[REQUIREMENT]: ../specs/glossary/requirement.md diff --git a/docs/decisions/DECK-0014 Vocabulary Governance.md b/docs/decisions/DECK-0014 Vocabulary Governance.md index 4dcfaac8..2d8aae3b 100644 --- a/docs/decisions/DECK-0014 Vocabulary Governance.md +++ b/docs/decisions/DECK-0014 Vocabulary Governance.md @@ -9,7 +9,7 @@ tags: - evidence status: accepted created: 2026-09-15 -updated: 2026-09-15 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,34 +27,53 @@ change: terms-enter-ontology-first ## Context and Problem Statement -DECK-0010 declared the deck's world in RDF and made a decision record the price of a new class. It did not say who admits a term, how an artifact gets its name, or what happens when the corpus drifts. Naming the prove-stage skill on 2026-09-15 took a survey of agent-skill catalogs, CI vendors, philosophy, and governance vocabularies before the answer came from the deck's own lifecycle scheme, which already names the stage. The same session found four words for one prove-stage record: receipt, proof, attestation, evidence. The ontology has a class for measure-stage evidence, `rune:Verdict`, and none for the prove stage. +DECK-0010 declared the world of the *[repository][REPOSITORY]* in RDF (the Resource Description Framework) and made a *[decision record][DECISIONRECORD]* the price of a new class. It did not say who admits a term, how an *[artifact][ARTIFACT]* gets its name, or what happens when the corpus drifts. + +On 2026-09-15 a search looked for the name of the prove-stage *[skill][SKILL]*. The search went through catalogs of agent [skills][SKILL], continuous integration (CI) vendors, philosophy, and governance vocabularies. The answer came from the [repository][REPOSITORY]'s own lifecycle scheme, which already states the stage name. The same session found four words for one prove-stage record: receipt, *[proof][PROOF]*, *[attestation][ATTESTATION]*, evidence. The ontology has a class for measure-stage evidence, `rune:Verdict`, and none for the prove stage. ## Decision Drivers -- A term used in prose before it exists in the ontology is a synonym waiting to happen. -- An artifact named by taste is renamed later. A name derived from the stage table is stable. -- A rule or skill is paid for on every turn, and a vocabulary at density one is what keeps it short. -- The prove stage produces evidence that nothing in the graph can hold. +- A term that prose uses before the ontology defines it invites a synonym. The next author picks another word for the same thing. +- An [artifact][ARTIFACT] that the author names by taste gets renamed later. A name derived from the stage table is stable. +- The owner pays for a *[rule][RULE]* or a [skill][SKILL] on every turn. A vocabulary at density one, one word for each concept, shortens the [rule][RULE] or the [skill][SKILL]. +- The prove stage produces evidence, and no class in the graph can store it. ## Considered Options -1. A skill, AgenticOntology, that owns admission, naming, drift audit, and retirement, plus `rune:Proof` as the prove-stage evidence class with a shape. -2. A naming rule alone, loaded on every turn, with the ontology untouched. -3. Keep naming ad hoc and add classes when a change needs one. +1. A [skill][SKILL], AgenticOntology, owns admission, naming, drift audit, and retirement. The class `rune:Proof` with its shape stores the prove-stage evidence. +2. The owner adds a naming [rule][RULE] alone, loaded on every turn, and leaves the ontology untouched. +3. The owner keeps naming ad hoc and adds a class when a *[change][CHANGE]* needs one. ## Decision Outcome -Chosen option: the skill and the class. +Chosen option: the [skill][SKILL] and the class. + +AgenticOntology routes four acts. To admit a term, the author searches the corpus and then the established vocabularies in a fixed order. The author then creates the term with a superclass, a Warning shape, and a seeded smoke instance. To name an [artifact][ARTIFACT], the author joins the stage verb or the discipline noun to the entity the stage consumes. To audit drift, the [skill][SKILL] counts synonyms per label and labels per word and reports the result as a density, with a target of one. To retire a term, the author deprecates it, keeps an alternative label for one release, and then removes it. -AgenticOntology routes four acts. Admit a term: corpus search, established-vocabulary search in a fixed order, then mint with a superclass, a Warning shape, and a seeded smoke instance. Name an artifact: stage verb or discipline noun plus the entity the stage consumes. Audit drift: synonyms per label and labels per word, reported as a density with the target one. Retire a term: deprecate, alternative label for one release, remove. The lifecycle table, the density measure, and the vocabulary search order are companions. +The lifecycle table, the density measure, and the vocabulary search order are *[companions][COMPANION]* of the [skill][SKILL]. -`rune:Proof` is a `prov:Entity` that `proves` a candidate commit and carries one `proofKind` from a SKOS scheme, with `checks` and `behavior` as the first kinds. A candidate is proven when a proof exists for its commit, a query and not a stored state. The ContinuousIntegration receipt is the first Proof. +`rune:Proof` is a `prov:Entity`. It `proves` a candidate commit and has one `proofKind` from a SKOS (Simple Knowledge Organization System) scheme. The first kinds are `checks` and `behavior`. A candidate counts as proven when a [proof][PROOF] exists for its commit. A query answers that question, and no stored state does. The ContinuousIntegration receipt is the first [Proof][PROOF]. -Option 2 puts the rule in every context window and still leaves the ontology behind the prose. Option 3 is the state this decision replaces. +Option 2 puts the [rule][RULE] in every context window and still leaves the ontology behind the prose. Option 3 is the state this decision replaces. ### Consequences -- [-] A new concept costs one ontology edit and one shape before it costs any prose. -- [-] The density audit is manual until the extractor computes it, which is a recorded follow-up. -- [-] The skill names `rudof` and `rg`. A checkout without rudof cannot run the shape check, and the hook says so under `REQUIRE_GATES`. -- [-] The smoke fixture states four Violations and thirteen Warnings, and the hook asserts those counts. A missing property that a shape also constrains by value reports twice, which the fixture comment states. The permitted proof kinds are enumerated in the shape, because the hook validates the instance graph without the ontology loaded. +- [-] The author pays for a new concept with one ontology edit and one shape before writing any prose. +- [-] The owner runs the density audit by hand until the extractor computes it. That extractor is a recorded follow-up. +- [-] The [skill][SKILL] names two tools, `rudof` and `rg`. A checkout without rudof cannot run the shape check. The hook reports that under `REQUIRE_GATES`. +- [-] The smoke *[fixture][FIXTURE]* states four Violations and thirteen Warnings, and the hook asserts those counts. A missing property that a shape also constrains by value reports twice. The [fixture][FIXTURE] comment says so. The shape lists the permitted [proof][PROOF] kinds, because the hook validates the instance graph without the ontology loaded. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[REPOSITORY]: ../specs/glossary/repository.md +[DECISIONRECORD]: ../specs/glossary/decision-record.md +[ARTIFACT]: ../specs/glossary/artifact.md +[SKILL]: ../specs/glossary/skill.md +[COMPANION]: ../specs/glossary/companion.md +[PROOF]: ../specs/glossary/proof.md +[FIXTURE]: ../specs/glossary/fixture.md +[RULE]: ../specs/glossary/rule.md +[CHANGE]: ../specs/glossary/change.md +[ATTESTATION]: ../specs/glossary/attestation.md diff --git a/docs/decisions/DECK-0015 Behavior Proof per Change.md b/docs/decisions/DECK-0015 Behavior Proof per Change.md index c0890689..b6e05ee8 100644 --- a/docs/decisions/DECK-0015 Behavior Proof per Change.md +++ b/docs/decisions/DECK-0015 Behavior Proof per Change.md @@ -9,7 +9,7 @@ tags: - acceptance status: accepted created: 2026-09-15 -updated: 2026-09-15 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -27,31 +27,43 @@ change: record-proof-per-scenario ## Context and Problem Statement -DECK-0013 made the check stages the proof that a candidate is clean. A clean candidate can still do the wrong thing: every check passes on a feature that does not behave as its specification says. Reviewers then read the diff and infer behavior, or trust the pull request's prose. The specification already states the behavior as scenarios, and DECK-0014 gave the prove stage a `rune:Proof` with a `behavior` kind that nothing produces yet. A terminal-recording skill was proposed as a README demo helper (#64). A demo that asserts nothing is not evidence. +A clean candidate can still do the wrong thing, and this record makes a recorded *[proof][PROOF]* of its behavior part of every *[change][CHANGE]*. DECK-0013 made the check stages the [proof][PROOF] that a candidate is clean. Every check passes on a feature that does not behave as its specification says. A reviewer then reads the diff and infers the behavior, or trusts the prose of the pull request. The specification already states the behavior as *[scenarios][SCENARIO]*, and DECK-0014 gave the prove stage a `rune:Proof` with a `behavior` kind that nothing produces yet. The proposal in #64 was a terminal-recording *[skill][SKILL]* as a README demo helper. A demo that asserts nothing is not evidence. ## Decision Drivers -- The specification's scenarios are the acceptance criteria. A proof that walks them is a proof of the requirement, not of the implementation's self-image. -- A recording that a reviewer can watch, and a transcript a machine can grep, is evidence a diff is not. -- A demo without expectations passes when the feature is broken. +- The [scenarios][SCENARIO] of the specification are the acceptance criteria. A [proof][PROOF] that walks them proves the *[requirement][REQUIREMENT]* instead of the self-image of the implementation. +- A recording that a reviewer watches, and a transcript that a machine searches, are evidence. A diff is not. +- A demo without expectations passes when the feature does not work. ## Considered Options -1. AcceptanceTesting in core: one recorded scene per scenario, `expect` on every THEN, a failed expectation fails the recording, the GIF and transcript filed as the `behavior` proof, and VersionControl names it before every push. -2. The terminal-recording skill as proposed, a README demo helper with no assertions. -3. Rely on unit tests and reviewer reading. +1. AcceptanceTesting in core. The author records one *[scene][SCENES]* per [scenario][SCENARIO], with `expect` on every THEN. A failed expectation fails the recording. The author files the GIF and the transcript as the `behavior` [proof][PROOF], and the VersionControl [skill][SKILL] lists the [proof][PROOF] as a step before every push. +2. The terminal-recording [skill][SKILL] as proposed. It is a README demo helper with no assertions. +3. Unit tests and a reviewer's reading. ## Decision Outcome -Chosen option: AcceptanceTesting, with the mandate in the `record-proof-per-scenario` delta. +Option 1, AcceptanceTesting, with the mandate in the `record-proof-per-scenario` delta. -A change with user-visible behavior carries one scene per scenario of its delta specification. Each scene runs the WHEN steps against the built candidate and asserts each THEN with `expect`. The driver exits nonzero on a miss and `asciinema rec --return` carries that out, so the recording itself fails. The GIF lives under `docs/proofs//`, the pull request's Testing section embeds it with the commit id, and the proof record carries the scenario list, the exit status, and the transcript digest. A scenario without a scene is listed as unproven. +A [change][CHANGE] with user-visible behavior has one scene per [scenario][SCENARIO] of its *[delta specification][DELTASPECIFICATION]*. Each scene runs the WHEN steps against the built candidate and asserts each THEN with `expect`. The driver exits nonzero on a miss, and `asciinema rec --return` passes that exit status out. The recording itself then fails. The author stores the GIF under `docs/proofs//`. The Testing section of the pull request embeds the GIF with the commit id. The [proof][PROOF] record states the [scenario][SCENARIO] list, the exit status, and the transcript digest. The [proof][PROOF] lists a [scenario][SCENARIO] without a scene as unproven. -Option 2 records what happened without saying what should have. Option 3 is the state this decision replaces. +Option 2 records what happened without stating what the feature must do. Option 3 is the state this decision replaces. ### Consequences -- [-] An implementation is not done until its scenes pass, which adds a recording pass to every change with behavior. A prose-only change carries no proof and says so. -- [-] The proof is only as good as the scenarios. A specification without scenarios cannot be proven, which is a defect in the specification. -- [-] The skill depends on asciinema, agg, and ffmpeg, which `scripts/install-tools` does not install. The skill's prerequisites name the Homebrew formulae. -- [+] The `behavior` proof kind in `rune:proofKinds` gains its producer. +- [-] An implementation is not done until its [scenes][SCENES] pass. This adds a recording pass to every [change][CHANGE] with behavior. A prose-only [change][CHANGE] has no [proof][PROOF] and says so. +- [-] The [proof][PROOF] is only as good as the [scenarios][SCENARIO]. Nobody can prove a specification without [scenarios][SCENARIO], and that is a defect in the specification. +- [-] The [skill][SKILL] depends on asciinema, agg, and ffmpeg, and `scripts/install-tools` does not install them. The prerequisites of the [skill][SKILL] list the Homebrew formulae. +- [+] The `behavior` [proof][PROOF] kind in `rune:proofKinds` gains its producer. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[PROOF]: ../specs/glossary/proof.md +[CHANGE]: ../specs/glossary/change.md +[SCENARIO]: ../specs/glossary/scenario.md +[SKILL]: ../specs/glossary/skill.md +[REQUIREMENT]: ../specs/glossary/requirement.md +[SCENES]: ../specs/glossary/scenes.md +[DELTASPECIFICATION]: ../specs/glossary/delta-specification.md diff --git a/docs/decisions/DECK-0017 Sealed Review Ceremony.md b/docs/decisions/DECK-0017 Sealed Review Ceremony.md index 40bda405..7b6610e6 100644 --- a/docs/decisions/DECK-0017 Sealed Review Ceremony.md +++ b/docs/decisions/DECK-0017 Sealed Review Ceremony.md @@ -9,7 +9,7 @@ tags: - signing status: accepted created: 2026-09-19 -updated: 2026-09-23 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -28,75 +28,69 @@ upstream: ## Context and Problem Statement -The skeleton's review ceremony held that the owner's hardware key enters at tags, not merges, and -that merging demands no signature ritual beyond the platform's own. The cli's `rune sign` -proposal cites a "CORE-0013 Attribution Signing and Merge Ceremony" for that rule. No decision of -that title exists in the deck, so the choice was recorded only in the skeleton spec. A read of -21 pull requests across deck and cli on 2026-09-19 found the loop open at both ends: free-lane -findings sat unanswered under clean adjudicator verdicts, three pull requests merged with the -loop open, one before any lane reported, paid rounds had no bound, and the owner's stored login -was the only thing standing between an agent and a pull request in the owner's name. +The skeleton's review ceremony said that the owner's hardware key enters at tags and not at merges. It also said that merging demands no signature ritual beyond the platform's own. The cli's `rune sign` proposal cites a "CORE-0013 Attribution Signing and Merge Ceremony" for that policy. No decision of that title exists in the *[repository][REPOSITORY]*, so only the skeleton specification recorded the choice. + +A read of 21 pull requests across the [repository][REPOSITORY] and the cli clone on 2026-09-19 found the loop open at both ends. Findings from a free *[agentic review lane][AGENTICREVIEWLANE]* sat unanswered under clean adjudicator verdicts. Three pull requests merged with the loop open, one before any [lane][AGENTICREVIEWLANE] reported. Paid rounds had no bound. The owner's stored login was the only thing between a *[harness][HARNESS]* and a pull request in the owner's name. ## Decision Drivers -- A pull request in the owner's name is an attestation, and an attestation needs the key +- A pull request in the owner's name is a statement by the owner, and such a statement needs the key - Every finding from every lane must end in a recorded disposition, or the verdict is a fault -- Paid review must be bounded per work item and never started by an agent -- Every vendor lane can be switched off on any day, so none may be load-bearing -- Merging with the loop open must be impossible by ruleset, not by habit +- Paid review must have a bound per *[work item][WORKITEM]*, and a [harness][HARNESS] must never start it +- A vendor can switch off its lane on any day, so no lane may be load-bearing +- The ruleset, not habit, must make merging with the loop open impossible ## Considered Options -1. **Keep the tag-only key and the owner click.** Cheapest. Leaves impersonation through the - stored login open and leaves the loop closable by habit only. -2. **One merge-seal only.** Closes the merge, leaves opening a pull request in the owner's name - unattested. -3. **Two seals and a controller ledger.** A draft opens under the app identity on the first - accepted push. `rune sign open` signs an open-seal bound to repository, base, tree, and a - single-use nonce and flips the draft ready. Ready invites the controller's triage. The paid - lane judges a pinned `(reviewed_sha, generation)` under three rounds per work item and must - dispose of every ledger thread. `rune sign next` signs a merge-seal as an empty child of - `reviewed_sha`. The ruleset requires both seals and the controller's check. +1. **Keep the tag-only key and the owner click.** It is the cheapest. It leaves impersonation through the stored login open, and only habit closes the loop. +2. **One *[merge seal][MERGESEAL]* only.** It closes the merge. Opening a pull request in the owner's name stays unattested. +3. **Two seals and a controller ledger**, where a draft opens under the app identity on the first accepted push. The *[signer][SIGNER]* (`rune sign open`) signs an *[open seal][OPENSEAL]* bound to the [repository][REPOSITORY], the base, the tree, and a single-use nonce. The [signer][SIGNER] then flips the draft to ready, which invites the triage of the *[review controller][REVIEWCONTROLLER]*. The paid lane judges a pinned `(reviewed_sha, generation)` under three rounds per [work item][WORKITEM], and it must dispose of every ledger thread. The [signer][SIGNER] (`rune sign next`) signs a [merge seal][MERGESEAL] as an empty child of `reviewed_sha`. The ruleset requires both seals and the controller's check. ## Decision Outcome -Option 3, as three adversarial passes bound it: astra, grok, and lumo on the first draft, astra -and grok on the change documents, and three agents on trust boundaries, rollout, and provider -behavior on the final design. The skeleton specs `review-ceremony`, `review-requests`, `review-lanes`, and -`release-ceremony` are edited in place to state it. The glossary carries `ledger`, -`open-seal`, `merge-seal`, `controller`, and `work item`. The spec history is rewritten at the -owner's instruction. This record is the trail. - -Bound into the design and now requirements: - -- The open-seal binds one pull request through a nonce. -- The merge-seal never moves `reviewed_sha`. -- The ledger is the controller's, built from the API by lane login, with a status per expected - lane and a generation that any change increments. -- `KEYS`, the verifier, and the lane table come from the protected branch. -- Agents push branches after the prek pipeline and never open, ready, close, merge, label, - resolve, or comment. -- The session agent may invoke `rune sign`. The touch is the owner's. +Option 3, as three adversarial passes bound it. The *[models][MODEL]* astra, grok, and lumo attacked the first draft. The [models][MODEL] astra and grok attacked the documents of the *[change][CHANGE]*. Three [harness][HARNESS] sessions attacked the final design on trust boundaries, rollout, and provider behavior. + +The skeleton specifications `review-ceremony`, `review-requests`, `review-lanes`, and `release-ceremony` now state it, edited in place. The glossary defines `ledger`, `open-seal`, `merge-seal`, `controller`, and `work item`. The owner instructed a rewrite of the specification history. This record is the trail. + +The design binds these points, and they are now requirements: + +- The [open seal][OPENSEAL] binds one pull request through a nonce. +- The [merge seal][MERGESEAL] never moves `reviewed_sha`. +- The ledger belongs to the controller. The controller builds it from the API (application programming interface) by lane login. It records a state for each expected lane and a generation that any update increments. +- `KEYS`, the verifier, and the *[lane table][LANETABLE]* come from the protected branch. +- A [harness][HARNESS] pushes a branch after the prek pipeline. It never opens, readies, closes, merges, labels, resolves, or comments. +- The [harness][HARNESS] session that the owner works in may invoke `rune sign`. The touch is the owner's. - `free lanes only` is a recorded coverage state and a valid queue entry. -- Outside pull requests enter through `rune sign adopt`. +- An outside pull request enters through `rune sign adopt`. ### Consequences -- [+] Two key touches per pull request, each authorizing a displayed action +- [+] The owner touches the key twice per pull request, and each touch authorizes a displayed action - [+] The stored owner login can open a pull request, and that pull request cannot merge -- [+] A lane that vanishes leaves a recorded status, not a hole -- [-] `rune sign open`, `adopt`, `owner-seal`, the controller ledger, and the triage do not exist - yet. Until they do, the deck's rules describe a ceremony the tooling cannot enforce +- [+] A lane that vanishes leaves a recorded state instead of a hole +- [-] `rune sign open`, `adopt`, `owner-seal`, the controller ledger, and the triage do not exist yet. Until they do, the *[rules][RULE]* of the *[deck][DECK]* describe a ceremony the tooling cannot enforce - [-] The rune-sign proposal's citation of a CORE-0013 signing decision is stale and points here +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + ## Audit -- 2026-09-19: created, superseding the tag-only rule in the skeleton `release-ceremony` spec. - The route to enforcement is the workshop's `docs/specs/2026-09-19-review-loop-plan.md`. -- 2026-09-19: the ledger's carrier fixed. The full ledger is a workflow artifact. Its public - record is a check run named `ledger` on `reviewed_sha` under the reviewing app, whose first - line names the artifact, its sha256, the generation, and the pull request. Only the creating - app can edit a check run, so the line is the controller's word without a comment or a commit. - The merge-seal binds the digest, so a rebuilt ledger at the same generation unseals. The - reviewing app carries `checks: write` for it. -- 2026-09-23: accepted by the owner. The decision stands while the tooling is pending. A record under `docs/decisions/` is never proposed. +- 2026-09-19: the author created this record, which supersedes the tag-only policy in the skeleton `release-ceremony` specification. The route to enforcement is the workshop's `docs/specs/2026-09-19-review-loop-plan.md`. +- 2026-09-19: the author fixed the ledger's carrier. The full ledger is a workflow artifact. Its public record is a check run named `ledger` on `reviewed_sha` under the reviewing app, which has the `checks: write` permission for it. The first line of that check run names the artifact, its sha256, the generation, and the pull request. Only the creating app can edit a check run, so the line is the controller's word without a comment or a commit. The [merge seal][MERGESEAL] binds the digest, so a rebuilt ledger at the same generation unseals. +- 2026-09-23: the owner accepted the record. The decision stands while the tooling is pending. A record under `docs/decisions/` is never proposed. + +[REPOSITORY]: ../specs/glossary/repository.md +[AGENTICREVIEWLANE]: ../specs/glossary/agentic-review-lane.md +[SIGNER]: ../specs/glossary/signer.md +[REVIEWCONTROLLER]: ../specs/glossary/review-controller.md +[LANETABLE]: ../specs/glossary/lane-table.md +[DECK]: ../specs/glossary/deck.md +[WORKITEM]: ../specs/glossary/work-item.md +[MERGESEAL]: ../specs/glossary/merge-seal.md +[OPENSEAL]: ../specs/glossary/open-seal.md +[HARNESS]: ../specs/glossary/harness.md +[MODEL]: ../specs/glossary/model.md +[CHANGE]: ../specs/glossary/change.md +[RULE]: ../specs/glossary/rule.md diff --git a/docs/decisions/DECK-0018 Keep Shared Skill Procedures Independent of Harness Syntax.md b/docs/decisions/DECK-0018 Keep Shared Skill Procedures Independent of Harness Syntax.md index 5aabac1c..752444ed 100644 --- a/docs/decisions/DECK-0018 Keep Shared Skill Procedures Independent of Harness Syntax.md +++ b/docs/decisions/DECK-0018 Keep Shared Skill Procedures Independent of Harness Syntax.md @@ -6,7 +6,7 @@ category: deck tags: [skills, portability, authoring] status: accepted created: 2026-09-11 -updated: 2026-09-11 +updated: 2026-10-19 author: "@N4M3Z" project: runedeck responsible: ["@N4M3Z"] @@ -21,26 +21,40 @@ change: skills-portable-across-harnesses ## Context and Problem Statement -The generic layer cannot provide consistent instructions when it requires tools from a particular harness. -The authoring guide must enforce the same boundary that the validator checks. +The generic layer cannot give consistent *[instructions][INSTRUCTION]* when it requires tools from one *[harness][HARNESS]*. +The authoring guide must enforce the same boundary that the *[validator][VALIDATOR]* checks. -This ADR remains an unnumbered proposal pending maintainer review. +This *[architecture decision record][ARCHITECTUREDECISIONRECORD]* (ADR) remains an unnumbered proposal pending maintainer review. ## Considered Options -1. Treating native commands as harmless text leaves their execution meaning unclear. -2. Suppressing their findings would preserve the contradiction between the authoring guide and the validator. -3. Duplicating all workflow text would create multiple places to maintain the same procedure. +1. The author treats native commands as harmless text. What each command does when a [harness][HARNESS] runs it then remains unclear. +2. The author suppresses the [validator][VALIDATOR]'s findings. The contradiction between the authoring guide and the [validator][VALIDATOR] then remains. +3. The author duplicates all workflow text. Several places then repeat the same procedure, and each one needs maintenance. ## Decision Outcome -Use portable capability instructions in generic entrypoints and companions. -Keep native scopes and procedures in explicit harness entrypoint variants. -Require the layer gate before declaring authoring compliance. -Preserve native evidence as a separate acceptance condition. +The author writes the generic entrypoint (the *[canon][CANON]*) and the generic *[companions][COMPANION]* as portable capability [instructions][INSTRUCTION]. +The author keeps native scopes and procedures in an explicit entrypoint *[variant][VARIANT]* for that [harness][HARNESS]. +The layer check must pass before the author declares authoring compliance. +The author keeps native evidence as a separate acceptance condition. ### Consequences -- [+] Shared instructions remain usable without a fixed harness tool catalog. -- [+] Provider-specific metadata stays with its provider. -- [+] The change requires no new assembly mechanism and grants no deployment or approval authority. +- [+] Shared [instructions][INSTRUCTION] remain usable without a fixed catalog of [harness][HARNESS] tools. +- [+] Metadata specific to one [harness][HARNESS] remains with that [harness][HARNESS]. +- [+] The *[change][CHANGE]* does not need a new *[assembly][ASSEMBLY]* mechanism. It grants neither deployment nor approval authority. + +## More Information + +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[INSTRUCTION]: ../specs/glossary/instruction.md +[HARNESS]: ../specs/glossary/harness.md +[VALIDATOR]: ../specs/glossary/validator.md +[CANON]: ../specs/glossary/canon.md +[COMPANION]: ../specs/glossary/companion.md +[VARIANT]: ../specs/glossary/variant.md +[ARCHITECTUREDECISIONRECORD]: ../specs/glossary/architecture-decision-record.md +[CHANGE]: ../specs/glossary/change.md +[ASSEMBLY]: ../specs/glossary/assembly.md diff --git a/docs/decisions/DECK-0019 A Proof Plays as a Cast Where a Person Reads It.md b/docs/decisions/DECK-0019 A Proof Plays as a Cast Where a Person Reads It.md index cb26d22d..1a6a56d8 100644 --- a/docs/decisions/DECK-0019 A Proof Plays as a Cast Where a Person Reads It.md +++ b/docs/decisions/DECK-0019 A Proof Plays as a Cast Where a Person Reads It.md @@ -9,7 +9,7 @@ tags: - skill status: accepted created: 2026-09-20 -updated: 2026-09-21 +updated: 2026-10-19 author: "@N4M3Z" project: deck related: @@ -26,28 +26,33 @@ change: playable-proof-recordings-page ## Context and Problem Statement -A proof is recorded as a cast and rendered as a GIF. The GIF is what a pull request body can show, and it is what the owner watched. It loops, cannot pause, and never says which scene is on screen. The cast has the scene titles already. +The driver records a *[proof][PROOF]* as a cast and renders a GIF image from it. A pull request body can show the GIF, and the owner watched the GIF. The GIF loops, cannot pause, and never says which scene is on screen. The cast has the scene titles already. ## Considered Options -1. Render the GIF with a scene counter burned into each frame. +1. Render the GIF with a scene counter drawn into each frame. 2. Play the cast on the page with a player that pauses, scrubs, and names the scene. 3. Both: the player where a person reads, the GIF where only an image renders. ## Decision Outcome -Option 3. The player is one vanilla script in html-tools, tested without a browser, and reads the scene titles from the driver's own `# Scenario:` lines. The GIF stays because a pull request body renders images only. +Option 3. The player is one script in html-tools with no framework behind it. Its tests run without a browser. The player reads the scene titles from the driver's own `# Scenario:` lines. The GIF stays, because a pull request body renders only images. -The skill MUST keep these rules: +The *[skill][SKILL]* MUST keep these rules: -- A page that shows a proof MUST play the cast through the html-tools player. -- The GIF MUST remain beside the cast in the repository. +- A page that shows a [proof][PROOF] MUST play the cast through the html-tools player. +- The GIF MUST remain beside the cast in the *[repository][REPOSITORY]*. ### Consequences -- [-] A page that shows proofs vendors html-tools. The workshop's proof page of 2026-09-20 inlines the script instead, because it is one file with no repository of its own. -- [-] The scene marker is the driver's comment line. A recording made outside the driver has no scenes, only a scrubber. +- [-] A page that shows a [proof][PROOF] has to include a copy of html-tools. The workshop's [proof][PROOF] page of 2026-09-20 inlines the script instead, because the page is one file with no [repository][REPOSITORY] of its own. +- [-] The driver's comment line is the scene marker. A recording made outside the driver has no scenes. The player then shows only a scrubber. ## More Information -- Recorded proofs `docs/proofs/playable-proof-recordings-page/` (pause and scene label under node) and `docs/proofs/convenient-proof-page-viewer/` (the page generator on a five-scene cast), 2026-09-21. +- Recorded [proofs][PROOF] `docs/proofs/playable-proof-recordings-page/` (pause and scene label under node) and `docs/proofs/convenient-proof-page-viewer/` (the page generator on a five-scene cast), 2026-09-21. +- Rewritten on 2026-10-19 in the plain spoken register. The decision is unchanged. + +[PROOF]: ../specs/glossary/proof.md +[SKILL]: ../specs/glossary/skill.md +[REPOSITORY]: ../specs/glossary/repository.md From 2cf42a3afedf61e8a87307629d4f451c39d615af Mon Sep 17 00:00:00 2001 From: "Claude Fable 5.1 (claude-fable-5-1)" Date: Mon, 19 Oct 2026 15:56:48 +0200 Subject: [PATCH 3/8] chore(quality): a fresh branch passes the commit stage on every file The first push of a branch checks every file, and three items on main failed there: the idea snapshot check needs the Obsidian CLI, which no runner has, so the commit stage skips it on CI and it stays a local gate; a table in the agentic-first-pipeline-rules design had no blank line after it; and two delta specs carried front matter above their first heading, which the current OpenSpec refuses in strict mode while every other delta spec goes without it. Co-Authored-By: Martin Zeman --- .github/workflows/quality.yaml | 8 ++++++-- docs/changes/agentic-first-pipeline-rules/design.md | 1 + .../specs/routine-prompts-from-deck/spec.md | 4 ---- .../specs/scanner-routines-from-deck/spec.md | 4 ---- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index a7803dd3..4c59fc18 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -150,6 +150,10 @@ jobs: # The prek CLI is called directly: the wrapper action logs at # TRACE level and buries the failing hook's output. REQUIRE_GATES # turns a missing linter into a failure instead of a skip. + # The idea snapshot check reads the owner's vault through the + # Obsidian CLI, which no runner has, so it stays a local gate: + # a fresh branch's first push checks every file and would fail + # on it under REQUIRE_GATES. - name: Commit-stage checks env: REQUIRE_GATES: "1" @@ -158,9 +162,9 @@ jobs: TO_REF: ${{ steps.check-range.outputs.to }} run: | if [ "$RANGE_USABLE" = "true" ]; then - prek run --from-ref "$FROM_REF" --to-ref "$TO_REF" + prek run --skip sync-idea --from-ref "$FROM_REF" --to-ref "$TO_REF" else - prek run --all-files + prek run --skip sync-idea --all-files fi # Scan the same range at the pre-push stage. Without a usable base, diff --git a/docs/changes/agentic-first-pipeline-rules/design.md b/docs/changes/agentic-first-pipeline-rules/design.md index 039d601d..92dcbcf1 100644 --- a/docs/changes/agentic-first-pipeline-rules/design.md +++ b/docs/changes/agentic-first-pipeline-rules/design.md @@ -74,6 +74,7 @@ A requirement names a role. This table names the command or link form behind eac |---|---|---| | Pushback before scaffold | `rune spec propose` | Creates the change directory once the challenge pass leaves a sharpened intent standing. | | Any medium, one entry point | `obsidian://` | The link form a drawing in the owner's vault arrives in. The Obsidian CLI reads it. | + A requirement names a role. This table names the program and hook behind each requirement, so nothing is lost when a command moves out of a requirement. | Requirement | Command | Notes | diff --git a/docs/changes/routine-prompts-from-deck/specs/routine-prompts-from-deck/spec.md b/docs/changes/routine-prompts-from-deck/specs/routine-prompts-from-deck/spec.md index cc53f896..60f4d323 100644 --- a/docs/changes/routine-prompts-from-deck/specs/routine-prompts-from-deck/spec.md +++ b/docs/changes/routine-prompts-from-deck/specs/routine-prompts-from-deck/spec.md @@ -1,7 +1,3 @@ ---- -type: spec ---- - ## ADDED Requirements ### Requirement: Routine file is the behavior source diff --git a/docs/changes/scanner-routines-from-deck/specs/scanner-routines-from-deck/spec.md b/docs/changes/scanner-routines-from-deck/specs/scanner-routines-from-deck/spec.md index 7f32638e..b1cb80ed 100644 --- a/docs/changes/scanner-routines-from-deck/specs/scanner-routines-from-deck/spec.md +++ b/docs/changes/scanner-routines-from-deck/specs/scanner-routines-from-deck/spec.md @@ -1,7 +1,3 @@ ---- -type: spec ---- - ## ADDED Requirements ### Requirement: Detector decides findings and status in code From 6eeaa49111c3f189abb98c72820b35921fbf688e Mon Sep 17 00:00:00 2001 From: "Codex Gpt 5.6 Sol (gpt-5.6-sol)" Date: Mon, 19 Oct 2026 18:03:53 +0200 Subject: [PATCH 4/8] open-seal: {"repo":"runedeck/deck","base":"main","pull_request":76,"tree":"866617e821e044c4cf3e03af762cbb3df1fae204","nonce":"53c75b736e91701aa68c288f9d10212a93871e9347e845eaddadac4790e611ab"} From e2523e9a64b575eead93ae5a5ccbcfd20a7fe27d Mon Sep 17 00:00:00 2001 From: "Claude Fable 5.1 (claude-fable-5-1)" Date: Mon, 19 Oct 2026 18:31:50 +0200 Subject: [PATCH 5/8] fix(contract): the portability check builds the cli that accepts glossary links The contract workflow pins its own cli candidate, and that pin still built the rune that reports a term link into the repository glossary as an escape. It now builds the signed head that strips term links in assembly and accepts the reference form. The ReviewChange canon named one harness tool by its literal name in two places, which the same check refuses; it now says the harness choice prompt. Co-Authored-By: Martin Zeman --- .github/workflows/safetyfirst.yaml | 2 +- runes/core/skills/ReviewChange/SKILL.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/safetyfirst.yaml b/.github/workflows/safetyfirst.yaml index 16b7957a..d2a2563d 100644 --- a/.github/workflows/safetyfirst.yaml +++ b/.github/workflows/safetyfirst.yaml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: repository: runedeck/cli - ref: 7412b6acf037420129c8ac45759d4c75af0e96af + ref: 1cea8c7995591518df25687bc1203e45524ce624 path: .validation-cli persist-credentials: false - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable diff --git a/runes/core/skills/ReviewChange/SKILL.md b/runes/core/skills/ReviewChange/SKILL.md index 696e3aa2..96f34347 100644 --- a/runes/core/skills/ReviewChange/SKILL.md +++ b/runes/core/skills/ReviewChange/SKILL.md @@ -13,7 +13,7 @@ The owner decides a specification one *[requirement][REQUIREMENT]* at a time. Yo - The [change][CHANGE] directory with its specifications, the `design.md` coverage table, the *[decision records][DECISIONRECORD]* it cites, and any reviewer verdicts. - The glossary pages, so you can explain a term inside a [requirement][REQUIREMENT] from its definition. -- One channel, either the terminal with `AskUserQuestion` or voice through a project built from voice-safe files. +- One channel, either the terminal with the harness's choice prompt or voice through a project built from voice-safe files. ## Constraints @@ -65,7 +65,7 @@ At the end, or when the owner asks, read every decision back as prose. Write one ## References -- The terminal form: `AskUserQuestion` with four [requirements][REQUIREMENT] per call and a header such as `N/32`. Each item gets one summary line. Do not copy the [requirement][REQUIREMENT] over. The options are `Keep` plus three concrete proposed [changes][CHANGE], and `Other` for the owner's own words. Keep the state in a `state.json` in the workshop's spec directory so the walk resumes. Discuss after every batch before the next one. +- The terminal form: the harness's choice prompt with four [requirements][REQUIREMENT] per call and a header such as `N/32`. Each item gets one summary line. Do not copy the [requirement][REQUIREMENT] over. The options are `Keep` plus three concrete proposed [changes][CHANGE], and `Other` for the owner's own words. Keep the state in a `state.json` in the workshop's spec directory so the walk resumes. Discuss after every batch before the next one. - The voice form: a project built from voice-safe files. Such a file has no index ids and reads as narrative under headings. It has no tables, lists, code, paths, extensions, or abbreviations. Each section is self-contained, and the file stays under twelve kilobytes. - The SelfContainedQuestionOptions rule: describe each option's [change][CHANGE] in plain words. From 3d809039b78a764ceb2d0d75e084dc0374eb0905 Mon Sep 17 00:00:00 2001 From: "Claude Fable 5.1 (claude-fable-5-1)" Date: Mon, 19 Oct 2026 18:36:26 +0200 Subject: [PATCH 6/8] fix(terms): plurals in ies, no match inside a URL or a tag, and a probe that reads the help Review findings on the pull request. The fallback matcher built only `capabilitys` and `capabilityes` for a label that ends in y, so `capabilities` and `repositories` passed unlinked; it now takes `ies`. A term inside a bare URL, an autolink, or an HTML tag counted as prose and the fixer would have rewritten the address; both are masked now. The two scripts handed over to any installed rune that had `spec fix`, including the older build without the every-occurrence rule; they now hand over only when that rune's own help says it links every occurrence. The ReviewChange skill wrote the verb "changes" as a link to the change term; the sentence now names the owner reversing a decision. Co-Authored-By: Martin Zeman --- runes/core/skills/ReviewChange/SKILL.md | 2 +- scripts/fallback/check-terms | 3 +- scripts/fallback/fix-terms | 3 +- scripts/fallback/termlib.py | 37 +++++++++++++++++++++---- tests/test_fallback_terms.py | 6 ++-- 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/runes/core/skills/ReviewChange/SKILL.md b/runes/core/skills/ReviewChange/SKILL.md index 96f34347..60dd2c23 100644 --- a/runes/core/skills/ReviewChange/SKILL.md +++ b/runes/core/skills/ReviewChange/SKILL.md @@ -23,7 +23,7 @@ The owner decides a specification one *[requirement][REQUIREMENT]* at a time. Yo - Never skip past "I do not understand" or "I have no idea what you just said". Re-explain with the four-part template first, then ask again. - Before the next [requirement][REQUIREMENT], discuss the owner's answers and answer the owner's questions. The owner's words: "always before you move on, I need you to discuss with me and answer my questions". - A verdict binds to the [requirement][REQUIREMENT]'s current text. When the owner [changes][CHANGE] the text, say what the new text claims before you move on. -- When a decision [changes][CHANGE] during the walk, the readback states only the final decision and marks the reversal where it matters. +- When the owner reverses a decision during the walk, the readback states only the final decision and marks the reversal where it matters. ## Instructions diff --git a/scripts/fallback/check-terms b/scripts/fallback/check-terms index c2bf8a0a..8c81b964 100755 --- a/scripts/fallback/check-terms +++ b/scripts/fallback/check-terms @@ -105,8 +105,7 @@ def check(root): def rune_has_link_model(): - rune = shutil.which("rune") - return bool(rune) and subprocess.run([rune, "spec", "fix", "-h"], capture_output=True, check=False).returncode == 0 + return termlib.rune_links_every_occurrence() def rune_findings(): diff --git a/scripts/fallback/fix-terms b/scripts/fallback/fix-terms index 22f4bac9..53c011c1 100755 --- a/scripts/fallback/fix-terms +++ b/scripts/fallback/fix-terms @@ -188,8 +188,7 @@ def fix_terms(root, index): def rune_has_fix(): - rune = shutil.which("rune") - return bool(rune) and subprocess.run([rune, "spec", "fix", "-h"], capture_output=True, check=False).returncode == 0 + return termlib.rune_links_every_occurrence() def main(): diff --git a/scripts/fallback/termlib.py b/scripts/fallback/termlib.py index 2f16cee6..c050d8b2 100644 --- a/scripts/fallback/termlib.py +++ b/scripts/fallback/termlib.py @@ -14,6 +14,8 @@ import os import pathlib import re +import shutil +import subprocess NS = "https://runedeck.ai/ns#" ONTOLOGY = pathlib.Path("ontology") / "rune.ttl" @@ -62,6 +64,10 @@ # A leading YAML block closed by `---` or `...`, as rune's prose view sees it. FRONT_MATTER = re.compile(r"\A---[ \t]*\n.*?^(?:---|\.\.\.)[ \t]*$", re.MULTILINE | re.DOTALL) SCHEME = re.compile(r"^[a-z][a-z0-9+.-]*:") +# A bare URL and anything in angle brackets (an autolink, an HTML tag with +# its attributes) are not prose, so a term inside them is never linked. +BARE_URL = re.compile(r"\b[a-z][a-z0-9+.-]*://[^\s<>\]\)]+", re.IGNORECASE) +ANGLE_SPAN = re.compile(r"<[^<>\n]*>") Term = collections.namedtuple("Term", "label precedence name kind comment status") Finding = collections.namedtuple("Finding", "path line severity name message") @@ -274,9 +280,10 @@ def destination_end(line, start): def prose_mask(line): """True for each character of a line that is not prose: a term link, a - legacy citation, text in brackets, and an inline-link destination.""" + legacy citation, text in brackets, an inline-link destination, a bare + URL, and an autolink or HTML tag in angle brackets.""" masked = [False] * len(line) - for pattern in (REF_LINK, LEGACY): + for pattern in (REF_LINK, LEGACY, BARE_URL, ANGLE_SPAN): for match in pattern.finditer(line): masked[match.start():match.end()] = [True] * (match.end() - match.start()) depth, i = 0, 0 @@ -298,10 +305,17 @@ def prose_mask(line): def label_pattern(label): """A whole-word, case-insensitive pattern for a label and its plural: - `s` or `es`, and `es` alone for a label that ends in s (`harnesses`). + `s` or `es`, `es` alone for a label that ends in s (`harnesses`), and + `ies` for a label that ends in a consonant and y (`capabilities`). It matches the source text itself, so offsets hold for any letter.""" - plural = "(?:es)?" if label.lower().endswith("s") else "(?:es|s)?" - return re.compile(r"(? 1 and lower.endswith("y") and lower[-2] not in "aeiou": + body, plural = re.escape(label[:-1]) + "(?:y|ies)", "" + else: + body, plural = re.escape(label), "(?:es|s)?" + return re.compile(r"(? .\n@prefix rdfs: .\n\n" + "".join( f'rune:{name} a rdfs:Class ;\n rdfs:label "{label}" ;\n rdfs:comment "The {label}." .\n\n' - for name, label in (("Harness", "harness"), ("Rule", "rule"), ("Requirement", "requirement"), ("MissedRequirement", "missed requirement"), ("RedReview", "red review"), ("ReviewLane", "review lane")) + for name, label in (("Harness", "harness"), ("Rule", "rule"), ("Requirement", "requirement"), ("MissedRequirement", "missed requirement"), ("RedReview", "red review"), ("ReviewLane", "review lane"), ("Capability", "capability")) ) -PAGE = {"HARNESS": "harness", "RULE": "rule", "REQUIREMENT": "requirement", "MISSEDREQUIREMENT": "missed-requirement", "REDREVIEW": "red-review", "REVIEWLANE": "review-lane"} +PAGE = {"HARNESS": "harness", "RULE": "rule", "REQUIREMENT": "requirement", "MISSEDREQUIREMENT": "missed-requirement", "REDREVIEW": "red-review", "REVIEWLANE": "review-lane", "CAPABILITY": "capability"} def definitions(*tags): @@ -188,6 +188,8 @@ def definitions(*tags): UNLINKED = { "line-edges": (FIRST + "harness works.\nThe harness\n" + HARNESS, ["harness", "harness"]), "es-plural": (FIRST + "Two harnesses work.\n" + HARNESS, ["harnesses"]), + "ies-plural": ("A *[capability][CAPABILITY]* here. Two capabilities, no capabilitys.\n\n[CAPABILITY]: ../specs/glossary/capability.md\n", ["capabilities"]), + "bare-url": (FIRST + "See https://example.com/harness and and x, then a harness.\n" + HARNESS, ["harness"]), # An emphasized word is a run-in label (`**Change ceremony.**`) and stays plain, as in rune. "italic-word": (FIRST + "A later *harness* here.\n" + HARNESS, []), "legacy-citation": (FIRST + "A *harness* [HARNESS] cite.\n" + HARNESS, []), From d59578a780487975ab947d692bfeab794ff3b4ef Mon Sep 17 00:00:00 2001 From: "Claude Fable 5.1 (claude-fable-5-1)" Date: Mon, 19 Oct 2026 19:09:25 +0200 Subject: [PATCH 7/8] fix(contract): the policy digest pin follows the 2026-09-22 policy The SafetyFirst policy test pins a digest of the workflow bytes. The policy changed on 2026-09-22, when deletion moved to the shared trash script, and the pin stayed at the 2026-09-14 bytes; the contract job hid it behind the harness-symbol findings the same job reported. The pin now names the current policy. Co-Authored-By: Martin Zeman --- tests/test_safetyfirst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_safetyfirst.py b/tests/test_safetyfirst.py index 0ad77701..f4e50a40 100644 --- a/tests/test_safetyfirst.py +++ b/tests/test_safetyfirst.py @@ -18,7 +18,7 @@ ROOT = Path(__file__).resolve().parents[1] SKILL = ROOT / "runes/core/skills/SafetyFirst" -POLICY_SHA256 = "5ecac2914fd8a9e041cd61228e06af64b1888cd8da2f4de76a6adf71e9e19a8e" +POLICY_SHA256 = "6b762301756a6c0df6bd162c718f7799d59839882a93d1c83f9f90fc8c42b8b8" def injection_commands(text): From 213a3867ba1214e9236c1bb228f896042ca67c55 Mon Sep 17 00:00:00 2001 From: "Claude Fable 5.1 (claude-fable-5-1)" Date: Mon, 19 Oct 2026 20:08:48 +0200 Subject: [PATCH 8/8] fix(skill): ReviewChange uses the change word in its term sense only The reviewer found a verb still linked to the change term on line 25, which the first fix had missed one line above its own. Two more everyday uses in the same file went the same way: edits to the ontology and the proposed edits in a choice prompt are not changes in the spec-driven sense, so they are now called edits. Co-Authored-By: Martin Zeman --- runes/core/skills/ReviewChange/SKILL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runes/core/skills/ReviewChange/SKILL.md b/runes/core/skills/ReviewChange/SKILL.md index 60dd2c23..88cb9b2e 100644 --- a/runes/core/skills/ReviewChange/SKILL.md +++ b/runes/core/skills/ReviewChange/SKILL.md @@ -22,7 +22,7 @@ The owner decides a specification one *[requirement][REQUIREMENT]* at a time. Yo - Numbers are words in anything spoken or read back. - Never skip past "I do not understand" or "I have no idea what you just said". Re-explain with the four-part template first, then ask again. - Before the next [requirement][REQUIREMENT], discuss the owner's answers and answer the owner's questions. The owner's words: "always before you move on, I need you to discuss with me and answer my questions". -- A verdict binds to the [requirement][REQUIREMENT]'s current text. When the owner [changes][CHANGE] the text, say what the new text claims before you move on. +- A verdict binds to the [requirement][REQUIREMENT]'s current text. When the owner rewrites the text, say what the new text claims before you move on. - When the owner reverses a decision during the walk, the readback states only the final decision and marks the reversal where it matters. ## Instructions @@ -54,7 +54,7 @@ When a [requirement][REQUIREMENT] or a term has an arcane name, rewrite it in pl ### Read back as prose -At the end, or when the owner asks, read every decision back as prose. Write one paragraph per [requirement][REQUIREMENT], named by heading, that says what the owner decided and why, in the plain register. Rules that apply to every specification, new [requirements][REQUIREMENT], new [changes][CHANGE], and ontology [changes][CHANGE] get their own sections. Open items come last. Write the readback to a dated file that the implementing session reads first. The first one was `runedeck-review-decisions-2026-10-17.md` in the workshop. +At the end, or when the owner asks, read every decision back as prose. Write one paragraph per [requirement][REQUIREMENT], named by heading, that says what the owner decided and why, in the plain register. Rules that apply to every specification, new [requirements][REQUIREMENT], new [changes][CHANGE], and ontology edits get their own sections. Open items come last. Write the readback to a dated file that the implementing session reads first. The first one was `runedeck-review-decisions-2026-10-17.md` in the workshop. ## Verification @@ -65,7 +65,7 @@ At the end, or when the owner asks, read every decision back as prose. Write one ## References -- The terminal form: the harness's choice prompt with four [requirements][REQUIREMENT] per call and a header such as `N/32`. Each item gets one summary line. Do not copy the [requirement][REQUIREMENT] over. The options are `Keep` plus three concrete proposed [changes][CHANGE], and `Other` for the owner's own words. Keep the state in a `state.json` in the workshop's spec directory so the walk resumes. Discuss after every batch before the next one. +- The terminal form: the harness's choice prompt with four [requirements][REQUIREMENT] per call and a header such as `N/32`. Each item gets one summary line. Do not copy the [requirement][REQUIREMENT] over. The options are `Keep` plus three concrete proposed edits, and `Other` for the owner's own words. Keep the state in a `state.json` in the workshop's spec directory so the walk resumes. Discuss after every batch before the next one. - The voice form: a project built from voice-safe files. Such a file has no index ids and reads as narrative under headings. It has no tables, lists, code, paths, extensions, or abbreviations. Each section is self-contained, and the file stays under twelve kilobytes. - The SelfContainedQuestionOptions rule: describe each option's [change][CHANGE] in plain words.