Whitepaper · language · from code

Chrysalis Web Language

Tip 1.0.26 (2026-08-21). Package @chrysalis/cwl · Apache-2.0 · AgenticOp-io/chrysalis-cwl. This paper follows the pillar tree: hand-written parser, WebIR twin, language golds 01–35, dna-seed, diagnose codes, and smoke:ut-spine.

Docs · Product · WebIR · Source: engines/chrysalis-cwl · Cite: 10.5281/zenodo.22691492

Thesis

A web application’s identity is the set of routes, pages, data loaders, UI islands, and effects that determine how requests are answered. Frameworks encode that identity in many dialects. CWL is one consolidated inscription — the DNA of the web — so humans can audit it and machines can emit from the same model.

CWL maps 1:1 to WebIR (@chrysalis/webir 2.0.2). There is no second private IR for one migration sprint. Convert peels and emits through that pair; Secure may bridge surface to traffic DNA. Neither owns the grammar.

If meaning cannot be recovered honestly, emit hole reason;. Guessing is a forged gene. Silent stubs are cancer.

Packages (pillar tree)

PackageVersionRole
@chrysalis/cwl1.0.26Language pin + CLI. Exports parser, print, diagnose, lsp-map, dna-seed
@chrysalis/webir2.0.2Semantic IR: Module, Effect, dialects web-request / effect / data
@chrysalis/runtime-cwl2.0.2In-process HTTP simulate — createCwlRuntime, compileCwlRoutes, startCwlServer
runtime-cwl-browser2.0.2RFC-0019 island contract markers (data-cwl-island) — no hydration claim
runtime-cwl-worker2.0.2Worker wrap of runtime fetch
emit-runtime-cwl2.0.2WebIR → deployable Node project (routes.cwl, webir.json, Dockerfile)

DNA seed is not a separate npm package — it is @chrysalis/cwl/dna-seed (source staged from scripts/hub-ingest/cwl-dna-seed.mjs). Public npm is not the default path; GitHub Packages / file: pins. Convert junctions packages/cwl and packages/webir into this tree.

Pipeline

.author.cwl
  → parseCwlModule / printCwlModule / diagnoseCwlSource
  → (optional) cwl-ingest → WebIR Module
  → runtime-cwl simulate  |  emit-runtime-cwl  |  thin emit reverse
  → dna-seed → draft app-dna-v1 (+ bridge envelope)
  → Secure Helix: strip → promote/sign → compare ⊆ DNA → enforce

Canonical sources live under scripts/hub-ingest/ and sync into packages/cwl/lib/ via sync:cwl-package-lib. Editor grammar (TextMate only): editors/vscode/syntaxes/cwl.tmLanguage.json — gate test:cwl-grammarCWL_GRAMMAR_OK. There is no PEG/Ohm grammar; the parser is hand-written JavaScript.

Parser and AST

Primary files: cwl-parser.mjs, cwl-print.mjs, cwl-diagnose.mjs (schemaVersion 6), cwl-fmt.mjs, cwl-ingest.mjs, cwl-control-lower.mjs.

parseCwlModule returns approximately:

{
  moduleName, moduleLine, file,
  routes[], moduleUses[], moduleAuthUses[],
  imports[], importLines[], components[]
}

Each route carries method, path, pathParams, name, surfaceKind (api|page), effects, handler param arrays (path/query/body/multipart), responseStatus, streamKind, loadBody, earlyGuards, foreachBindings, attachmentHoles, and body. Body kinds: object | literal | html | ui | hole.

RFC-0021 projectable conditions lower to WebIR; opaque g_* residuals are skipped on ingest (no invented verify) and diagnosed as opaque-residual (info).

if_guard   ::= "if" cond_expr "{" (status | return)* "}"
cond_expr  ::= or_expr | opaque_ident
or_expr    ::= and_expr ("||" and_expr)*
and_expr   ::= unary_expr ("&&" unary_expr)*
unary_expr ::= "!" IDENT | cmp_expr
cmp_expr   ::= IDENT ("==" | "!=") literal
opaque_ident ::= IDENT   (* g_* residuals *)
foreach_bind ::= "foreach" IDENT "as" [IDENT "=>"] IDENT "{" (return)* "}"

Named surfaces

SurfaceSyntaxRFCsStatus
API@route + handler0001–0008Shipped · golds 01–08
Pages@page + return html0010, 0011, 0014Shipped
Dataload { … }0013Deepening · gold 27 runtime-ok
UIreturn ui, @component, islands0017–0019, 0024, 0028Language shipped; hydration non-goal
Effectseffects:, use auth|json|urlencoded0007, 0001, 0020Presets + middleware golds 22/30
Controlif / foreach0021Accepted · golds 19, 23
Modulesmodule, import0009Shipped · gold 12
DNA bridgesurface ↔ app-dna-v10022, 0023Contract · golds 24, 34
Transportmultipart · SSE0026, 0027Accepted · golds 31, 32

Not CWL surfaces: Chimera (migration shell), emit backends (targets), databases/queues/SDKs, or Helix traffic DNA itself.

Minimal syntax

module api;
use json;
use urlencoded;

@route GET "/items/:id"
handler item_show {
  effects: none;
  param id;
  return { ok: true, id: id };
}

@page GET "/"
page home {
  effects: none;
  return html "<h1>Welcome</h1>";
}

@route POST "/legacy"
handler legacy_post {
  effects: io, db;
  hole legacy:invoice_create "delegate to PHP stack";
}

Language golds 01–35

Under fixtures/language-gold/. Gate: npm run test:language.

IdProves
01–08Literals, path/query/header/cookie/body, status, auth effects, content-type
09–16Pages, load, holes, multi-file, middleware, headers, HTML interp, layout
17–19UI v0/v1, early-exit / foreach
20–23Probes, form-action hole, effects middleware, nested control
24 / 34DNA bridge + deploy profiles; surfaces deepen (SSE/multipart/HEAD)
25–28Island kinds, nested literals, load redirect/error/cookie, Set-Cookie header
29–33Transport holes, executable effects, multipart, SSE, named island contracts
35Urlencoded form POST + body (tip 1.0.26 · runtime-ok)

RFC index (accepted)

Each RFC cites cross-language evidence and must lower to WebIR without a second IR. Canonical list: docs/language/CWL-RFC.md.

RFCTitle
0001Module use json / use urlencoded
0002Path parameters (:id templates)
0003Query parameters
0004Headers and cookies
0005JSON request body fields
0006Response status
0007Auth presets and effects
0008Response content-type
0009Multi-file modules (import)
0010Full-stack page surface
0011Layout imports + page params
0012Full-stack component holes (SvelteKit)
0013Page load / SSR data (v2: redirect/error/cookie)
0014HTML interpolation in @page
0015Production readiness probes
0016Form action probe + hole catalog
0017–0019Native UI v0/v1, components, client islands
0020Effects middleware chains
0021Early-exit cond expressions + foreach
0022CWL ↔ app-dna-v1 bridge (contract only)
0023Deploy / DNA profiles (cwl-deploy-profile-v1)
0024Island kinds (Wasm / vendor / opaque)
0025Nested structured object/array literals
0026Multipart field/file part bindings
0027SSE single-shot stream sse;
0028Named client islands + form event contracts

DNA seed (RFC-0022 / 0023)

APIs: cwlSurfaceToDraftDna, seedDraftDnaFromCwlPath, dnaBridgeContractEqual, pathTemplateShapeEqual, responseKeyFingerprint, loadDeployProfile. Gate: test:cwl-dna-bridgeCWL_DNA_BRIDGE_OK.

Identity key: `${host} ${METHOD} ${path_template}`. Per-route seed fields:

FieldRule
hostProfile / opts else "default"; unknown key in hosts{} throws
methodUppercased
path_templateAuthored path (named params kept)
content_classhtml if html/ui/page; json if object; other if SSE/opaque
status_classesIf status set → [floor(N/100)*100] else []
response_key_fingerprintJSON only — sorted key paths depth ≤ 2; else null
request_key_fingerprintSorted body + multipart field/file names
query_key_fingerprintSorted query binding names

Bridge envelope only (stripped before certified DNA): cwl_surface, cwl_effects, optional cwl_stream / multipart annotations. CWL holes are not copied into DNA holes[]. Path shape: :id:userId via pathTemplateShapeEqual. Deploy profile schema: cwl-deploy-profile-v1 (external artifact).

Diagnose codes

CodeSevMeaning
parseerrorParser threw
module-namewarnMissing module
duplicate-routewarnSame method+path twice
uncatalogued-holewarnHole not in catalog
catalogued-holeinfoHonest catalogued hole
surface-mismatchwarn@page vs @route body kind mismatch
param-unusedwarnDeclared param unused in HTML
opaque-residualinfoAuthored g_* skipped on ingest
no-routesinfoEmpty module

ok = no errors; warns do not fail check. Catalog includes origin peels (hub-svelte:*, hub-next:*, …) and kept tip hole unsupported:websocket.

Tip 1.0.26

Genome deepen (2026-08-21): ingest preserves authored HTML shell beside load { redirect|error } (gold 27); gold 35-form-urlencoded for use urlencoded; + body bindings. Prior: emit reverse 1.0.25 · DNA bridge 1.0.24 · UI islands 1.0.23. WebSocket duplex remains unsupported:websocket.

CLI and gates

cwl parse | print | fmt | diagnose | check | emit-check | dna-seed

CommandToken
test:languageFull language chain
test:cwl-dna-bridgeCWL_DNA_BRIDGE_OK
test:cwl-hole-catalogCWL_HOLE_CATALOG_OK
test:cwl-grammarCWL_GRAMMAR_OK
smoke:ut-spineUT_SPINE_OK
smoke:ut-evidenceUT_EVIDENCE_OK
npm run build:webir
CWL_REQUIRE_WEBIR=1 npm run test:language
npm run smoke:cwl-ingest-matrix
npm run smoke:cwl-runtime-matrix
npm run smoke:cwl-emit

UT ↔ Helix spine (CWL owns the gate)

scripts/smoke-ut-spine.mjs (G10125). Convert does not own this spine. Invariant: CWL owns surface contract; Helix disposes DNA; Convert does not own this spine.

  1. Assert gold 24-dna-bridge/routes.cwl
  2. Validate optional deploy-profile.json
  3. Always run gate-cwl-dna-bridge.mjs
  4. If sibling Secure present: seed → strip → promote+sign → compare ⊆ DNA → score allow/deny
  5. Write reports/ut-spine/ut-spine.json; print UT_SPINE_OK
CWL gold (24-dna-bridge)
  → RFC-0022 contract gate
  → Helix seed → strip → promote(+HMAC)
  → compare: CWL surface ⊆ certified DNA
  → enforce allow/deny

More: Traffic decides · Helix · WebIR.

Non-goals

  • Origin PL dialects as CWL grammar (Convert peels only).
  • Nest / LiveView / Flutter / middleware-onion façades.
  • Inventing mail/SQL/rate/EventSource/upload/WS runtimes.
  • UI island hydration / client JS execution in runtime-cwl.
  • Firewall policy (Secure); customer migrations (Convert).
  • WebSocket duplex as a finished gene (kept hole).

Edit language only in chrysalis-cwl; Convert syncs junctions. Next: WebIR · Convert · Helix · System overview