ail: embed Form-A spec in merge-prose prompt (iter 20f)
Closes a design hole shipped in 20d: the merge-prose prompt instructed
the LLM to emit JSON-AST and gave a 12-line schema-essentials reminder.
JSON-AST is the canonical hashable artefact, not a writing surface; the
reminder was not a language spec. Foreign LLMs had no realistic shot.
20f makes two coupled changes:
1) The LLM now emits Form-A (the canonical authoring surface fixed by
Decision 6). merge-prose loads the original via ailang_core::load_module
and re-renders via ailang_surface::print before embedding (round-trip
is a gating contract on the surface crate, so this is lossless). The
user runs `ail parse foo.new.ailx` to recover JSON, then `ail check`.
2) crates/ailang-core/specs/form_a.md is the complete LLM-targeted
Form-A specification — grammar, every term/pattern/type/def keyword,
schema invariants, pitfall catalogue, four few-shot modules from
examples/*.ailx. Exported as ailang_core::FORM_A_SPEC via include_str!
and embedded verbatim in every merge-prose prompt.
Drift detection in crates/ailang-core/tests/spec_drift.rs: every variant
of Term, Pattern, Type, Def, Literal is reached via exhaustive `match`.
The arms are not the assertion — adding a new variant without updating
the match is a compile error before the test runs. Once matched, an
anchor string is asserted to appear in FORM_A_SPEC. 8 tests, all green.
The hand-written + mechanical-drift-test combo addresses the user's
"distance to code is too big" concern about a docs-only spec. Generator
overkill rejected: structural drift is mechanically caught, but prose
explanation, schema-invariant catalogue, pitfall list, and few-shot
corpus cannot be emitted from AST shape alone.
Tests:
- ailang-core: +8 spec_drift tests; existing 12 unchanged
- ail unit (3): rewritten in lockstep — assert (own T)/(effects IO)
landmarks, FORM-A SPECIFICATION header, FORM_A_SPEC body verbatim
- ail e2e merge_prose_prints_framed_prompt: rewritten — assert
`(module foo` + `FORM-A SPECIFICATION` instead of `ailang/v0`
- Workspace: all green
Richer integration paths (LLM tool-use, MCP server, LSP) were named
in the design discussion and deferred per "kiss". All three layer
additively on the static-prompt path; static prompt remains the
lowest-common-denominator fallback.
This commit is contained in:
@@ -96,6 +96,18 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
/// loading fails with [`Error::SchemaMismatch`].
|
||||
pub const SCHEMA: &str = "ailang/v0";
|
||||
|
||||
/// Iter 20f: complete LLM-targeted specification of Form-A — the
|
||||
/// canonical authoring surface (Decision 6). Embedded verbatim into
|
||||
/// any prompt that asks an LLM to produce or edit AILang code; in
|
||||
/// particular, `ail merge-prose` includes it in the round-trip
|
||||
/// prompt template. The string is the raw bytes of
|
||||
/// `specs/form_a.md`, co-located with this crate so the spec sits
|
||||
/// next to the AST it describes; a drift test
|
||||
/// (`tests/spec_drift.rs`) walks every AST enum variant and asserts
|
||||
/// its serde tag appears in this string. Adding a new variant
|
||||
/// without updating the spec fails the test suite.
|
||||
pub const FORM_A_SPEC: &str = include_str!("../specs/form_a.md");
|
||||
|
||||
/// Load a single module file, validate its schema, and deserialize it
|
||||
/// into a [`Module`].
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user