docs(plan): handover for the parser contract#81
Merged
Conversation
Captures the next architectural beat after the renderer split:
- The leverage isn't in splitting transformer.ts per file (it's
already organized as 17 named functions internally). The leverage is
in fixing the contract: explicit TransformContext with
peekNext/consumeNext, plus isolated per-node parse tests that don't
go through the full markdown→AST→render pipeline.
- Land both as one PR. Pure refactor — zero snapshot drift, 17
KNOWN_FAILURES still .fails. No file split, no syntax change, no
bug fix in the same PR.
- Then knock out KNOWN_FAILURES one cluster at a time with the new
tooling. Then attack syntax changes one at a time.
Captures the user's pending syntax direction (((pill)) over |pill|,
:::columns over :::grid, consistent {.classname}, disallow row +
heading-alignment). Two ideas got pushed back during discussion:
free-floating alignment classes (stateful parsing trap — counter:
"applies to nearest element only") and navbar | separators (collides
with tables — counter: alignment classes on items). Both need design
calls before code.
The doc is the in-repo source of truth; the user-memory pointer at
parser-contract-handover.md cross-references it so a fresh agent
loads MEMORY.md, sees the entry, and reads the full plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
teezeit
added a commit
that referenced
this pull request
May 1, 2026
## Summary Pure refactor: replaces the implicit `nextNode` parameter and post-hoc `i++` patterns in `processNodeList` with a first-class `TransformContext` that every transform receives. Cross-sibling peek/consume is now explicit, auditable, and unit-testable in isolation. This is the parser-contract step from `.github/dev-docs/parser-contract-plan.md` (PR #81). It unblocks the bug-fix and syntax-change PRs that follow. ## What changed **New: `packages/core/src/parser/_context.ts`** - `TransformContext` interface — `peekNext()`, `consumeNext()`, `transformChild()`, `transformChildren()`, plus shared helpers (`parseAttributes`, `extractTextContent`, `isHtmlCommentNode`). - `makeContext(siblings, startIndex, options, deps)` — sibling-aware factory used by `processNodeList`. - `makeIsolatedContext(options, deps)` — for transforms that walk a single child without cross-sibling lookahead. **Modified: `packages/core/src/parser/transformer.ts`** - Every transform function migrated from `(node, options [, nextNode])` to `(node, ctx)`. - `processNodeList` builds a `TransformContext` per iteration and advances by reading `handle.getCursor()`. - The post-hoc shape rule (`if (transformed.type === 'select' && nextNode...) i++;`) is gone — consumption now lives next to the peek inside `transformParagraph`, where the read actually happens. - The dropdown lookahead is the only cross-sibling peek in the parser; both single-line and multi-line dropdown branches now call `ctx.consumeNext()` themselves. - New `__transformerInternals` export (test-only) so isolated parse tests can dispatch through `transformNode` directly. **New: `packages/core/tests/lib/transform-test-helpers.ts`** - `mdastFor(markdown)` — runs the same MDAST stage that `parse()` runs, stops before the wiremd transform. - `makeTestContext(siblings, options)` — sibling-aware test context. - `runTransform(mdast, siblings, options)` — fires `transformNode` and returns `{ node, cursor }` so tests can assert on lookahead consumption. **New: `packages/core/tests/parser/dropdown-lookahead.test.ts`** - First per-node parse test. 4 cases pinning the dropdown contract: - Dropdown + following list → consumes the list. - Dropdown alone → no consume. - Dropdown + non-list sibling → no consume. - Non-dropdown paragraph + list → no peek. ## What this PR is NOT - Not a file split. `transformer.ts` stays in one file. Each function gets the new `ctx` parameter; that's it. - Not a bug fix. All 17 `KNOWN_FAILURES` continue to fail. - Not a syntax change. The user's pending syntax direction (`((pill))`, `:::columns`, etc.) lands in separate PRs. ## Test plan - [x] `pnpm typecheck` — clean - [x] `pnpm test` — **1,184 passed** (1,180 baseline + 4 new isolated parse tests) - [x] `pnpm review:refresh` — **"Nothing matched — log unchanged"** (zero snapshot drift) - [x] All 17 `.expected-fail.invariants.ts` tests continue failing as expected 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Self-contained brief for the next architectural beat after the renderer split. No code change — just the planning doc at
.github/dev-docs/parser-contract-plan.md.The renderer monolith is split (#77 #78 #79 #80). The next high-leverage move is not splitting
parser/transformer.tsper file — it's fixing the contract: explicitTransformContextwithpeekNext/consumeNext, plus isolated per-node parse tests that don't go through the full markdown→AST→render pipeline.The doc explains why (the file split is cosmetic; the leverage is the contract), what to build (
_context.ts+ test helpers + per-node tests), and what NOT to do (no file split in this PR, no syntax change, no bug fix). It also captures the user's pending syntax direction (((pill))over|pill|,:::columnsover:::grid, etc.) so the next session has the destination clear before starting work.Two syntax ideas got pushed back during discussion and are flagged for design calls before any code:
{.right}/{.center}/{.left}"applies until resolved" — stateful-parsing trap; counter-proposed "applies to nearest element only."[[ a | b | c ]]|separators — collides with table syntax, new escape rules; counter-proposed alignment classes on items.Test plan
🤖 Generated with Claude Code