Skip to content

feat(o4b): TS contract foundation — the four primitives cross to TypeScript#9

Merged
joelteply merged 1 commit into
mainfrom
feat/o4b-1-ts-contract-foundation
Jul 3, 2026
Merged

feat(o4b): TS contract foundation — the four primitives cross to TypeScript#9
joelteply merged 1 commit into
mainfrom
feat/o4b-1-ts-contract-foundation

Conversation

@joelteply

Copy link
Copy Markdown
Contributor

O4b-1 — the positron contract crosses to TypeScript

First slice of the positron-lit build-out (roadmap O4b). Until now @positron/core shipped only the generated wire data (StateEnvelope, CommandEnvelope, session frames) and the repo had a Rust-only CI. This lands the missing half.

What

  • npm/core/src/contract.ts — the four positron primitives (ViewState / Renderer / Host / Observer) as hand-authored TS interfaces, the TS twin of the Rust traits in positron-core/src/lib.rs.
    • Hand-authored on purpose: ts-rs projects data (structs/enums with a wire form); a trait has no serialized form to generate. The Rust trait and the TS interface are the same contract in two languages — the generated wire types are the data those contracts move.
    • Rust snake_case → TS camelCase (observer_idobserverId); Rust associated types (Renderer::Output, Host::Command/Event) → TS generics.
  • Toolchain (first TS build in a Rust-only repo): root package.json npm workspaces over npm/*; tsconfig.base.json strict shared options (noEmit — packages ship .ts source); per-package tsconfig sets only include.
  • CI web job: npm installtsc --noEmitnode --test. contract.ts has no Rust drift gate, so this job is its gate.
  • Lockfile ignored (matching Cargo.lock) → npm install, not npm ci; deps are dev-only (typescript/tsx/@types/node).

Proof

contract.test.ts mirrors positron-core's own Rust smoke tests: same Counter fixture, same kind/revision/deterministic-render assertions, plus an Observer perceive check.

Validated locally: npm run typecheck clean, npm test 4/4 pass.

Why a slice (not all of O4b)

O4b-2 (next) adds @positron/lit — the actual Lit DOM Renderer (Output = TemplateResult) + LitHost (against the ClientMessage/ServerMessage session frames) + a Counter example with a headless template assertion. This slice is the foundation it builds on: the contract must exist in TS, typecheck, and be CI-gated first.

Task #117 (positron O1..O6 build-out).

🤖 Generated with Claude Code

…Script

O4b-1 of the positron-lit build-out. Until now @positron/core shipped only
the *generated wire data* (StateEnvelope, CommandEnvelope, session frames)
and the repo had a Rust-only CI. This lands the missing half: the four
positron primitives — ViewState / Renderer / Host / Observer — as
hand-authored TS interfaces (npm/core/src/contract.ts), the TS twin of the
Rust traits in positron-core/src/lib.rs.

They are hand-authored on purpose: ts-rs projects *data* (structs/enums with
a wire form); a trait has no serialized form to generate. The Rust trait and
the TS interface are the same contract in two languages — the generated wire
types are the data those contracts move.

Toolchain (first TS build in a Rust-only repo):
- root package.json = npm workspaces over npm/* + shared devDeps
  (typescript/tsx/@types/node)
- tsconfig.base.json = strict shared options; per-package tsconfig sets only
  `include`. noEmit — these packages ship .ts source, they're typechecked
  not built.
- CI gains a `web` job: npm install → `tsc --noEmit` → `node --test` smoke
  tests. contract.ts has no Rust drift gate, so this job IS its gate.
- lockfile ignored (matching Cargo.lock), so `npm install` not `npm ci`.

Proof mirrors positron-core's own smoke tests: the same Counter fixture, the
same kind/revision/deterministic-render assertions, plus an Observer
perceive check — the TS side proven usable the way the Rust side is.

Validated: `npm run typecheck` clean, `npm test` 4/4 pass.

Task #117 (positron O1..O6 build-out).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
@joelteply joelteply merged commit bf347c7 into main Jul 3, 2026
3 checks passed
@joelteply joelteply deleted the feat/o4b-1-ts-contract-foundation branch July 3, 2026 03:56
joelteply added a commit that referenced this pull request Jul 3, 2026
…Result> + LitHost) (#10)

* feat(o4b): @positron/lit — the Lit DOM renderer (Renderer<S, TemplateResult> + LitHost)

O4b-2 — the DOM/TS outlier of the positron Renderer/Host contract, the
sibling of the wgpu backend on the web surface. Completes O4b (the
contract's TypeScript crossing, foundation landed in O4b-1 / PR #9).

New package `@positron/lit`:
- `renderer.ts` — `LitRenderer<S> = Renderer<S, TemplateResult>`. Not a
  fresh interface: the render contract lives once in @positron/core;
  this only pins the DOM output type (compression rule).
- `host.ts` — `LitHost` implements core's `Host`: state flows down
  (render → injected commit sink), events flow up (injected
  event→command mapper). DOM-free by construction — imports only the
  `TemplateResult` type, never a DOM value — so it's headlessly testable.
- `dom.ts` — `domCommit(container)`, the ONE DOM-touching seam (Lit's
  `render` into a container). The analogue of the wgpu outlier's
  `gpu.rs`: quarantined from the pure projection, off the headless test
  path. Same pure/impure split as both Rust outliers.
- `lit.test.ts` — Counter + CounterLitRenderer fixture (mirrors core's
  contract.test.ts). Asserts the projection headlessly on the
  TemplateResult's `strings`/`values` — the DOM analogue of wgpu's quad
  assertions — plus LitHost state-down/event-up. 4/4 pass, no jsdom.

Toolchain: adds `npm/lit/` to the workspaces; deps `lit ^3.2.0` +
`@positron/core` (type-only — lit imports only interfaces from core, all
erased, so no runtime coupling). The existing `web` CI job gates it.

Docs: ARCHITECTURE.md marks O4b landed and records the deferred
subsume-vs-complement decision — positron's portable contract
(ViewState/Renderer/Host/Observer + session frames) and continuum's
`sdk/typescript` (its Commands/Events/domain types) COMPLEMENT; they meet
at ContinuumHost (O5), neither subsumes the other. README Status +
diagram updated.

Task #117 (positron O1..O6 build-out).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* docs(o4b): breadcrumb the Lit-4 narrowing point in lit.test.ts

Review non-blocking nit: in Lit 4, `TemplateResult` becomes possibly-
compiled and drops `.strings`, so the static-structure assertion is the
exact line that will need an `UncompiledTemplateResult` narrowing on that
bump. Note it in place so future-you doesn't have to re-derive it. Comment
only — no behavior change (8/8 still green, typecheck clean).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant