Skip to content

Spec Observations from Deterministic Linting #22

@rawwerks

Description

@rawwerks

Spec Observations from Deterministic Linting

Building a deterministic linter for .md programs against the spec draft surfaced several observations about how the corpus diverges from (or extends) the documented spec. These are offered as questions to help shape the spec, not as assertions about what it should say.

Methodology

Analyzed 76 .md files across three directories in the Press repo:

  • test/fixtures/ — 5 multi-file programs (18 files)
  • programs/ — 4 production programs (12 files)
  • lib/ — standard library (46 files: roles, composites, controls, drivers, profiles)

The linter's discover command produces a census of vocabulary and patterns that aren't covered by the current spec.

Vocabulary Gaps

1. Undocumented kind: values

The spec defines: program, program-node, service.

The corpus also uses:

Kind Files What it seems to mean
driver 21 Behavioral constraints injected into execution (e.g., verify-before-return, await-discipline)
profile 1 Model-specific driver bundles (e.g., gemini-3-flash)

Question: Should driver and profile become first-class kinds? They seem categorically different from executable components — they're behavioral modifiers, not services that receive inputs and produce outputs.

2. Undocumented frontmatter keys

The spec documents 16 keys. The corpus uses 7 additional ones:

Key Files Where it appears
delegates 30 All program-node files
slots 18 Composites and controls
author 21 All driver files
tags 21 All driver files
components 1 Alternative to nodes
models 1 Profile files
drivers 1 Profile files

Question: delegates and slots appear in 30 and 18 files respectively — they define the delegation topology and composition surface. Should these be in the spec? Or are they runtime concerns that the spec intentionally leaves open?

3. author and tags on drivers

Every driver file has author and tags in frontmatter. No other kind uses them.

Question: Is this driver-specific metadata, or should there be a general metadata section in the spec that any component can use?

Structural Questions

4. ### heading semantics

The spec says ### headings delimit components. In practice, ### headings serve at least four purposes:

Pattern Example
Component definition ### game-solver
State schema ### &GameKnowledge
Output/type schema ### BriefAdherence
Documentation ### When to use direct delegation

The linter uses heuristics to classify these (kebab-case = component, &-prefix = state, PascalCase/natural language = documentation).

Question: Should the spec define a convention here? Even just "state schemas use &-prefix" would help tooling.

5. Multi-file detection

The spec describes both inline components and multi-file program directories but doesn't specify how a tool should detect which mode applies.

Question: Is there a canonical signal? The linter checks whether a directory contains a kind: program root file and treats sibling .md files as nodes.

6. Nested frontmatter

state: with reads:/writes: sub-keys is used in 7 files. The spec only shows flat frontmatter.

Question: Which keys support nesting? Just state: and shape:? Or is arbitrary nesting allowed?

Behavioral Observations

7. Role ↔ delegation correlation

The linter observed how role: correlates with delegates: across the corpus:

Pattern Files
coordinator + no delegates: in frontmatter 18
leaf + no delegates: 8
orchestrator + delegates: declared 3
orchestrator + no delegates: 1
coordinator + delegates: declared 1

Most coordinators (18 of 19) don't declare delegates: in frontmatter — they delegate through the body/runtime instead.

Question: Is delegates: a frontmatter concern at all, or is it primarily a runtime/body concern? The correlation suggests delegates: in frontmatter is only used for orchestrators with explicit delegation graphs, while coordinators express delegation through prose in the body.

8. Three contract formats in the corpus

The linter found contracts expressed in three different structural patterns:

Format Where used Example
Bare top-level Test fixtures requires:\n- text: input (after frontmatter, no heading)
## requires / ## ensures Spec description Markdown heading sections
Inside fenced code block Stdlib, production programs Under ## Contract heading

All three are used in the Press corpus. After teaching the linter to recognize all three, only 1 of 76 files genuinely lacks ensures (prose-via-forme.md, a boot helper).

Question: Should the spec canonicalize one format, or explicitly bless all three? A tool implementing prose compile needs to know where to look.

9. prose compile validation rules for .md programs

The spec defines prose compile <file> as "validate syntax and contracts without executing." compiler.md defines validation rules (E001–E028, W001–W011) for legacy .prose files, but there's no equivalent rule catalog for .md programs. The linter invented its own codes (V2E001–V2E051, V2W001–V2W030) to fill this gap.

Question: Should the spec define validation rules for .md programs? Or is prose compile on .md files expected to just check frontmatter schema and contract presence without a formal rule catalog?

Legacy (.prose) Spec Clarification Questions

The linter also validates legacy .prose files against compiler.md. A few spec-defined rules raised implementation questions:

9. resume: with no existing memory (E018) — static or runtime?

The spec lists E018 but this seems like a runtime check (whether a persistent agent has been used in a previous session). Is this intended as a static lint rule? Does a session: agent before resume: agent in the same file satisfy it, or does it require cross-run persistence?

10. Input after executable statement — E022 or warning?

The spec says E022 (error). The linter currently treats this as OPW007 (warning in compat, error in strict). Which is normative? Should compat mode allow it or is this always an error?

11. Cross-file program invocation rules (E025–E028) — scope?

These rules (unknown program, missing required input, unknown input name, unknown output property) require resolving use imports to validate contracts across files. Is the intent that a linter should do this? Or are these runtime-only checks?

12. on-fail vs on_fail — which is canonical?

The spec grammar shows on-fail: (hyphenated). The corpus uses both on-fail and on_fail. Is on_fail (underscore) a supported alias or a compatibility shim that should be warned about?

13. Skill validation (W007) — what does "not imported" mean?

Skills are declared in skills: ["..."] on agents, not imported via use. Does W007 mean "skill name doesn't match any known skill in a registry"? Or "the agent uses a capability that requires a skill not listed in its own skills: array"?

Reproducibility

cd tools/lint
cargo run -- lint path/to/prose/skills/open-prose/examples/
cargo run -- lint-md path/to/press/lib/ path/to/press/programs/ path/to/press/test/fixtures/
cargo run -- discover path/to/press/lib/ path/to/press/programs/ path/to/press/test/fixtures/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions