From 73b7f8aa40ed417aa188293d7315a66d67156aae Mon Sep 17 00:00:00 2001 From: rmichaelthomas Date: Sat, 4 Jul 2026 21:38:52 -0700 Subject: [PATCH 1/3] docs: propagate v0.16.0 vocabulary wave (unless-on-deontics, Calendar Era, Definitional Era) Vocabulary count updated 60 -> 61 reserved words (21 verbs, 22 connectives, 10 operators, 3 articles, 3 multi-word reserved, 2 declarations) across CLAUDE.md, AGENTS.md, README.md, and docs/. Adds a new syntax.md section documenting `define`/named predicates (previously undocumented), extends the roadmap vocabulary table and DEV_README status line with unless-exception-clauses, the Calendar Era, and the Definitional Era, and gives pipeline.md a full pass covering the predicate table, DefineNode/PredicateApplicationNode, TokenType.DATE, and exception-clause AST fields. Source: checkpoints v28 (unless on deontics), v29/v30 (Calendar Era), v31 (Definitional Era), verified against src/liminate/vocabulary.py. Co-Authored-By: Claude Sonnet 5 --- AGENTS.md | 2 +- CLAUDE.md | 2 +- README.md | 16 ++++--- docs/DEV_README.md | 18 +++---- docs/architecture/pipeline.md | 87 ++++++++++++++++++++++++++++++++-- docs/language/syntax.md | 81 +++++++++++++++++++++++++++++-- docs/roadmap/v1-v2-boundary.md | 21 ++++---- 7 files changed, 191 insertions(+), 36 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index be93103..a28f20f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ Liminate is a prose-as-syntax programming language. The interpreter is a Python pipeline: lexer → reorderer → parser + renderer → analyzer → interpreter, with a Phase 2 event-driven listener layered on top. -Current vocabulary: 21 verbs, 22 connectives, 10 operators, 1 declaration, 60 reserved words. Domain packs may declare additional verbs and nouns via the pack-verb contract. +Current vocabulary: 21 verbs, 22 connectives, 10 operators, 2 declarations, 61 reserved words. Domain packs may declare additional verbs and nouns via the pack-verb contract. ## Critical Rules diff --git a/CLAUDE.md b/CLAUDE.md index 887119a..b8dcd43 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Liminate is a prose-as-syntax programming language. The interpreter is a Python pipeline: lexer → reorderer → parser + renderer → analyzer → interpreter, with a Phase 2 event-driven listener layered on top. -Current vocabulary: 21 verbs, 22 connectives, 60 reserved words. Domain packs may declare additional verbs and nouns via the pack-verb contract. +Current vocabulary: 21 verbs, 22 connectives, 61 reserved words. Domain packs may declare additional verbs and nouns via the pack-verb contract. ## Critical Rules diff --git a/README.md b/README.md index 847e220..b6b7124 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The sentence is the program. *Part of the Prosecode family — a set of tools for writing, verifying, and transferring structured reasoning.* -Liminate is a prose-as-syntax language whose syntax is plain English. A small, bounded vocabulary of 60 reserved words combines into sentences that a real interpreter lexes, parses, type-checks, and runs. Not a prompt. Not a code generator. The prose IS the program. +Liminate is a prose-as-syntax language whose syntax is plain English. A small, bounded vocabulary of 61 reserved words combines into sentences that a real interpreter lexes, parses, type-checks, and runs. Not a prompt. Not a code generator. The prose IS the program. ## What it does @@ -71,7 +71,7 @@ pytest tests/ ## How it works -The current build is **v0.15.0**: 21 verbs, 22 connectives, 10 operators, 1 declaration, 60 base reserved words, **1499 tests passing**. +The current build is **v0.16.0**: 21 verbs, 22 connectives, 10 operators, 2 declarations, 61 base reserved words, **1654 tests passing**. ### The pipeline @@ -82,9 +82,9 @@ Two phases of execution: - **Phase 1 — sequential.** Each statement runs in order. Stepwise commit: if a later op fails, earlier side effects remain and the error names what was completed. - **Phase 2 — reactive listener.** `when`/`unless` register handlers driven by an external event source (a domain pack adapter). Edge-triggered, depth-first cascading, conservative same-handler-twice cycle detection. `finish` exits immediately and totally. -### The vocabulary (60 words) +### The vocabulary (61 words) -**Verbs (21):** `remember`, `show`, `filter`, `keep`, `count`, `gather`, `sum`, `each`, `choose`, `finish`, `add`, `remove`, `weakens`, `require`, `forbid`, `permit`, `assign`, `expect`, `sort`, `compare`, `transform`. The Deontic Era completes the obligation/prohibition/permission triangle: `require` halts when its condition is false, `forbid` halts when its condition is true (`PROHIBITION_VIOLATED`), and `permit` emits an informational line when its condition is true but never halts. `sum` was renamed from `combine` in v0.15.0 — `combine` is tombstoned and now errors with a rename-specific message. +**Verbs (21):** `remember`, `show`, `filter`, `keep`, `count`, `gather`, `sum`, `each`, `choose`, `finish`, `add`, `remove`, `weakens`, `require`, `forbid`, `permit`, `assign`, `expect`, `sort`, `compare`, `transform`. The Deontic Era completes the obligation/prohibition/permission triangle: `require` halts when its condition is false, `forbid` halts when its condition is true (`PROHIBITION_VIOLATED`), and `permit` emits an informational line when its condition is true but never halts. `sum` was renamed from `combine` in v0.15.0 — `combine` is tombstoned and now errors with a rename-specific message. All four condition-bearing verbs in this family — `require`, `forbid`, `permit`, `expect` — additionally accept an `unless ` clause after the main condition (v0.16.0): `forbid total is above 10000 unless approved is equal to yes`. Zero new words — `unless` in a new grammatical position. **Connectives (22):** `where`, `and`, `or`, `from`, `with`, `called`, `to`, `how`, `as`, `of`, `if`, `otherwise`, `when`, `unless`, `includes`, `within`, `over`, `then`, `by`, `because`, `starting`, `until`. `because` attaches a quoted rationale to any verb statement as inert metadata — visible in canonical rendering, `inspect`, and Receipts, but never executed. Statement-terminal and per-statement: `require amount is above 50000 because "SOX compliance"`. The last two, `starting` and `until`, are statement-initial temporal modifiers that attach quoted ISO 8601 dates as inert metadata — an effective date and a sunset clause: `starting "2025-07-01" until "2025-12-31" require amount is above 50000`. Temporal evaluation is a product-layer concern, not interpreter runtime. @@ -92,7 +92,7 @@ Two phases of execution: **Articles (3):** `the`, `a`, `an` — decorative; the parser ignores them. -**Declarations (1):** `about` — declares the program's topic as inert metadata. Single, first-line-only; visible to tooling (`inspect`, the build manifest) but never stored or executed. `about "expense authorization"` or `about expense-authorization`. +**Declarations (2):** `about` and `define`. `about` declares the program's topic as inert metadata — single, first-line-only; visible to tooling (`inspect`, the build manifest) but never stored or executed: `about "expense authorization"` or `about expense-authorization`. `define : ` (v0.16.0's Definitional Era) names a reusable domain predicate — `define overdue: due-date is below cutoff` — referenced elsewhere as `is overdue` / `is not overdue`. Predicates compose, are redefinable, and work everywhere the condition grammar works (`where`, `require`, `forbid`, `when`, etc.) with no per-verb changes. **Delimiter (1):** `:` — separates a composition name from its body, and a `choose` branch's condition from its action. @@ -100,9 +100,11 @@ Two phases of execution: Arithmetic expressions use PEMDAS precedence (multiply/divide before add/subtract, left-to-right within the same tier) and work in any value position. +Values include numbers, strings, dates, lists, and records. Dates (v0.16.0's Calendar Era) are bare ISO 8601 literals (`2025-07-01`, no quotes needed) with whole-day arithmetic (`2025-07-01 plus 30`), all comparison operators, day-based `within` tolerance, and `highest`/`lowest`/`sort` support. + ### Domain packs -A pack is a small JSON file that adds nouns and verbs while it's loaded. The base 60 words are permanent; pack-contributed words are reserved only when the pack is active. +A pack is a small JSON file that adds nouns and verbs while it's loaded. The base 61 words are permanent; pack-contributed words are reserved only when the pack is active. A pack verb declares a slot signature, a type constraint, and one of six execution dispatches: @@ -161,7 +163,7 @@ The locked test sentences are simultaneously test cases and grammar artifacts ### Design principles - **The prose IS the program.** No inference, no guessing. If the prose doesn't say it, it doesn't happen. -- **The vocabulary is the boundary.** 60 base reserved words. Expressiveness scales through composition and domain packs, not through adding keywords. +- **The vocabulary is the boundary.** 61 base reserved words. Expressiveness scales through composition and domain packs, not through adding keywords. - **The reorderer does not guess.** Ambiguous arrangements produce an amber clarification prompt rather than a silent pick. - **Authorize, don't author.** The on-ramp is modification of a working program, not authorship from a blank file. - **The AST is the source of truth.** The parser reconstructs a canonical English sentence so you see what was understood before it runs. diff --git a/docs/DEV_README.md b/docs/DEV_README.md index 641d248..792d46a 100644 --- a/docs/DEV_README.md +++ b/docs/DEV_README.md @@ -4,7 +4,7 @@ A prose-as-syntax programming language designed from the human end. > *"Every programming language in history was designed by programmers. This one wasn't. That's why the design is different."* -**Status (June 5, 2026):** v1 interpreter + v2a (`keep`, `of`, multi-field `each show`, descriptor preservation) + UX polish (`--quiet`, named-offender errors, auto-show truncation) + v2.1-patches + v2b (composition return values, generalized `of`) + v2c (quoting mechanism for multi-word strings) + v2d (composition parameters with `from`, `choose` verb with `if`/`otherwise`) + v3a event-driven execution (`when`/`unless`/`finish`, two-phase listener model, single-threaded event queue, cascading triggers with cycle detection, domain-pack adapter contract) + v3b (quoted-string case preservation) + v4a pack verb contract (general-purpose JSON-defined pack verbs with slot signatures, type constraints, and execution dispatch; UI domain pack with 10 nouns + `navigate to `) + `add` verb addendum + `includes` connective / `remove` verb addendum + `within` connective with the session pack's `measure` verb + **Metabolic Era batch 1** (`weakens`/`over` — autonomous linear decay over a stated period of ticks) + **Normative Era batch 2** (`require`/`then` — enforcement and declared sequencing) + **Delegated/Epistemic Era batch 3** (`assign`/`expect` — item-to-recipient delegation and non-halting tracked anticipation) + **Infrastructure Era** (`by`/`plus`/`minus`/`multiplied by`/`divided by` — arithmetic expressions with PEMDAS precedence) + **Infrastructure Era batch 2** (`sort`/`reverse` — in-place list reordering by a field) + **V2 promotions** (`compare` — structured comparison into a `comparison` record; `transform` — per-element list mutation, the final V2-deferred word, so `V2_RESERVED` is now empty) + **Meta-Structural Era** (`about` — program topic declaration; `because` — statement-terminal quoted rationale on any verb statement; `inherited` — statement-initial provenance modifier with optional `from ` attribution; all three are inert self-describing metadata, visible to rendering and `inspect` but never executed) + **Deontic Era** (`forbid` — halts with `PROHIBITION_VIOLATED` on a true condition, the mirror of `require`; `permit` — emits an informational line on a true condition and never halts, the `expect` pattern; together they complete the require/forbid/permit triangle) + **Temporal-Boundary Era** (`starting`/`until` — statement-initial connectives attaching quoted ISO 8601 effective dates and sunset clauses as inert metadata; temporal evaluation is a product-layer concern, not interpreter runtime) + `require each` grammar extension (`require each ` — iterated enforcement with a named binding, halts at the first item violating the condition) + **Platform Era** (exit non-zero on interpreter errors, `--help`/`-h` flag, test/pack-sync CI workflow, enterprise-readiness versioning policy and trust-boundary docs) + **Public API Era** (extracted `run()` as the single shared program-execution entry point for the CLI and Receipts; `ContractResult` return type with `results`/`symbol_table`/`topic`/`had_error`; `enter_phase2` control for static inspectors; per-result `line`/`source`/`timestamp`/`duration_ms` metadata) + **Pack-Verb Expansion Era** (v0.12 → v0.14: `PACK_VERB_FAILURE` result status with structured failure metadata and inherited `when` blocks; three new pack-verb execution types — `numeric_extract_compare`, `range_check` (the `validate` verb), and `conformance_check` (the `fit` verb) — bringing the execution union to eight; iterable pack verbs via `each` in pack slots; empty-list construction and descending/step-valued ranges; literal-valued composition parameters; analyzer-time deontic contradiction detection; timer-driven decay tick advancement) + **v0.15.0 vocabulary wave** (`combine` renamed to `sum`, same slot signature and numeric-sum execution; `combine` tombstoned — reserved, inactive, rejected with a rename-specific error, excluded from the public word count; `highest`/`lowest` operators added — value-position list-extrema selectors with flat-list and record-field grammar modes, numeric-only, value-returning, erroring on an empty list, unlike `sum`'s empty-list-is-0 additive identity). **1499 tests passing.** The sequential feature set (v1 → v2d), the reactive feature set (v3a/v3b), the pack-verb extension contract, the Metabolic Era's autonomous-time primitives, the Normative Era's enforcement-and-sequencing primitives, the Delegated/Epistemic Era's delegation-and-anticipation primitives, the Infrastructure Era's arithmetic-and-reordering primitives, the Meta-Structural Era's self-describing metadata, the Deontic Era's prohibition-and-permission verbs, the Temporal-Boundary Era's effective-date-and-sunset connectives, `require each` iterated enforcement, and the public `run()` embedding API together form a structurally complete and embeddable programming language; pack verbs let domains add vocabulary without touching the base 60 reserved words. +**Status (July 4, 2026):** v1 interpreter + v2a (`keep`, `of`, multi-field `each show`, descriptor preservation) + UX polish (`--quiet`, named-offender errors, auto-show truncation) + v2.1-patches + v2b (composition return values, generalized `of`) + v2c (quoting mechanism for multi-word strings) + v2d (composition parameters with `from`, `choose` verb with `if`/`otherwise`) + v3a event-driven execution (`when`/`unless`/`finish`, two-phase listener model, single-threaded event queue, cascading triggers with cycle detection, domain-pack adapter contract) + v3b (quoted-string case preservation) + v4a pack verb contract (general-purpose JSON-defined pack verbs with slot signatures, type constraints, and execution dispatch; UI domain pack with 10 nouns + `navigate to `) + `add` verb addendum + `includes` connective / `remove` verb addendum + `within` connective with the session pack's `measure` verb + **Metabolic Era batch 1** (`weakens`/`over` — autonomous linear decay over a stated period of ticks) + **Normative Era batch 2** (`require`/`then` — enforcement and declared sequencing) + **Delegated/Epistemic Era batch 3** (`assign`/`expect` — item-to-recipient delegation and non-halting tracked anticipation) + **Infrastructure Era** (`by`/`plus`/`minus`/`multiplied by`/`divided by` — arithmetic expressions with PEMDAS precedence) + **Infrastructure Era batch 2** (`sort`/`reverse` — in-place list reordering by a field) + **V2 promotions** (`compare` — structured comparison into a `comparison` record; `transform` — per-element list mutation, the final V2-deferred word, so `V2_RESERVED` is now empty) + **Meta-Structural Era** (`about` — program topic declaration; `because` — statement-terminal quoted rationale on any verb statement; `inherited` — statement-initial provenance modifier with optional `from ` attribution; all three are inert self-describing metadata, visible to rendering and `inspect` but never executed) + **Deontic Era** (`forbid` — halts with `PROHIBITION_VIOLATED` on a true condition, the mirror of `require`; `permit` — emits an informational line on a true condition and never halts, the `expect` pattern; together they complete the require/forbid/permit triangle) + **Temporal-Boundary Era** (`starting`/`until` — statement-initial connectives attaching quoted ISO 8601 effective dates and sunset clauses as inert metadata; temporal evaluation is a product-layer concern, not interpreter runtime) + `require each` grammar extension (`require each ` — iterated enforcement with a named binding, halts at the first item violating the condition) + **Platform Era** (exit non-zero on interpreter errors, `--help`/`-h` flag, test/pack-sync CI workflow, enterprise-readiness versioning policy and trust-boundary docs) + **Public API Era** (extracted `run()` as the single shared program-execution entry point for the CLI and Receipts; `ContractResult` return type with `results`/`symbol_table`/`topic`/`had_error`; `enter_phase2` control for static inspectors; per-result `line`/`source`/`timestamp`/`duration_ms` metadata) + **Pack-Verb Expansion Era** (v0.12 → v0.14: `PACK_VERB_FAILURE` result status with structured failure metadata and inherited `when` blocks; three new pack-verb execution types — `numeric_extract_compare`, `range_check` (the `validate` verb), and `conformance_check` (the `fit` verb) — bringing the execution union to eight; iterable pack verbs via `each` in pack slots; empty-list construction and descending/step-valued ranges; literal-valued composition parameters; analyzer-time deontic contradiction detection; timer-driven decay tick advancement) + **v0.15.0 vocabulary wave** (`combine` renamed to `sum`, same slot signature and numeric-sum execution; `combine` tombstoned — reserved, inactive, rejected with a rename-specific error, excluded from the public word count; `highest`/`lowest` operators added — value-position list-extrema selectors with flat-list and record-field grammar modes, numeric-only, value-returning, erroring on an empty list, unlike `sum`'s empty-list-is-0 additive identity) + **`unless` exception clauses** (v0.16.0: `require`/`forbid`/`permit`/`expect` all accept an `unless ` clause after the main condition — zero new words, an existing connective in a new grammatical position; polarity-appropriate `main AND NOT exception` runtime semantics; the contradiction detector issues a conditional-contradiction warning naming the exception when a guarded deontic conflicts with another rule) + **Calendar Era** (v0.16.0: `date` as a third scalar value type alongside number and string — zero new words, a bare ISO 8601 date is recognized by shape; whole-day arithmetic, all comparison operators, day-based `within` tolerance, product-layer `today` injection, and `highest`/`lowest`/`sort`/list operations extended to dates) + **Definitional Era** (v0.16.0: `define : ` — one new declaration, joining `about` — names a reusable, testable condition referenced elsewhere as `is ` / `is not `; resolved at parse time via a predicate table, forward-declared, composable, redefinable, and working everywhere the unified condition grammar already works with zero per-verb changes) + **predicate-aware contradiction detection** (the analyzer's contradiction pre-pass threads predicate names through and treats predicate applications as opaque atoms, so guarded and predicated deontics no longer produce false positives or crashes in the detector). **1654 tests passing.** The sequential feature set (v1 → v2d), the reactive feature set (v3a/v3b), the pack-verb extension contract, the Metabolic Era's autonomous-time primitives, the Normative Era's enforcement-and-sequencing primitives, the Delegated/Epistemic Era's delegation-and-anticipation primitives, the Infrastructure Era's arithmetic-and-reordering primitives, the Meta-Structural Era's self-describing metadata, the Deontic Era's prohibition-and-permission verbs, the Temporal-Boundary Era's effective-date-and-sunset connectives, `require each` iterated enforcement, the public `run()` embedding API, the deontic family's `unless` exception clauses, the Calendar Era's date value type, and the Definitional Era's named predicates together form a structurally complete and embeddable programming language; pack verbs let domains add vocabulary without touching the base 61 reserved words. --- @@ -34,7 +34,7 @@ A prose-as-syntax programming language designed from the human end. `filter the orders where total is above 50` is not a prompt to an AI. It is the program. -Liminate is a general-purpose programming language whose source code is readable English prose. A bounded vocabulary of 60 words combines into sentences that execute directly. There is no separate code the prose generates — the sentence IS the program. +Liminate is a general-purpose programming language whose source code is readable English prose. A bounded vocabulary of 61 words combines into sentences that execute directly. There is no separate code the prose generates — the sentence IS the program. This is not a domain-specific language for queries or data, nor a natural-language layer over Python, nor a code-generating AI. It is a programming language with its own pipeline: lexer, reorderer, parser, semantic analyzer, interpreter. The prose-as-syntax constraint is structural, not cosmetic. @@ -58,7 +58,7 @@ Five properties combine in Liminate that exist individually in other systems but 1. **Prose-as-syntax where the prose IS the executable code.** Not prose that generates code (vibe coding). Not prose that describes a game world (Inform 7, which is domain-locked). General-purpose computation expressed as readable English sentences that execute directly. -2. **Bounded vocabulary as design constraint.** 60 reserved words in the current build (21 verbs + 22 connectives + 10 single-word operators + `equal`/`multiplied`/`divided` as multi-word components + 3 articles + 1 declaration + 0 v2-deferred words — `V2_RESERVED` is now empty). The vocabulary is the language boundary, not a starter set that grows. Expressiveness scales through domain packs, composition over expansion, and named-composition chunking — not through adding more keywords. +2. **Bounded vocabulary as design constraint.** 61 reserved words in the current build (21 verbs + 22 connectives + 10 single-word operators + `equal`/`multiplied`/`divided` as multi-word components + 3 articles + 2 declarations + 0 v2-deferred words — `V2_RESERVED` is now empty). The vocabulary is the language boundary, not a starter set that grows. Expressiveness scales through domain packs, composition over expansion, and named-composition chunking — not through adding more keywords. 3. **Graduation from tiles to text within one language.** The same AST underlies a tile-composition surface (for first-encounter authoring), a prose surface (for fluent authoring), and an optional symbolic surface (for velocity). Three views, one structure. Scratch-to-Python is two languages; Liminate is one language with three surfaces. The v1 interpreter implements the prose surface; the tile surface is a separate downstream concern. @@ -171,7 +171,7 @@ Every statement is echoed first in canonical prose form (the parser's interpreta ## The vocabulary -The current base vocabulary is 60 reserved words across six categories. The complete list is the entire language surface — no other words are part of Liminate, only user-provided names and literal values. Domain packs may add their own verbs and nouns at runtime via the pack verb contract; pack-contributed words are reserved only while the pack is loaded, and the base 60 are permanent. +The current base vocabulary is 61 reserved words across six categories. The complete list is the entire language surface — no other words are part of Liminate, only user-provided names and literal values. Domain packs may add their own verbs and nouns at runtime via the pack verb contract; pack-contributed words are reserved only while the pack is loaded, and the base 61 are permanent. ### Verbs (16) @@ -488,7 +488,7 @@ Line 3 then shows `4, 5` — the filter's commit persists. Multi-operation seque The shipped build covers v1 (48 locked test sentences) + v2a (11 more) + UX polish + v2.1-patches + v2b (9 more) + v2c (12 more) + v2d (15 more) + v3a (18 more) + v3b (4 more) + v4a (10 more) plus the Metabolic / Normative / Delegated / Epistemic / Infrastructure / Meta-Structural / Deontic / Temporal-Boundary era batches, `require each` grammar extension, Platform + Public API infrastructure, the Pack-Verb Expansion Era (`validate`/`fit` pack verbs, iterable pack verbs, empty-list/range additions), and the v0.15.0 vocabulary wave (`combine` → `sum`, `highest`/`lowest`). **1499 tests passing.** Larger scope is intentionally deferred — but the sequential feature set (v1 → v2d), the reactive feature set (v3a/v3b), the pack-verb extension contract, the era batches, and the public `run()` embedding API together form a structurally complete and embeddable programming language. -**Currently shipped.** Two-phase execution (Phase 1 sequential, Phase 2 reactive). 21 base verbs. 22 connectives. 60 base reserved words. Self-describing metadata via the Meta-Structural Era's `about` declaration, `because` rationale, and `inherited` provenance modifier (all inert — rendered and inspected, never executed). The Deontic Era's `forbid` (halts with `PROHIBITION_VIOLATED` on a true condition) and `permit` (emits an informational line on a true condition, never halts) completing the require/forbid/permit triangle. The Temporal-Boundary Era's `starting`/`until` statement-initial connectives attaching quoted ISO 8601 effective dates and sunset clauses as inert metadata (temporal evaluation is a product-layer concern, not interpreter runtime). Arithmetic expressions with PEMDAS precedence (`plus`/`minus`/`multiplied by`/`divided by`). In-place list reordering (`sort ... by [in reverse]`). Structured comparison (`compare to ` → a `comparison` record). Per-element list mutation (`transform of by ` / `transform by `). Numbers (integers + decimals). Strings (single-token bare words + multi-word quoted strings via v2c, with verbatim case preservation per). Lists (homogeneous — all numbers, all strings, or all records). Records (named fields, with descriptor preserved on the symbol for pack-verb type checks). Named compositions with optional parameters. Conditional branching via `choose if/otherwise`. In-place `filter`, non-destructive `keep`, non-destructive `sum`, copy semantics, iterator context for `each`, multi-field display in `each ... show`, single-record field access via `show of ` and ` of ` in any value position. Descriptor preservation, named-offender error wording, stepwise sequences. Composition return values via `remember the X from `. Event-driven `when`/`unless`/`finish` with indented action blocks, single-threaded event queue, edge-triggered evaluation with deep value equality, depth-first cascading with conservative cycle detection, domain-pack adapter contract registered externally via `--pack ` JSON or `Session(domain_packs=...)`. v4a general-purpose pack verb contract — packs declare verbs with slot signatures + type constraints + execution dispatch in JSON; the parser dispatches pack verbs after base verbs, the analyzer enforces descriptor-based type constraints, and `set_value` is the first execution type. The UI domain pack ships 10 component nouns and the `navigate to ` verb. CLI flags `--quiet`, `--test`, `--pack` (any position). `require each` grammar extension — `require each ` iterates enforcement over a list with a named binding, halting at the first item that violates the condition. Platform additions: non-zero exit on interpreter errors; `--help`/`-h` flag; test/pack-sync CI workflow; VERSIONING.md (replay guarantee, pinning guidance); TRUST-BOUNDARY.md (data-flow documentation). Public API: `run(source, ...)` — the single shared program-execution entry point used by the CLI and Receipts, returning a `ContractResult` (`results`, `symbol_table`, `topic`, `had_error`); `enter_phase2` parameter lets static inspectors skip the listener without losing Phase 1 results; every `LiminateResult` now carries `line` (1-based source line), `source` (raw line text), `timestamp`, and `duration_ms` for per-line embedder serialization without re-running the loop. v0.15.0 vocabulary wave: `combine` renamed to `sum` (tombstoned, uncounted); `highest`/`lowest` list-extrema operators (flat-list and record-field forms, numeric-only, value-returning, empty-list is a runtime error). +**Currently shipped.** Two-phase execution (Phase 1 sequential, Phase 2 reactive). 21 base verbs. 22 connectives. 61 base reserved words. Self-describing metadata via the Meta-Structural Era's `about` declaration, `because` rationale, and `inherited` provenance modifier (all inert — rendered and inspected, never executed). The Deontic Era's `forbid` (halts with `PROHIBITION_VIOLATED` on a true condition) and `permit` (emits an informational line on a true condition, never halts) completing the require/forbid/permit triangle. The Temporal-Boundary Era's `starting`/`until` statement-initial connectives attaching quoted ISO 8601 effective dates and sunset clauses as inert metadata (temporal evaluation is a product-layer concern, not interpreter runtime). Arithmetic expressions with PEMDAS precedence (`plus`/`minus`/`multiplied by`/`divided by`). In-place list reordering (`sort ... by [in reverse]`). Structured comparison (`compare to ` → a `comparison` record). Per-element list mutation (`transform of by ` / `transform by `). Numbers (integers + decimals). Strings (single-token bare words + multi-word quoted strings via v2c, with verbatim case preservation per). Lists (homogeneous — all numbers, all strings, or all records). Records (named fields, with descriptor preserved on the symbol for pack-verb type checks). Named compositions with optional parameters. Conditional branching via `choose if/otherwise`. In-place `filter`, non-destructive `keep`, non-destructive `sum`, copy semantics, iterator context for `each`, multi-field display in `each ... show`, single-record field access via `show of ` and ` of ` in any value position. Descriptor preservation, named-offender error wording, stepwise sequences. Composition return values via `remember the X from `. Event-driven `when`/`unless`/`finish` with indented action blocks, single-threaded event queue, edge-triggered evaluation with deep value equality, depth-first cascading with conservative cycle detection, domain-pack adapter contract registered externally via `--pack ` JSON or `Session(domain_packs=...)`. v4a general-purpose pack verb contract — packs declare verbs with slot signatures + type constraints + execution dispatch in JSON; the parser dispatches pack verbs after base verbs, the analyzer enforces descriptor-based type constraints, and `set_value` is the first execution type. The UI domain pack ships 10 component nouns and the `navigate to ` verb. CLI flags `--quiet`, `--test`, `--pack` (any position). `require each` grammar extension — `require each ` iterates enforcement over a list with a named binding, halting at the first item that violates the condition. Platform additions: non-zero exit on interpreter errors; `--help`/`-h` flag; test/pack-sync CI workflow; VERSIONING.md (replay guarantee, pinning guidance); TRUST-BOUNDARY.md (data-flow documentation). Public API: `run(source, ...)` — the single shared program-execution entry point used by the CLI and Receipts, returning a `ContractResult` (`results`, `symbol_table`, `topic`, `had_error`); `enter_phase2` parameter lets static inspectors skip the listener without losing Phase 1 results; every `LiminateResult` now carries `line` (1-based source line), `source` (raw line text), `timestamp`, and `duration_ms` for per-line embedder serialization without re-running the loop. v0.15.0 vocabulary wave: `combine` renamed to `sum` (tombstoned, uncounted); `highest`/`lowest` list-extrema operators (flat-list and record-field forms, numeric-only, value-returning, empty-list is a runtime error). v0.16.0: `unless` exception clauses on `require`/`forbid`/`permit`/`expect` (zero new words, new grammatical position); the Calendar Era's `date` value type (zero new words, bare ISO 8601, whole-day arithmetic, all comparisons, `within`/`highest`/`lowest`/`sort` support, product-layer `today` injection); the Definitional Era's `define : ` declaration (one new word) — named, composable, redefinable predicates referenced as `is ` / `is not ` everywhere the condition grammar works; and predicate-aware contradiction detection in the analyzer. **Not built (deliberately).** Tile-composition interface. Proposal engine and authorize-don't-author authoring flow. Real-world domain packs (healthcare, smart home, game) — the language ships a test adapter only, packs are product work. Domain pack activation syntax (an Liminate-level `use`/`load` verb). Symbolic syntax surface. External data sources beyond domain-pack adapters. Negative numbers. Scope isolation beyond the iterator context and composition parameters. Mixed-type lists. Descending ranges. Ranges over 10,000 items. Nested records (and therefore chained `of`). `choose` inside `each`. Sophisticated cycle detection beyond same-handler-twice. Adapter timeout or preemption. Tile interface, proposal engine, domain packs as product surfaces. @@ -502,7 +502,7 @@ These are the load-bearing decisions that shape every implementation choice. Eac **The prose IS the program.** The interpreter operates exclusively on what the user stated. It does not infer, assume, guess, or fill in unstated information. If the prose doesn't say it, it doesn't happen. -**The vocabulary is the boundary.** 60 reserved words in the current build. v2c added a quoting mechanism for multi-word string values, but only in value positions — names and field names still come from the unquoted name-space. Vocabulary words cannot appear unquoted as user-provided names or as string values. This is structurally why slot-filling parser logic works: every word's category is known in advance. Each addition to the vocabulary (v2a's `keep`, v2a's `of`, v2d's `choose`/`if`/`otherwise`, v3a's `when`/`unless`/`finish`, the `add`/`remove`/`includes`/`within` addenda, Metabolic Era's `weakens`/`over`, Normative Era's `require`/`then`, Delegated/Epistemic Era's `assign`/`expect`, the Infrastructure Era's `by`/`plus`/`minus`/`multiplied by`/`divided by` and `sort`/`reverse`, the V2 promotions `compare`/`transform`, the Meta-Structural Era's `about`/`because`/`inherited`, the Deontic Era's `forbid`/`permit`, the Temporal-Boundary Era's `starting`/`until`, and the v0.15.0 vocabulary wave's `highest`/`lowest`) is the smallest spec change consistent with a dogfooded gap. The same wave renamed `combine` to `sum` — the only word ever removed from an active category, replaced by a tombstone that rejects the old name with a rename-specific error instead of a generic one. +**The vocabulary is the boundary.** 61 reserved words in the current build. v2c added a quoting mechanism for multi-word string values, but only in value positions — names and field names still come from the unquoted name-space. Vocabulary words cannot appear unquoted as user-provided names or as string values. This is structurally why slot-filling parser logic works: every word's category is known in advance. Each addition to the vocabulary (v2a's `keep`, v2a's `of`, v2d's `choose`/`if`/`otherwise`, v3a's `when`/`unless`/`finish`, the `add`/`remove`/`includes`/`within` addenda, Metabolic Era's `weakens`/`over`, Normative Era's `require`/`then`, Delegated/Epistemic Era's `assign`/`expect`, the Infrastructure Era's `by`/`plus`/`minus`/`multiplied by`/`divided by` and `sort`/`reverse`, the V2 promotions `compare`/`transform`, the Meta-Structural Era's `about`/`because`/`inherited`, the Deontic Era's `forbid`/`permit`, the Temporal-Boundary Era's `starting`/`until`, the v0.15.0 vocabulary wave's `highest`/`lowest`, and v0.16.0's `define`) is the smallest spec change consistent with a dogfooded gap. The same v0.15.0 wave renamed `combine` to `sum` — the only word ever removed from an active category, replaced by a tombstone that rejects the old name with a rename-specific error instead of a generic one. v0.16.0's `unless`-on-deontics and Calendar Era additions are grammar and value-type extensions, not new words — `define` is the only word added since `highest`/`lowest`. **The reorderer does not guess.** When an arrangement of words could fill slots in more than one valid way, the system produces an amber clarification prompt rather than picking one interpretation. Authorship over inference. @@ -562,7 +562,7 @@ liminate/ └── pack_ui.json v4a UI domain pack: 10 nouns + `navigate` verb ``` -1499 tests pass via `pytest tests/`. Each spec section that locks a behavior has at least one test that exercises it. +1654 tests pass via `pytest tests/`. Each spec section that locks a behavior has at least one test that exercises it. --- @@ -589,7 +589,7 @@ Sentence coverage across the test suite: | 114–117 | Quoted-string case preservation across lex, render, and `where` equality | | 118–127 | `navigate to ` basic + semantic errors + parse error, UI components with known and freeform fields, `when` on UI components, `navigate` inside an action block, pack-verb reserved-word check, pack noun usable as name without pack | -All 127 sentences are wired through the test suite. v1/v2a/v2b/v2c/v2d coverage lives in `tests/test_integration.py`; v3a in `tests/test_integration_v3a.py`; v4a in `tests/test_integration_v4a.py`. The full suite is 1499 tests. +All 127 sentences are wired through the test suite. v1/v2a/v2b/v2c/v2d coverage lives in `tests/test_integration.py`; v3a in `tests/test_integration_v3a.py`; v4a in `tests/test_integration_v4a.py`. The full suite is 1654 tests (127 locked sentences plus coverage for every era and vocabulary wave since). --- @@ -639,7 +639,7 @@ The build is a paired collaboration. The architect produces and approves every d ## Status and what's next -**Currently shipped.** v1 → v2d (sequential) + v3a/v3b (event-driven listener mode + quoted-string case preservation) + v4a (pack verb contract + UI domain pack) + Metabolic / Normative / Delegated / Epistemic / Infrastructure / Meta-Structural / Deontic / Temporal-Boundary era batches + `require each` grammar extension + Platform + Public API infrastructure + Pack-Verb Expansion Era (`validate`/`fit` pack verbs, iterable pack verbs, `PACK_VERB_FAILURE` status, empty-list/range additions) + v0.15.0 vocabulary wave (`combine` renamed to `sum`, tombstoned; `highest`/`lowest` list-extrema operators). 1499 tests passing. The interpreter runs in a terminal as text-only, reads `.limn` source files, and offers an interactive REPL with `--quiet`, `--test`, and `--pack` flags. A separate TypeScript port (lexer/reorderer/parser/analyzer/renderer — no interpreter) lives in the Möbius monorepo at `packages/liminate-lang/` and validates the same 127 sentences against this implementation as its sync contract. +**Currently shipped.** v1 → v2d (sequential) + v3a/v3b (event-driven listener mode + quoted-string case preservation) + v4a (pack verb contract + UI domain pack) + Metabolic / Normative / Delegated / Epistemic / Infrastructure / Meta-Structural / Deontic / Temporal-Boundary era batches + `require each` grammar extension + Platform + Public API infrastructure + Pack-Verb Expansion Era (`validate`/`fit` pack verbs, iterable pack verbs, `PACK_VERB_FAILURE` status, empty-list/range additions) + v0.15.0 vocabulary wave (`combine` renamed to `sum`, tombstoned; `highest`/`lowest` list-extrema operators) + v0.16.0 (`unless` exception clauses on the deontic verb family; the Calendar Era's `date` value type; the Definitional Era's `define` declaration and named predicates; predicate-aware contradiction detection). 1654 tests passing. The interpreter runs in a terminal as text-only, reads `.limn` source files, and offers an interactive REPL with `--quiet`, `--test`, and `--pack` flags. A separate TypeScript port (lexer/reorderer/parser/analyzer/renderer — no interpreter) lives in the Möbius monorepo at `packages/liminate-lang/` and validates the same 127 sentences against this implementation as its sync contract. **The largest remaining work is not language additions.** It's everything around the language — Future surfaces and domain packs as product surfaces. Specifically: diff --git a/docs/architecture/pipeline.md b/docs/architecture/pipeline.md index b2b746c..d95d0e6 100644 --- a/docs/architecture/pipeline.md +++ b/docs/architecture/pipeline.md @@ -141,6 +141,12 @@ the most mechanical stage: - Accumulates a `"..."` run as a single `QUOTED_STRING` token (v2c §86). Quoted content preserves spaces and any punctuation; the vocabulary lookup is bypassed for quoted tokens (§89). +- Recognizes a bare `YYYY-MM-DD` word as a `TokenType.DATE` token + (Calendar Era, v29) — a date is recognized by shape, the same way a + bare number is recognized by its digit shape. Quoting a date-shaped + value (`"2025-07-01"`) still produces a `QUOTED_STRING`, so the + string/date distinction stays under the author's control. Zero new + reserved words. - Identifies blank lines (and lines that are only punctuation) as producing zero tokens, which the rest of the pipeline skips. - Exposes a separate `leading_indent(line)` helper (v3a §110) that @@ -195,8 +201,11 @@ plus one-token lookahead: - `and` / `or` — list construction, compound condition, operation sequencing, record-field continuation, or multi-field display inside `each ... show` (five contexts). -- `is` — comparison introducer (followed by an operator) or equality - operator (followed by a value). +- `is` — comparison introducer (followed by an operator), equality + operator (followed by a value), or (v0.16.0, Definitional Era) a + predicate application if the following bareword is a `define`d + predicate name — checked against a parser-maintained predicate table + before falling through to string equality. - `not` — always modifies the operator that follows. - `to` — range endpoint after `from `, or part of `equal to`. - `from` — range start in `gather`, result capture in `remember` @@ -209,8 +218,9 @@ plus one-token lookahead: - `if` / `otherwise` — branch introducers inside `choose` (v2d §99). - `when` — top-level handler registration; rejected with a specific error in any nested position (v3a §108). -- `unless` — guard clause on a `when` line; rejected anywhere else - (v3a §109). +- `unless` — guard clause on a `when` line (v3a §109), and (v0.16.0) + an independent exception-condition clause on `require`/`forbid`/ + `permit`/`expect`; rejected in any other position. If any condition (in `where`, `choose if`, `when`, or `unless`) mixes both `and` and `or`, the parse still succeeds (standard precedence: @@ -229,6 +239,33 @@ If parsing fails, the parser returns a parse error with a plain- English message — never an "unexpected token at column N" style error. +**Exception clauses (v0.16.0).** `RequireNode`, `ForbidNode`, +`PermitNode`, and `ExpectNode` each carry an `exception: ASTNode | +None` field. After the verb-specific sub-parser consumes the main +condition via `_parse_or_condition`, it peeks for `unless` and, if +found, calls `_parse_or_condition` again for a complete, independent +exception condition tree — the same structural model as the `unless` +guard on `WhenNode`. The exception sits before the statement-terminal +`because`/`from` metadata layer, so the canonical order is `starting +... until ... inherited unless because +"..." from `. + +**Predicate table (v0.16.0, Definitional Era).** The parser maintains +a predicate table — a `dict[str, ASTNode]` — populated as `define +: ` statements are parsed. `DefineNode` carries the +name and the condition AST. Elsewhere, `_finish_simple_condition` +checks the table after consuming `is` (or `is not`): a match produces +a `PredicateApplicationNode` (`subject`, `predicate_name`, `negated`); +no match falls through to the existing equality path unchanged. Because +the check lives in the leaf function every condition-accepting +construct converges to, predicates work in `where`, `keep`, `filter`, +`require` (including `require each`), `forbid`, `permit`, `expect`, +`choose if`, `when`, and `unless` with no verb-specific parser changes. +The forward-declaration requirement (a predicate must be `define`d +above any line that uses it) structurally rules out recursion — a +predicate body may reference another predicate, but never itself, +directly or mutually. + ### Canonical renderer The renderer is the inverse of the parser. It walks the AST and @@ -283,6 +320,30 @@ the things it verifies: a composition whose last operation is `show`/`filter`/`each`/ `choose`/`finish` cannot be captured in `remember the X from `. +- **Exception clauses (v0.16.0).** The `exception` tree on + `RequireNode`/`ForbidNode`/`PermitNode`/`ExpectNode` is validated + through `_check_choose_condition` — the same path used for the main + condition and for `WhenNode`'s `unless` guard. `_contains_mixed_precedence` + checks the exception tree too, so a mixed `and`/`or` inside an + exception triggers the same amber-precedence confirmation as + anywhere else. +- **Predicates (v0.16.0, Definitional Era).** A `DefineNode` is + validated for condition grammar (legal operators and structure) but + not for field existence — field names in a predicate body resolve + later, at application time, against whatever context the predicate + is applied in. A `PredicateApplicationNode` is validated by checking + that its `predicate_name` exists in the parser's predicate table; an + undefined name is a semantic error. Redefining a predicate name + emits an amber warning rather than an error. +- **Predicate-aware contradiction detection (v0.16.0).** The + contradiction pre-pass threads predicate names through instead of + treating a predicate application as an ordinary field/value + comparison, and treats predicate applications as opaque atoms during + satisfiability analysis — so a guarded or predicated `require`/ + `forbid` pair no longer produces false-positive contradiction + warnings or crashes the detector. When a detected conflict involves + an `unless`-guarded deontic, the warning names the exception + condition rather than reporting an unconditional contradiction. For Phase 2 `when` registration, the analyzer validates only the condition and `unless` guard (v3a §108) — action-block statements are @@ -335,6 +396,24 @@ table. Key behaviors: - **Copy semantics everywhere.** Data is copied when stored or retrieved by name. Two names never alias the same underlying collection. +- **Exception evaluation is polarity-appropriate per verb (v0.16.0).** + `require` halts on `NOT main AND NOT exception`; `forbid` halts on + `main AND NOT exception`; `permit` emits on `main AND NOT exception` + (the exception narrows the permission); `expect` reports divergence + on `NOT main AND NOT exception`. +- **Predicates are evaluated live, not snapshotted (v0.16.0).** A + `PredicateApplicationNode` re-evaluates its stored condition AST + against the current context every time it runs — fields resolve + against the current iterator item (or an explicit record subject), + and names resolve against the current symbol table. If a value the + predicate depends on changes via `remember`, every subsequent + application sees the new value. +- **Date arithmetic is whole-day only (Calendar Era, v29).** + `date plus number` and `date minus number` return a date; `date + minus date` returns the day count between them as a number. `date + plus date`, and multiplying or dividing a date, are runtime errors. + Every comparison operator and `within` (day-based tolerance) work on + dates; comparing a date to a number or string is a type error. - **Stepwise execution.** When several operations are joined by `and` (sequencing, not condition), each one commits independently. If a later one fails, earlier side effects remain and the error message diff --git a/docs/language/syntax.md b/docs/language/syntax.md index 9a81909..e94cb95 100644 --- a/docs/language/syntax.md +++ b/docs/language/syntax.md @@ -1,7 +1,7 @@ # Liminate syntax A practical guide to writing Liminate programs. Liminate is a bounded -prose language: 60 reserved words plus user-provided names and literal +prose language: 61 reserved words plus user-provided names and literal values. The prose IS the program. This guide covers the full shipped surface: v1, v2a (`keep`, `of`, @@ -15,9 +15,13 @@ additions, the Infrastructure Era (`by`/`plus`/`minus`/`multiplied by`/`divided by` arithmetic, `sort`/`reverse`, `compare`, `transform`), the Deontic Era (`forbid`/`permit` — completing the require/forbid/permit triangle), the Temporal-Boundary Era -(`starting`/`until` — effective dates and sunset clauses), and the +(`starting`/`until` — effective dates and sunset clauses), the v0.15.0 vocabulary wave (`combine` renamed to `sum`; `highest`/`lowest` -list-extrema selectors). +list-extrema selectors), `unless` exception clauses on the deontic +verb family (`require`/`forbid`/`permit`/`expect`, v0.16.0, zero new +words), the Calendar Era (`date` as a third scalar value type, v0.16.0, +zero new words), and the Definitional Era (`define` — named, reusable +domain predicates, v0.16.0, one new declaration). See [`../roadmap/v1-v2-boundary.md`](../roadmap/v1-v2-boundary.md) for what's intentionally not built. @@ -51,7 +55,7 @@ three rules: - Start with a letter. - Contain letters, digits, and hyphens. -- Cannot be one of the 60 reserved words. +- Cannot be one of the 61 reserved words. Valid: `age`, `orders`, `find-big-orders`, `order1`, `my-list`. @@ -1035,7 +1039,7 @@ A literal value can be: `"filter"` (the literal word "filter" as data, not the verb). See [Quoting](#quoting-v2c) for the full rules. -Vocabulary words (the 60 reserved words) cannot be used **unquoted** +Vocabulary words (the 61 reserved words) cannot be used **unquoted** as values: ``` @@ -1129,6 +1133,73 @@ references `today` is unaffected by its presence. require due-date is below today ``` +## Named predicates (`define`) + +The Definitional Era (v31) adds `define` — a declaration that names a +reusable, testable condition (a domain adjective like "overdue" or +"high-risk") once, instead of repeating the condition text everywhere +it's used. `define` is the second declaration after `about`. One new +word. + +``` +define overdue: due-date is below cutoff +``` + +The name becomes usable anywhere the condition grammar is — `where`, +`keep`, `filter`, `require`, `require each`, `forbid`, `permit`, +`expect`, `choose if`, `when`, and `unless` — as `is ` or `is not +`: + +``` +filter the orders where each is overdue +keep the orders where each is not overdue +forbid total is above 10000 unless each is overdue +``` + +**The `is ` collision** is resolved at parse time: `is +overdue` produces a predicate application if `overdue` has been +`define`d, and ordinary string equality otherwise. Quoting still forces +literal equality (`is "overdue"`) for the rare case where a word +happens to collide with a predicate name. + +**Forward declaration.** A predicate must be `define`d before it's +used — `define overdue: ...` has to appear above any line that tests +`is overdue`. This matches `remember how to` (compositions must be +defined before they're called) and matches reading order. + +**Predicates compose.** A predicate body may reference another +predicate: + +``` +define overdue: due-date is below cutoff +define high-risk: is overdue and total is above 10000 +``` + +The forward-declaration rule makes recursion structurally impossible — +by the time a predicate references another by name, that other +predicate must already exist. + +**Predicates are live, not snapshots.** Names inside a predicate body +resolve against the symbol table at the moment the predicate is +evaluated, not at the moment it was defined. If `cutoff` changes via +`remember`, every use of `is overdue` picks up the new value +immediately. + +**Scope and redefinition.** `define` is top-level only — it can't +appear inside a `when` block, an `each` body, or a `choose` branch. +Redefining a name overwrites the earlier definition (the same mutation +semantics as `remember`); the analyzer emits an amber warning so +accidental shadowing doesn't pass silently. + +**Field elision.** Inside a `define` body, an elided left-hand side +(a condition that starts with `is`, `not`, or `includes`) refers to the +current item, the same as inside `require each`: + +``` +define big: is above 100 +filter the numbers where each is big +``` + ## Mixed `and` / `or` and the amber prompt A `where` clause that mixes `and` and `or` is unambiguous to the diff --git a/docs/roadmap/v1-v2-boundary.md b/docs/roadmap/v1-v2-boundary.md index f1f17ca..57b696a 100644 --- a/docs/roadmap/v1-v2-boundary.md +++ b/docs/roadmap/v1-v2-boundary.md @@ -34,29 +34,32 @@ If a feature is shipped, it has a locked specification, a working implementation ### Execution model -- **Two-phase execution.** Phase 1 runs every top-level sequential statement (`remember`, `show`, `filter`, `keep`, `count`, `gather`, `combine`, `each`, `choose`, named composition calls) in source order. Phase 2 starts after Phase 1 completes with zero errors AND at least one `when` handler registered. (v3a §107) +- **Two-phase execution.** Phase 1 runs every top-level sequential statement (`remember`, `show`, `filter`, `keep`, `count`, `gather`, `sum`, `each`, `choose`, named composition calls) in source order. Phase 2 starts after Phase 1 completes with zero errors AND at least one `when` handler registered. (v3a §107) - **Stepwise sequences.** When operations chain with `and` between complete verb phrases, each operation commits independently. A later failure does not roll back earlier side effects. (v1d §56) - **Single-threaded event queue (Phase 2).** Adapters push `(name, value)` updates into a shared FIFO. The interpreter drains one update to completion (write → re-eval → fire-eligible → cascade) before the next dequeue. (v3a §119) - **Edge-triggered evaluation (Phase 2).** Handlers fire on false→true transitions of their compound eligibility. Unchanged adapter updates are silently absorbed. Modifications inside an action block are coalesced by name and cascade depth-first. (v3a §113/§114) -### Vocabulary (58 reserved words) +### Vocabulary (61 reserved words) -- **21 verbs:** `remember`, `show`, `filter`, `keep`, `count`, `gather`, `combine`, `each`, `choose`, `finish`, `add`, `remove`, `weakens`, `require`, `forbid`, `permit`, `assign`, `expect`, `sort`, `compare`, `transform`. +- **21 verbs:** `remember`, `show`, `filter`, `keep`, `count`, `gather`, `sum`, `each`, `choose`, `finish`, `add`, `remove`, `weakens`, `require`, `forbid`, `permit`, `assign`, `expect`, `sort`, `compare`, `transform`. (`combine` is tombstoned — renamed to `sum` in v0.15.0; reserved but inactive, rejected with a rename-specific error, excluded from the count.) - **22 connectives:** `where`, `and`, `or`, `from`, `with`, `called`, `to`, `how`, `as`, `of`, `if`, `otherwise`, `when`, `unless`, `includes`, `within`, `over`, `then`, `by`, `because`, `starting`, `until`. -- **8 single-word operators:** `is`, `above`, `below`, `not`, `plus`, `minus`, `reverse`, `inherited`. Plus `equal`/`multiplied`/`divided` as multi-word components (combine with `to`/`by` per inception §22). +- **10 single-word operators:** `is`, `above`, `below`, `not`, `plus`, `minus`, `reverse`, `inherited`, `highest`, `lowest`. Plus `equal`/`multiplied`/`divided` as multi-word components (combine with `to`/`by` per inception §22). - **3 articles:** `the`, `a`, `an`. -- **1 declaration:** `about`. +- **2 declarations:** `about`, `define`. - **1 delimiter:** `:`. - **0 v2-deferred:** `V2_RESERVED` is now empty — `transform` and `compare` have been promoted to active verbs. The Meta-Structural Era added three self-describing words as inert metadata (visible to rendering and `inspect`, never executed): `about` (program topic declaration, first line), `because` (statement-terminal quoted rationale on any verb statement), and `inherited` (statement-initial provenance modifier marking a statement as carried forward from a prior context, with optional `from ` attribution). -The Deontic Era completed the obligation/prohibition/permission triangle: `forbid` (halts with `PROHIBITION_VIOLATED` when its condition is true — the mirror of `require`) and `permit` (emits an informational line when its condition is true, never halts — the `expect` pattern). The Temporal-Boundary Era added `starting` and `until`, statement-initial connectives attaching quoted ISO 8601 effective dates and sunset clauses as inert metadata; temporal evaluation is a product-layer concern, not interpreter runtime. +The Deontic Era completed the obligation/prohibition/permission triangle: `forbid` (halts with `PROHIBITION_VIOLATED` when its condition is true — the mirror of `require`) and `permit` (emits an informational line when its condition is true, never halts — the `expect` pattern). The Temporal-Boundary Era added `starting` and `until`, statement-initial connectives attaching quoted ISO 8601 effective dates and sunset clauses as inert metadata; temporal evaluation is a product-layer concern, not interpreter runtime. The v0.15.0 vocabulary wave renamed `combine` to `sum` and added `highest`/`lowest` as list-extrema value selectors. + +v0.16.0 added three more surfaces without breaking the two-phase shape: `unless` exception clauses on the deontic verb family (`require`/`forbid`/`permit`/`expect` — zero new words, an existing connective in a new grammatical position); the Calendar Era (`date` as a third scalar value type — zero new words, recognized by shape like a bare number); and the Definitional Era (`define : ` — one new declaration, joining `about` — names a reusable, testable condition referenced elsewhere as `is ` / `is not `, working everywhere the condition grammar already works). ### Data types and values - **Numbers** — non-negative integers and decimals (`30`, `3.14`). -- **Single-word strings** — bare words that are neither numbers nor reserved words (`active`, `portland`). Case-folded to lowercase. +- **Dates** — bare ISO 8601 literals, `YYYY-MM-DD` (`2025-07-01`). Whole-day arithmetic, all comparison operators, day-based `within` tolerance, and `highest`/`lowest`/`sort` support. (Calendar Era, v0.16.0) +- **Single-word strings** — bare words that are neither numbers, dates, nor reserved words (`active`, `portland`). Case-folded to lowercase. - **Multi-word / quoted strings** — `"in progress"`, `"high priority"`. Values only (names use hyphens). Quoting bypasses the reserved-word vocabulary lookup, so `with label as "filter"` stores the literal string "filter". The renderer drops quotes around safe single-word values when echoing canonical prose. (v2c §86–§92) - **Homogeneous lists** — all numbers, all strings, or all records. - **Records** — named-field bundles built with `as`. @@ -66,7 +69,7 @@ The Deontic Era completed the obligation/prohibition/permission triangle: `forbi - **In-place `filter`** — modifies the target list directly. - **Non-destructive `keep`** (v2a §67) — returns a fresh list of matches; source unchanged. Auto-shows by default; captures via `remember ... from keep ...`. Enables reusable filter compositions. -- **Non-destructive `combine`** — returns the sum without changing the source. +- **Non-destructive `sum`** — returns the sum without changing the source. - **Generalized `of`** (v2b §77) — ` of ` works in any value position: after `show`, in `where`/`choose if`/`when`/`unless` conditions on either side, in `with`/`from` value position. Single-level only; chained `of` (`a of b of c`) stays rejected until nested records exist. - **Multi-field `each show`** (v2a §69) — `each the docs show A and B` emits `A: ..., B: ...` per record. Duplicate field names are a semantic error. - **`choose` conditional branching** (v2d §99) — `choose if : [otherwise [if :] ]*`. First matching branch fires; terminal `otherwise` is a fallback. Side-effect-only. @@ -74,7 +77,7 @@ The Deontic Era completed the obligation/prohibition/permission triangle: `forbi - **`finish` immediate-and-total shutdown** (v3a §112) — inside an action block (directly, inside a `choose` branch, or inside a composition called from one), `finish` terminates the listener. Yields only the SHUTDOWN result. - **Composition return values** (v2b §76) — `remember the X from ` captures the value of the composition's last operation. Side-effect-only last ops (`show`/`filter`/`each`/`choose`/`finish`) are rejected at the call site. - **Composition parameters** (v2d §96) — one named parameter, names-only, deep-copy semantics, local binding with global shadow/restore. Parameter-mismatch errors at the call site. -- **Auto-show** — `count`, `combine`, `gather`, and `keep` (standalone) display their results without an explicit `show`. +- **Auto-show** — `count`, `sum`, `gather`, and `keep` (standalone) display their results without an explicit `show`. - **Copy semantics** — every data operation copies values. Two names never alias the same underlying collection. - **Iterator context for `each`** — during iteration, names resolve first as a field on the current record, then against the symbol table. From f6aff13d12d81aa50821a79f127f4603e84a4212 Mon Sep 17 00:00:00 2001 From: rmichaelthomas Date: Sat, 4 Jul 2026 21:45:19 -0700 Subject: [PATCH 2/3] docs: add website handoff packet for v28-v31 vocabulary wave Records the liminate-dev site changes made this session (already executed directly, not left pending) per the docs-propagation protocol's requirement that the website handoff always be produced as a separate deliverable. Co-Authored-By: Claude Sonnet 5 --- ...doff_2026_07_04_v28_v31_vocabulary_wave.md | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 docs/handoffs/liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md diff --git a/docs/handoffs/liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md b/docs/handoffs/liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md new file mode 100644 index 0000000..fd07c57 --- /dev/null +++ b/docs/handoffs/liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md @@ -0,0 +1,59 @@ +# Liminate Site Update — Handoff Packet + +**Date:** 2026-07-04 +**Source:** Liminate checkpoints v28 (Unless on Deontics Design Lock), v29 (Calendar Era Design Lock), v30 (Calendar Era Build Completion), v31 (Definitional Era Design Lock) — all July 4, 2026. Verified against `src/liminate/vocabulary.py` on `liminate@main`. +**Scope:** Vocabulary expanded from **60 → 61 reserved words** (21 verbs, 22 connectives, 10 operators, 3 articles, 3 multi-word reserved, 2 declarations). One new word: `define` (a declaration, joining `about`). Two grammar/value-type extensions that added **zero** new words: `unless` exception clauses on the deontic verb family (`require`/`forbid`/`permit`/`expect`), and the Calendar Era's `date` as a third scalar value type. Version v0.15.0 → v0.16.0. Test count 1499 → 1654. + +**Status of this packet:** Unlike prior handoff packets in this directory, the `liminate-dev` changes described below were **already executed directly** this session (Claude Code has write access to the `liminate-dev` repo locally) rather than left as a pending handoff. This packet is the record of what changed, not an instruction queue. Branch: `docs/propagate-v28-v31-vocabulary-wave` in `liminate-dev`, not yet pushed or merged. + +## Pages Updated + +| Page | Path | What changed | +|---|---|---| +| Ecosystem README | `README.md` | "58 reserved words" → "61 reserved words" | +| Receipts app shell | `static/index.html` | Syntax-highlighter JS: `VERBS` set `combine`→`sum`, `OPERATORS` set +`highest`/`lowest`, `DECLARATIONS` set +`define`. This was a **functional** fix, not just copy — those tokens weren't highlighting correctly before. | +| Landing page | `static/landing.html` | Hero stat `58`→`61`; "58-word interpreter" headline; "58-word vocabulary" in the how-it-works copy | +| Mood Ring reading page | `static/moodring-reading.html` | "58-word" → "61-word"; "58 reserved words" → "61 reserved words" | +| Mood Ring product page | `static/moodring.html` | OG/Twitter meta description + footer copy, "58 words" → "61 words" | +| Pulse product page | `static/pulse/index.html` | OG meta description, "58 words" → "61 words" | +| Site home | `static/site/index.html` | Status pills: `v0.14.0`→`v0.16.0`, `1456 tests`→`1654 tests`, `58 reserved words`→`61 reserved words` | +| Language reference doorway | `static/site/language/index.html` | Meta description + lede + vocabulary-page teaser + values-page teaser (added "dates"), all `58`→`61`, operator/declaration counts updated | +| Vocabulary reference | `static/site/language/vocabulary/index.html` | Full refresh: word count, verb table (`combine`→`sum`), operator table (+`highest`/`lowest`), declaration table (+`define`), and **new prose** (both plain-English and technical registers) covering `unless`-on-deontics, the Calendar Era, and the Definitional Era | +| Values reference | `static/site/language/values/index.html` | Added `date` as a value type, with a short explanation and example | +| Spec/repo page | `static/site/spec/index.html` | Rewrote the dated `v0.14.0` status snapshot in place to `v0.16.0` / 61 words / 1654 tests, appending the v0.15.0 wave and v0.16.0 features to the technical-register feature list | + +## Content Diffs + +### `static/site/language/vocabulary/index.html` + +**Current text (plain-English lede):** "Fifty-eight reserved words." / "The only other words allowed are the names you make up yourself and the values you write — like numbers and text." +**Updated text:** "Sixty-one reserved words." / "...like numbers, dates, and text." + +**New paragraphs added** (both plain and technical registers) covering: +- `unless` exception clauses on `require`/`forbid`/`permit`/`expect` — framed as "no new word, it just learned a new place to stand" +- The Calendar Era's `date` value type — framed as "recognized by its shape, the way a number already was" +- The Definitional Era's `define` declaration — framed as "the newest word... names a rule so you can reuse it" + +### `static/site/spec/index.html` + +**Current text:** `Status: v0.14.0. Current source has 58 reserved words, 139+ locked test sentences, and 1456 pytest cases.` +**Updated text:** `Status: v0.16.0. Current source has 61 reserved words, 139+ locked test sentences, and 1654 pytest cases.` — plus the technical-register feature list extended with the v0.15.0 vocabulary wave and v0.16.0's three additions. + +## New Pages (if any) + +None. No new page is warranted — `unless`-on-deontics and the Calendar Era are grammar/value-type extensions of existing pages (vocabulary, values), not new surfaces. + +## Style and Tone Notes + +The site's language pages run a dual-register pattern throughout (`data-plain` / `data-tech` spans toggled by a reading-mode switch). Plain-English register stays concrete and avoids implementation jargon ("it just learned a new place to stand" rather than "new grammatical position"); technical register uses precise interpreter vocabulary (AST node names, parse-time vs. runtime, etc.). New copy added this pass follows both registers consistently with the surrounding page. No marketing language or hype was introduced — all claims trace to the checkpoints and to `vocabulary.py`. + +## Do Not Change + +- `static/moodring-archive.html`, `static/case-study-*.html`, `static/legal-*.html` — out of scope, no vocabulary references found +- `static/agreements*.html`, `static/sentinels*.html`, `static/translate*.html` — separate product surfaces, not scanned this pass (no evidence they reference the reserved-word count) +- Anything under `benchmarks/` — historical performance records +- Prior handoff packets in this directory — archival, never modified + +## Not In Scope This Pass + +Per the architect's explicit call: `liminate-receipts` was not scanned or updated — it is superseded by `liminate-dev` (Receipts now lives at `liminate.dev/receipts`, served by this repo) and is not a separate propagation target going forward. From 88342dbb48477733708dd9fe7029b6f54821aa36 Mon Sep 17 00:00:00 2001 From: rmichaelthomas Date: Sat, 4 Jul 2026 21:52:19 -0700 Subject: [PATCH 3/3] docs: fix stale push-status line in v28-v31 handoff packet The liminate-dev branch was pushed after this packet was written; update the status line to match. Co-Authored-By: Claude Sonnet 5 --- .../liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handoffs/liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md b/docs/handoffs/liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md index fd07c57..e9d154e 100644 --- a/docs/handoffs/liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md +++ b/docs/handoffs/liminate_site_handoff_2026_07_04_v28_v31_vocabulary_wave.md @@ -4,7 +4,7 @@ **Source:** Liminate checkpoints v28 (Unless on Deontics Design Lock), v29 (Calendar Era Design Lock), v30 (Calendar Era Build Completion), v31 (Definitional Era Design Lock) — all July 4, 2026. Verified against `src/liminate/vocabulary.py` on `liminate@main`. **Scope:** Vocabulary expanded from **60 → 61 reserved words** (21 verbs, 22 connectives, 10 operators, 3 articles, 3 multi-word reserved, 2 declarations). One new word: `define` (a declaration, joining `about`). Two grammar/value-type extensions that added **zero** new words: `unless` exception clauses on the deontic verb family (`require`/`forbid`/`permit`/`expect`), and the Calendar Era's `date` as a third scalar value type. Version v0.15.0 → v0.16.0. Test count 1499 → 1654. -**Status of this packet:** Unlike prior handoff packets in this directory, the `liminate-dev` changes described below were **already executed directly** this session (Claude Code has write access to the `liminate-dev` repo locally) rather than left as a pending handoff. This packet is the record of what changed, not an instruction queue. Branch: `docs/propagate-v28-v31-vocabulary-wave` in `liminate-dev`, not yet pushed or merged. +**Status of this packet:** Unlike prior handoff packets in this directory, the `liminate-dev` changes described below were **already executed directly** this session (Claude Code has write access to the `liminate-dev` repo locally) rather than left as a pending handoff. This packet is the record of what changed, not an instruction queue. Branch: `docs/propagate-v28-v31-vocabulary-wave` in `liminate-dev`, pushed to origin; not yet merged. ## Pages Updated