usm/internal-dsl-builder [built]
Fluent typed TypeScript builder (internal DSL, per Fowler) that compiles to validated .usm YAML — defineFeature/defineService chains populate the semantic model and build() enforces the schema, exported from the package's public API.
Status: built
Intent
Authoring flows/contracts/tests in raw YAML is verbose and error-prone for humans and agents alike. Fowler's answer for this exact pain is an internal DSL: a fluent host-language builder that populates the semantic model and inherits the host's tooling (types, autocomplete, refactoring). LLMs write TypeScript far better than any bespoke grammar, so the builder aligns human ergonomics with agent capability. Constraints stay enforced by schema validation at build() — the YAML/JSON path remains first-class.
Decisions
internal-over-external
Decision: Internal (host-language) DSL, not a custom grammar
Rationale: Fowler's cost test: inherit TS tooling and LLM fluency, skip parser maintenance forever. The semantic model (.usm) remains the asset.
runtime-schema-validation
Decision: Enforce constraints by validating at build() rather than type-level branding
Rationale: Keeps the fluent API simple and the JSON Schema the single source of truth; builders cannot silently bypass the same validation MCP tools enforce.
Flows
Compose a feature spec with the builder (build-feature)
- call → defineFeature('org/slug', { system, service })
- chain → .summary().intent().status().flow(id, fn).contract(id, fn).test(id, fn).decision(id, fn).seeAlso([])
- build → returns { yaml, object, valid, errors } — schema-validated before hand-back
Write a built spec to disk (emit-and-write)
- build → the spec via builder
- write → writeFeature(result, path) — refuses invalid specs, atomic write
Extend an existing spec (extend-existing)
- load → defineFeature from a parsed existing .usm file
- mutate → chain additions (flows append by id)
- rebuild → revalidate
Contracts
builder-emits-valid-usm
Every build() result passes schema validation or reports errors explicitly
Acceptance criteria:
- [ ] build() runs validateUsm and includes errors in the result
- [ ] writeFeature refuses to write when valid is false
- [ ] Generated YAML matches MCP write-tool serialization conventions
required-fields-enforced
The builder surface mirrors the schema's required fields
Acceptance criteria:
- [ ] summary and intent required before a valid build
- [ ] $system and $service required at construction
- [ ] flows get steps; contracts get must_have; tests get expect
exported-public-api
The DSL ships as part of the package's public entry
Acceptance criteria:
- [ ] import { defineFeature, defineService, writeFeature } from '@smithgray/usm' works
- [ ] Types exported for IDE autocomplete
roundtrip-fidelity
Builder output parses back to the same object
Acceptance criteria:
- [ ] yaml → parseUsm → deep-equals object
Tests
builds-valid-feature
Then:
- assertion: chained builder builds with valid: true
- assertion: yaml parses back deep-equal to object
invalid-build-reported
Given:
- missing_intent: true
Then:
- assertion: valid: false with schema errors listed
- assertion: writeFeature refuses and writes nothing
extend-existing
Given:
- existing_parsed_feature: true
Then:
- assertion: loaded builder appends flows by id without dropping existing
- assertion: rebuild validates
service-builder
Then:
- assertion: defineService builds a schema-valid service with type and runtime
Implementation
- Primary: src/dsl/index.ts
- Test code: tests/dsl.test.ts
- Test code status: manual
See Also
- usm/mcp-write
- usm/schema-v1
- usm/query-layer