Repository navigation
feat(harness): add the projection, locators, checks, trace, and the first demos - #67
Draft
robfeldmann wants to merge 23 commits into
Draft
robfeldmann wants to merge 23 commits into
robfeldmann wants to merge 23 commits into
Conversation
…a draw's bytes ViewGraph.update() cleared the viewport invalidation reasons that resize had recorded, so a redraw after a resize reported none. The graph now remembers the change until the next render. TerminalSession gains a package bytesDrawn: the bytes the last draw wrote, which the harness reads for its cost counters without a lock or an output tap.
…irst demos Projection is the view graph's diagnostics as sorted-key JSON plus the screen, with a version. Five rules v0 read it (no-dirty-after-pass, frame-inside-clip, measured-within-proposal, siblings-disjoint, requirements-aggregate), through the check statement and tessera check. The scenario language gains dump, check, expect cost NAME<=N, and the layout= and id= locators. shot writes NAME.txt and NAME.grid.txt. Every step records a trace line (event, drew, the invalidation reasons, changed nodes and rows, cost): run --trace DIR and TESSERA_TRACE write it, and tessera replay runs it again and compares it. DemoSupport gains demos for Text, stacks, frame and padding, ZStack, Flex, and ForEach, each with a desktop and a mobile scenario; the stack demo is absorbed into stacks. The corpus is 16 scenarios, every shot also with a grid golden.
…demos siblings-disjoint looks through ForEach, if, and view groups to the container that places them, so layers built by a list under a ZStack no longer fail the rule. The grid golden now keeps an empty chars row for each styled blank row, so it never has more style rows than chars rows, and a screen with more than 62 distinct styles fails the shot as a harness error instead of writing a repeated letter. run --pretty prints a bare check's warnings, an empty path, name, or flag value is a usage error, an empty locator value is a parse error, the trace version is read before its schema, and a live trace records the terminal's focus changes in a form replay delivers. The Flex demo gains a second page (a layoutPriority tier, a child with no flex value, cross-axis placement), the Stacks demo a Spacer(minLength:) and a Spacer in a VStack, and the ZStack demo layers from a ForEach and an if. text.md says what the Text demo renders today.
The projection node gains isList, set from the reflected type before it is shortened, so siblings-disjoint looks through the library's ForEach, if, and view groups and not through an app's own view that happens to be named ForEach. The README says a container-less multi-view body is a true positive.
…-4/06-projection # Conflicts: # Examples/Tests/DemoSupportTests/CounterDemoTests.swift # Examples/Tests/DemoSupportTests/ScenarioCorpusTests.swift # Sources/TesseraHarness/Scenarios/Locator.swift # Tests/TesseraHarnessTests/HarnessCLIScenarioTests.swift # Tests/TesseraHarnessTests/ScenarioRunnerTests.swift # Tests/TesseraHarnessTests/ScenarioTests.swift # docs/plans/phase-4-view-layer-and-developer-tooling.md
Express the projection, checks, trace, replay, grid, statement, and demo tests as inline snapshots and expectNoDifference where they check structured state, sort the conformance lists of the new types, conform CostBound to CustomStringConvertible, and replace plan and slice references in comments and the changelog.
A trace file's name mirrors its scenario's path, so the scratch directory's path appears there in a second spelling on Windows; redact that one too.
…-4/06-projection # Conflicts: # .agents/skills/tessera-contributing/SKILL.md # .agents/skills/tessera/SKILL.md # Sources/TesseraHarness/HarnessCLI+Scenarios.swift # Sources/TesseraHarness/HarnessCLI.swift # Sources/TesseraHarness/HelpText.swift # Tests/TesseraHarnessTests/HarnessCLIScenarioTests.swift # Tests/TesseraHarnessTests/HarnessCLITests.swift
…swift-argument-parser check, replay, run --trace, and launch's TESSERA_TRACE move onto the ArgumentParser declarations the other commands use, and the last hand-rolled argument parsing and help text are gone. A usage error exits 2 and says what is accepted where it happened; results, reports, and exit codes are unchanged.
…plete their app names
…-4/06-projection # Conflicts: # .agents/skills/tessera-contributing/SKILL.md # CHANGELOG.md # Sources/TesseraHarness/HarnessCLI+Scenarios.swift # Tests/TesseraHarnessTests/HarnessCLIScenarioTests.swift # docs/architecture/developer-tooling.md
… and replay completion Like --repeat, --trace and --app are declared as arrays so validate() can reject a second one with the message the command line always gave, and the completion tests send the request the shipped scripts send for check and for replay's --app.
…-4/06-projection The merge also makes --trace and --app plain single-value options listed in singleValueOptions, so a repeated one is refused before ArgumentParser parses, as --repeat is.
This branch has not been deployed
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.
Slice 6 of plan 036 (
docs/plans/phase-4-view-layer-and-developer-tooling.md): the projection, theSELlocators, the invariant rules,expect cost, the trace,shotas text plus grid, and the first per-primitive demos. Stacked on #66 (phase-4/05-scenarios), so this PR targets that branch.What lands
Sources/TesseraHarness/Projection): the view graph's diagnostics as sorted-key JSON with aversion, plus the screen as rows and a style grid.dumpprints it.no-dirty-after-pass,frame-inside-clip,measured-within-proposal,siblings-disjoint,requirements-aggregate.check [RULE...]is a statement;tessera check NAME [SIZE]runs them over one app with no input. A violation names the rule and the node.siblings-disjointlooks throughForEach,if, and view groups to the container that places them, so layers a list builds under aZStackare layers.layout=andid=jointype=,text=(a substring of the leaf's own cells), andnth=. An empty value is a parse error.expect cost NAME<=Noverbodies,bytes,measures,placements, andrenders; the bytes come from a newpackageseam,TerminalSession.bytesDrawn(a running total, differenced per step).drew, invalidationreasons, changed nodes and rows,cost).tessera run --trace DIRandTESSERA_TRACEwrite it;tessera replay FILE --app NAMEruns it again and compares everything except bytes. Keys, pastes, resizes, and the terminal's focus changes replay; a mouse event or a terminal reply is refused by name.shotwritesNAME.txtandNAME.grid.txt(the catalog's grid dialect). The grid never has more style rows than chars rows, and a screen with more than 62 distinct styles fails the shot as a harness error.NAME.txtnow reads a wide grapheme as one character.DemoSupport, each with a desktop and a mobile scenario that shots both and asserts a frame (most also runcheck):text(with the archived Unicode samples),stacks(Spacer,Spacer(minLength:), aSpacerin aVStack, priority; absorbs thestackdemo),frame-padding,zstack(layers from aForEachand aniftoo),flex(two pages: the constraints, then a priority tier, a child with no.flexvalue, and cross-axis placement),foreach. The corpus is 16 scenarios.ViewGraph.update()discarded a resize's viewport reasons, so a resize redraw reported none; fixed with a test and a line in the Slice 1 spec.tessera-devtoolsskill (0.3.0),mise-tasks/dev/{check,replay,run},text.md(it says what the Text demo renders until Slice 8), the plan, and the CHANGELOG.Critic review
Every finding is fixed in
a7260a7, each behavioural one with a test: F1siblings-disjointthrough list nodes (crafted projections, live fixtures, thezstackdemo and its scenarios; it fails without the fix), F2 the grid row rule, F3 62 styles fail closed, F4run --prettywarnings, F5 empty arguments, F6 the demo gaps, F7siblings-disjointnamed in the Flex scenarios, F8 CHANGELOG, F9 the trace version first, F10 the help overview, F11text=is a substring, F12 theForEachDemocomment. The open question is settled by recording focus events replayably (Discoveries in the plan). The re-check's fix isa7d0b5c: a view list is recognised by its module-qualified type (TesseraCore.ForEach,Swift.Optional, …) and carried as the projection node'sisList, so an app's own view namedForEachis not looked through (a live test fails under short-name matching), and the README says a container-less multi-view body is a true positive.Verify
mise run core:test: 959 tests green on Xcode 26.6 and on Xcode 27.1 (838 on the base).swift test --package-path Examples --only-use-versions-from-resolved-file: 39 tests green on both (25 on the base).mise run dev:run Examples/Scenarios: 16 scenarios, 16 passed; two runs give the same bytes, and--repeat 3agrees.mise run quality:format,quality:lint, anddocs:lintgreen, and the pre-commit hook's lint ofExamples/Tests; noPackage.resolvedin the diff.6ea1bd5(run 40087437615, after the critic fixes): all 14 jobs green, and everytestleg printed16 scenarios, 16 passed, 0 failed, Windows included. The head carries the run after the re-check's fix.For the owner
Decisions and discoveries marked "(owner to weigh)" are in the plan. The ones most worth a look: a bare
checkfails on errors and only listsframe-inside-clipwarnings (andrun --prettyprints them);text=matches a substring of the topmost leaf; the grid golden's row rule and its 62-letter limit; live traces record focus events; and the library findings below.Library findings, each recorded in the plan and left as today's behaviour unless noted:
update()dropped a resize's viewport reasons (fixed here).layoutEnvironmentChangedandrenderEnvironmentChanged: stacks rebuild their children'sEnvironmentValuesand reconciliation compares storage identity.layoutPrioritySpacerstarves lower-priority rigid siblings.min(n)floor cannot compress whileTexthasmin == ideal, fill weights invert when children are oversized, andFlextakes no alignment (a smaller child is always leading or top, thoughflex.mdsays "the selected" alignment).measured-within-proposalandframe-inside-clipfire by contract on a fixed frame wider than its parent, on Flex overflow, and on non-wrappingText; the workspace demo at 40x16 trips them on a collapsed pane (Slice 8).@ViewBuilderbody is always dirty, so every step draws (Slice 4 found it; the cost counters now show it).