-
Notifications
You must be signed in to change notification settings - Fork 84
Description
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/