web: std/html markup (WEB slice 1) #93
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Html<Message>,Markup<Message>, the element table, escaping,document,fragment,Html.trusted: the builder side of the packagestd/html(WEB.md decision D1: markup is written with the builder, and only with it). The package already holds the document side (tokenizer, #79 step 1, merged); this issue adds the writing side next to it. Blocks #94 and the live UI (#103).Source
Scope (one PR, two commits)
Files:
std/html/src/(newmarkup.trb,html.trb,escape.trb,link.trb, generatedelements.trb;lib.trbexports),std/html/tests/(newmarkup.test.trb,escape.test.trb,page.test.trb),tools/html/elements.txt(the checked-in source table) andtools/html/elements.trb(the generator, next togenerate.trb),benchmarks/html-render.trbwith its C twinbenchmarks/c/html-render.c,docs/standard-library/html.md,docs/design/WEB.md(status), regeneratedskills/.Not in this issue (later slices): events and handler tables (
onClick:,onInput:,debounce:,part; slice 6),Html.sanitized(needs #79 steps 2 to 4),std/web, assets (Resourcefingerprints), theHtmlinterpolated literal (declined, D2).Acceptance criteria
Html<Message = Never>is a value holding the rendered pieces andMarkup<Message>is the builder receiver;Messageis a phantom parameter in this slice (no handler is stored, so noList<Never>is instantiated).Html.render(): Stringjoins the pieces once; a page with no events isHtml<Never>.var fns ofMarkup<Message>in the three shapes of WEB.md 4.2:td "text",td class: "amount" { ... },tr { ... }; children are the trailing block(var self: Markup<Message>) => Void = {}; void elements (br,img,input, ...) take no block and render without a closing tag. Global attributes are labelled parameters with defaults (id,class,title,lang,hidden,attributes:);typeis writtenkind:andforis writtentarget:.elements.trbis generated bytools/html/elements.trbfromtools/html/elements.txt(the elements and attributes of the WHATWG HTML standard's element index, about 110 elements, one element per line with its own attributes and their types) and checked in; the generator is a pure function of its input (same bytes everywhere,\nline ends), liketools/html/generate.trb, and its output is committed unmodified.&,<,>; attribute values escape&,<,>,",'and are always double-quoted; attribute names come only from the generated table or fromattributes:, whose names are checked against the HTML attribute-name grammar (a bad name is refused, never rendered, never a panic:attributes:takes a map of names to values as in WEB.md 4.2, and a bad name is apanicnaming it, like an out-of-range index). There is no function fromStringtoHtmlthat neither escapes nor sanitizes.fragment { ... }answers the children'sHtml;document title: "T" lang: "en" { ... }answers<!doctype html>plushtml,head(charset meta, viewport meta,title) andbody.Html.trusted(file: EmbeddedText)is the only way to insert raw markup;script(source: Resource)andstyle(sheet: EmbeddedText)are the only<script>/<style>elements;<script>/<style>taking aStringdoes not exist (compile error, checked by absence in the table and a test comment).href,src,action,formaction,poster,cite) take aLink: a trait instd/htmlwithfn target(): String, implemented forUriandUriReference.target()answers the text for relative references and the schemeshttp,https,mailto, andabout:invalidfor any other scheme (javascript:,data:, ...), which the browser does not navigate. AStringis not aLink. (std/webslice 2 implementsLinkforRoute; the fingerprinted-assetLinkcomes with slice 5.)ordersPagewithorderRowasextend<Message> Markup<Message>,document,if,for, thea href:link) is a test instd/html/tests/page.test.trbthat compares the rendered text exactly, including an order whose customer isAda <admin>(renderedAda <admin>).fragment, aLinkof each accepted scheme and of a refused one, and the name-grammar refusal.benchmarks/html-render.trbbuilds N orders pages with the builder and the same pages by plain string interpolation in one program, with a C twinbenchmarks/c/html-render.c(same output bytes, same checksum line), listed inbenchmarks/README.md, following the other programs' layout. It is a measurement, not a gate (benchmarks/README.md); the PR description states how to run it (sh benchmarks/run.sh html-render), and the measured ratio goes intodocs/PERFORMANCE.mdonce it has been run; whether F12 (a text buffer that appends in place) is needed is decided from that number in a follow-up, not guessed here.docs/standard-library/html.mdgets the builder side (the example page, escaping table,Link); the package doc comment inlib.trbnames both sides;WEB.mdslice 1 is marked done with the issue number;torb docs skill docs skillsis regenerated and committed.sh tools/gates.sh agreen in the PR's forge CI,torb format --checkclean on every new.trb,tools/check-commits.shclean.Commits
feat(std/html): add the markup builder with escaping by construction(package code, generator, table, tests incl. the example page)docs(std/html): document the builder and add the render benchmark(docs page, WEB.md status, PERFORMANCE/benchmarks, skills)Dependencies
Message; probe 5 of WEB.md built the same shape natively. If the implementer's CI shows aslot ... has the type Nevererror fromHtml<Never>, stop, record it on this issue and setwaitingon #102 instead of inventing aNothingtype.std/html/src/lib.trbanddocs/standard-library/html.mdwith #79 (document side); the tokenizer step is already merged, later steps of #79 add their own lines.std/uri(Uri,UriReference) andstd/resource(Resource,EmbeddedText), both on main.Moved from the planning lists of the docs; the design record stays the specification. Refined by the loop, see the decision comment.
std/webcore (WEB slice 2) #94[loop:torbscript] intake: in scope (author orion, label loop), source WEB.md slice 1, not a duplicate, no collision with a design record. TorbScript package with an example page as a test and a benchmark measured in forge CI; no seed impact expected. Blocks #94 and the live UI (#103). next: refine, waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z
std/webcore (WEB slice 2) #94std/html(step 1 of #79) #439std/htmldocuments (epic) #79std/htmldocuments (epic) #79[loop:torbscript] claim refine by session:loop-torbscript-20261024 until 2026-10-24T02:15:21Z
[loop:torbscript] decided by the loop: #93 is one PR (two commits), not an epic. It is mostly a generated element table plus a small core (
Html,Markup, escaping,Link), and the design record specifies every piece. Checked against current main (bc504c2):std/htmlexists with the tokenizer (#79 step 1),std/uriandstd/resource(Resource,EmbeddedText) exist,std/webandstd/livedo not, F5 (#104) and F6 (#95) are closed. The issue body now holds the full scope, acceptance criteria and the two commit messages.Decisions and why:
List<Never>inside the stand-in; in slice 1 nothing stores aMessage(events and handler tables are slice 6), soMessageis a phantom parameter,Html<Never>holds only strings, and probe 5 already built this shape natively. No unused-parameter diagnostic exists in the checker. Fallback if CI disagrees: wait on #102, never aNothingtype (that would be a breaking rename later).Linkis a trait, not a type: the language's coercions turn a value into a trait type where one is expected (CONCEPT, "five coercions"), which is howa href: Route.CreateOrderin the example type-checks withRoutefromstd/weblater. WEB.md 4.7 says aUriwith a foreign scheme "is refused where theUriis made"; main'sUridoes not do that (any scheme parses), and changingUriis astd/uricontract change outside this slice. So the check sits inLink.target()ofUriandUriReference: relative,http,https,mailtopass, everything else answersabout:invalid. AStringis not aLink.entities.json, so the table is a checked-in text filetools/html/elements.txt(from the standard's element index) and a generatortools/html/elements.trbwritesstd/html/src/elements.trbfrom it, astools/html/generate.trbdoes for the entities. Slice 6 adds the event parameters by rerunning the generator, not by hand edits.typeandforbecomekind:andtarget:(reserved words, WEB.md 4.2);document,fragmentstay free functions as in the record's example (the decided API), everything else is a method (Html.render(),Html.trusted).attributes:with a bad name panics, naming it: the names are author-written literals, so this is a programmer error like an index out of range, not a runtime input path.benchmarks/is a measurement, not a gate (its README), and forge CI does not runrun.sh. The program and its C twin go in; the ratio is recorded indocs/PERFORMANCE.mdonce measured, and F12 (in-place text buffer) is decided from that number in a follow-up. Nothing is guessed in this issue.std/, tooling, docs), no compiler change.[loop:torbscript] refine: ready, claim released, next: implement. Scope, acceptance criteria, files, two commit messages and the decisions are in the issue body and the decision comment above; no owner question (all technical), no seed touch, no open dependency (#102 only as a documented fallback). Worker usage: about 110k tokens (sonnet 5.5), no sub-spawns.
[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T02:25:29Z
[loop:torbscript] implement: slice 1 is built except two items that wait on #135. PR: #457 (branch
loop/93-html-builder, "Part of #93", not "Closes"). CI is running; a second commit with docs and the benchmark follows. next: review (the close stage must not auto-close #93: it stays open forHtml.trustedandstyleuntil #135 lands)Done in the PR:
Html,Markup, 110 generated elements (tools/html/elements.trbfromelements.txt), escaping per 4.7,fragment,document,script(source: Resource), theLinktrait forUriandUriReference, tests per 4.7 row, the 4.2 example page as an exact-output test.Findings:
Html.trusted(file: EmbeddedText)andstyle(sheet: EmbeddedText)needEmbeddedText.text(), which is RESOURCES.md slice 2 (#135). Deferred, waiting on #135.The generic parameter Message was not substituted; also forconst x: Html<Never> = fragment {...}).fragmentanddocumentare non-generic overNeveruntil that is fixed; components stayextend<Message> Markup<Message>. This is not theslot ... has the type Nevererror of #102.script(source:)writes the file's stable name until fingerprints (slice 5, #100).attributes:also refuseson*and URL-ish names (they have typed parameters or are not allowed at all).[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T01:58:14Z (finish the docs and benchmark commit on PR #457 after the first worker timed out)
[loop:torbscript] implement: second commit pushed on PR #457 (
194f5748docs(std/html): document the builder and add the render benchmark):docs/standard-library/html.md(builder side, example page, escaping table, Link), WEB.md slice 1 status,benchmarks/html-render.trb+ C twin + README row, regeneratedskills/, doc comments instd/html/src. Local checks:torb format --checkclean on the touched small files,torb docs skill docs skillsstable,tools/check-commits.shclean. Forge CI for the new head is running, next: review. #93 stays open until #135 lands (Html.trustedandstyleare not in this PR). Note: the benchmark compares the builder to the C twin; the PERFORMANCE.md ratio follows oncesh benchmarks/run.sh html-renderhas been run (a measurement, not a gate).[loop:torbscript] claim fix by session:loop-torbscript-20261024 until 2026-10-24T04:04:24Z (red CI on the PR)
[loop:torbscript] fix: the pdisc check failed because
benchmarks/README.mdgot itshtml-renderrow in194f5748without a restamp (stale body_hash in.pdisc-sidecar.yaml). Ranpdisc.py stamp benchmarks/README.md(check prints ok locally), commit1a9dd0a9docs(benchmarks): restamp the README after the html-render row. Forge jobpdisc / pdiscis green on1a9dd0a9; the other jobs (ci/gates etc.) are still queued on the runner. next: review[loop:torbscript] claim fix by session:loop-torbscript-20261024 until 2026-10-24T05:44:18Z
[loop:torbscript] fix (checkpoint): causes from the CI log of
1a9dd0a9: (1) check . and docs check: orderRow was defined on Markup in benchmarks/html-render.trb, std/html/tests/page.test and docs/standard-library/html.md (extend members are repo-wide); (2) docs: html.md heading not in sentence case and declaration markers without fragment blocks; (3) format --check: std/html/src/elements.trb would change. Done in2740b09b(pushed): renamed to benchOrderRow and customerRow, declaration blocks filled by torb docs declarations (Markup marker dropped, its extend table would add 110 long lines), skills/ regenerated. Remaining: format std/html/src/elements.trb (nightly torb format needs more than 15 min on the 2572-line file: find the change on a slice or make the generator tools/html/elements.trb emit formatted output, then regenerate), PERFORMANCE.md ratio for html-render. Worktree code/wt/torbscript-language-93 kept. next: CONTINUE with a fresh fix worker, then waiting (forge CI of the next sha)[loop:torbscript] fix: format --check root cause found and fixed (std/html/src/elements.trb needed a trailing comma after the last parameter and
globalAttributes id, class, ...as a command call; generator tools/html/elements.trb emits both now, verified with the nightly torb on slices of the file, 0 would change; commit is1fc09f6con the rebased branch), next: waiting (forge CI of1be63168). Open: PERFORMANCE.md html-render ratio has no source yet - benchmarks.yml runs only nightly/dispatch against release-tag programs and this box has no C compiler, so no number exists; it stays out until a bench run on main measures html-render. The previous run ofa3eb3c29was cancelled by the rebase push.[loop:torbscript] merge: PR 457 merged (Argus). The issue stays open for the html-render ratio in PERFORMANCE.md, which needs a native benchmark number, next: waiting (#135 resources embedding, then a native benchmark run)
[loop:torbscript] claim implement by session:loop-torbscript-20261026 until 2026-10-26T05:00:00Z (#135 closed: Html.trusted, style and the html-render benchmark ratio remain)
[loop:torbscript] implement (checkpoint): branch loop/93-trusted-style pushed (commit feat(std/html): add Html.trusted and style over EmbeddedText.text()). Finding: #135 landed the compiler side (checker reads the files, lowering generates the embeddedText table) but std/resource declared no
embeddedTextnative and noEmbeddedText.text(); added both. Html.trusted (static fn, answers Html) and style(sheet: EmbeddedText) added with tests and fixtures. Not done yet: PR not opened, CI not run, docs/standard-library/html.md + resource.md, WEB.md slice-1 status line, skills regen, html.md; PERFORMANCE.md ratio stays open (needs a bench run on main, no C compiler here, benchmarks.yml is nightly/dispatch only). Worktree code/wt/torbscript-language-93-trusted kept. Tokens so far ~90k. next: CONTINUE with a fresh implement worker[loop:torbscript] claim implement (continuation 1) by session:loop-torbscript-20261026 until 2026-10-26T06:00:00Z
[loop:torbscript] implement (checkpoint): PR #486 opened (branch
loop/93-trusted-style, "Part of #93", Argus requested). Commits: Html.trusted + style + EmbeddedText.text(); docs (html.md, resource.md, WEB.md slice 1 status, regenerated skills/, docs index); fix for the first red CI (real errors:Html<Never>(written)intrusted, the test must writeHtml<Never>.trusted("...")because Message cannot be inferred from a static call, and a literal{in the style test string needs\{). Fix pushed;torb check std/htmlclean, format --check clean on touched .trb, pdisc check ok, check-commits ok. CI of the fix sha: tier A, bootstrap, every-host-emits-same-C, pdisc green;gates(with archives/pack the seed) still pending;the playgroundfailed withNo such image: emscripten/emsdk:6.0.10on the runner (infra, not the change; rerun if it persists). Not closing #93: the html-render ratio in docs/PERFORMANCE.md needs a native benchmark run (sh benchmarks/run.sh html-render) on main with a C compiler; the loop cannot trigger it. That is the only acceptance item left. Worktree code/wt/torbscript-language-93-trusted kept. Tokens this worker ~95k. next: CONTINUE with a fresh worker to read ci-wait for the new sha (fixgatesif red), then review[loop:torbscript] claim implement (continuation 2) by session:loop-torbscript-20261026 until 2026-10-26T06:00:00Z
[loop:torbscript] release implement (continuation 2): the worker timed out at its limit without new commits; branch
loop/93-trusted-stylestays at1d9d2767. PR 486 CI on that sha: tier A, bootstrap, every host emits the same C, the playground (rerun, now green), commit messages, pdisc green;gates,archives,pack the seedstill pending in the runner queue. Argus review requested. Next: waiting (CI on PR 486, then fix findings if red; Argus review)[loop:torbscript] close: PR 486 was merged by the merge gate as
1d9d27672dand the push CI of that commit on main is green (gates, tier A/B, bootstrap, same C, playground, commit messages, pdisc). Closing the issue, next: done.std/webpackage:WebError,Handlerand the request and response helpers (step 1 of #94) #575std/webcore (WEB slice 2) #94HtmlNode,HtmlDocument.parse(step 2a of #79) #688std/htmldocuments (epic) #79