docs(planning): draft document-scaffolding — C-0011 capability + D-0017 generation gaps#201
docs(planning): draft document-scaffolding — C-0011 capability + D-0017 generation gaps#201sksizer wants to merge 3 commits into
Conversation
… gaps Add two draft planning docs exploring contract-driven document generation — the "basis for a template engine" the Why page now points at — as the dual of config scaffolding (C-0008 / D-0009: docs → contract; this: contract → document). - C-0011 (capability, product view): a `markdown-contract new <contract>` verb that emits an empty-but-valid starting document; the create door beside validate/read; framing for the template-engine and CMS claims. - D-0017 (decision, engineering gap analysis): what generates deterministically from the section grammar, where frontmatter inverts (declarative IR) vs. is best-effort (combinator Zod), the one hard gap (regex pattern / typed cells) and the proposed example:/placeholder: annotation to close it, the proposed surface, and the round-trip-to-valid property. Both are status open/proposed (draft), need_human_review, and validate clean against contracts/decision + contracts/capability via the dogfood config.
3f8bab9 to
acf3afe
Compare
| `new` at a contract and it walks the contract's declared shape and emits a fresh | ||
| `.md` whose structure passes that contract by construction. | ||
|
|
||
| The generated document is a **filled outline**: YAML frontmatter with every |
There was a problem hiding this comment.
I also had an idea that we could emit a typed function over the contract, or via the templates wrapper library that provides a typed data structure of the values to fill in ie:
contract defines frontmatter fields like:
let contract = Contract(
front:matter: {
status: <active|closed|pending>
})
then the template.create(contract).( {frontmatter: {status: "closed"}})
And it automatically puts those values in the right place.
There was a problem hiding this comment.
Great idea — captured it in d7afe85. It's the write-dual of typed consumption ([[C-0002-typed-consumption]]): the contract that types doc.frontmatter.status for reading types the values argument for writing.
- C-0011 — new "Typed builder — the write-dual of
read" section with your sketch:template.create(contract)({ frontmatter: { status: "closed" } }), where the input type is derived from the contract andcreate({})degenerates to the blank skeleton. - D-0017 — decision step 5: the input type comes from
z.input<>/ the declarative fields (frontmatter) and the grammar + leaves (body — atablereuses the typed-row shapecore/model.tsalready derives), the factory places values, and — the nice payoff — a bad enum member or missing required field becomes a compile-time type error instead of a post-hoc "fill me in" finding.
One contract, typed both ways, so a builder can't place a value the validator would reject. Both docs still validate clean.
Generated by Claude Code
Fold in the reviewer's idea: beyond emitting a blank skeleton string, the scaffolder can expose a typed factory template.create(contract)(values) whose values argument is typed by the contract — the write-side dual of typed consumption (C-0002). - C-0011: new "Typed builder — the write-dual of read" section, plus a two-shapes library entry-point bullet. - D-0017: decision step 5 (input-type derivation, placement, stronger compile-time round-trip, scope), an Options-considered bullet, and C-0002 added to related. Both still validate clean against contracts/decision + contracts/capability.
…ding Add spec-level example code for the C-0011 / D-0017 scaffolding surface under tests/proposed/, following the harness convention (stubs throw until the plane lands; types type-check now, behavioural tests stay skipped): - document-scaffolding.proposed.ts — proposed types/interfaces (ScaffoldOptions, ScaffoldInput<C>, TypedBuilder<C>, scaffold(), template.create()) with throwing runtime stubs. - document-scaffolding.todo.test.ts — example usage against a real contract; the type layer runs under tsc (an enum-narrowed status field, a @ts-expect-error on an out-of-enum value), the behavioural suite is describe.skip behind IMPLEMENTED. - README.md — what tests/proposed/ is and why it sits under tests/. Lives under tests/ so it is type-checked (tsconfig includes tests) but never built into dist (build is src-only) nor counted in src coverage. C-0011 now points at it. Verified: typecheck 0, lint 0, full suite 683 passed / 5 skipped, 0 proposed files in dist, both planning docs still validate clean.
Deploying markdown-contract-docs with
|
| Latest commit: |
f3b8e14
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://82138dd5.markdown-contract-docs.pages.dev |
| Branch Preview URL: | https://claude-planning-document-sca.markdown-contract-docs.pages.dev |
| and writes a document that satisfies it. The same declarative shape drives both | ||
| directions, so a scaffolded document and the contract that gates it can never | ||
| describe different things. | ||
| - Scaffolding emits **shape, never prose.** The generator produces the document's |
There was a problem hiding this comment.
if never emits prose, but given that contracts cn test prose content - doesn't this make it impossible to satisfy the poor statement that new rads a contract and writes a document that satisfies it?
Summary
Two draft planning docs exploring contract-driven document generation — turning a contract into an empty-but-valid starting markdown file. This is the architecture/product exploration behind the docs-site reframing in the companion PR (#200); it can land on its own timeline.
It mirrors the existing config-scaffolding pairing (C-0008 capability / D-0009 decision) with the transform reversed:
init)new)docs/planning/capabilities/C-0011-document-scaffolding.md— product perspectiveA
markdown-contract new <contract>verb that emits a conforming starting document: required frontmatter keys with starter values, required sections in order, and leaf stubs (table header row, checkbox list, fenced code). Framed as the create door beside validate/read, and the primitive a template engine or a CMS "new document" button would build on — without markdown-contract becoming either.docs/planning/decisions/D-0017-contract-to-document-generation.md— engineering gap analysisWhat it would take to meet the gaps:
z.toJSONSchema()), and.refine()/.transform()are non-invertible.patternregexes and typed table/list cells — with a proposed optionalexample:/placeholder:annotation to close it, plus a--fillpolicy fallback.validate(new(contract))→ zero structural findings), mirroringinit's accept-by-construction self-check.Both are
status: open/proposed,need_human_review: true, and each ends with an open-questions section. Drafts for review — not a commitment to build thenewverb.Testing
packages/coreand ran the dogfood validator overdocs/planning: No findings.contracts/decision.contract.yaml/contracts/capability.contract.yamldirectly: 0 findings each (required sections, frontmatter patterns, and the References-cites-a-wikilink rule all pass).