Skip to content

Commit efb7150

Browse files
Merge pull request #11 from TimeWarpEngineering/dev
feat: release 1.0.0 - stable API, full Yoga test coverage, five engine fixes
2 parents f9d80d0 + be6c4c5 commit efb7150

70 files changed

Lines changed: 5355 additions & 141 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Task 148 - Implement FixFlexBasisFitContent Experimental Feature
2+
3+
## Summary
4+
5+
Yoga's `ExperimentalFeature::FixFlexBasisFitContent` is not implemented (the
6+
C# `ExperimentalFeature` enum only carries `WebFlexBasis`). It gates two
7+
behaviors in `computeFlexBasisForChild` (CalculateLayout.cpp): a fit-content
8+
fix for positive resolved flex-basis, and skipping height-axis FitContent for
9+
non-measure container children inside scroll subtrees (prevents re-measure
10+
cascades in ScrollViews). One generated conformance test is skipped because of
11+
it (`flex_basis_in_scroll_content_container` in FlexBasisFitContentTests).
12+
13+
Backlog: experimental in upstream Yoga, default-off, no consumer demand yet.
14+
Not a 1.0 blocker — documented as excluded in release notes.
15+
16+
## Todo List
17+
18+
- [ ] Add `FixFlexBasisFitContent` to the `ExperimentalFeature` enum
19+
- [ ] Port the two gated blocks in `FlexBasis.ComputeFlexBasisForChild` from
20+
the current C++ (including the scroll-ancestor walk)
21+
- [ ] Re-run the test converter for FlexBasisFitContent to include the skipped
22+
test; port any related upstream unit tests
23+
- [ ] Full suite green
24+
25+
## Results
26+
27+
(Add after completion)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Task 149 - Auto Min-Size Extension (Min-Content Measure)
2+
3+
## Summary
4+
5+
Upstream Yoga carries an auto-min-size extension implementing CSS Flexbox §4.5
6+
(automatic minimum size of flex items floored at min-content), gated behind the
7+
`YGErrataMinSizeUndefinedInsteadOfAuto` errata bit, with supporting node APIs:
8+
`YGNodeSetMinContentMeasureFunc`, `YGNodeHasMinContentMeasureFunc`,
9+
`YGNodeSet/GetMinContentWidth/Height`. None of this exists in the C# port.
10+
11+
16 tests from `YGAutoMinSizeTest.cpp` were omitted during task 145 for this
12+
reason (the one portable test, `default_config_preserves_existing_shrink`, was
13+
ported and passes). See the omission list in the header of
14+
`test/timewarp-flexbox-tests/Algorithm/AutoMinSizeTests.cs`.
15+
16+
Backlog: default-off behavior upstream, no consumer demand yet; documented as
17+
excluded in release notes alongside task 148 (FixFlexBasisFitContent).
18+
19+
## Todo List
20+
21+
- [ ] Add the errata bit to the `Errata` enum and thread it through
22+
`Config.HasErrata` checks per the C++
23+
- [ ] Port the min-content measure plumbing on `Node`
24+
(min-content measure func + static min-content width/height)
25+
- [ ] Port the algorithm changes (min-content flooring in
26+
`boundAxisWithinMinAndMax` / flex basis paths - locate in current C++)
27+
- [ ] Port the 16 omitted YGAutoMinSizeTest tests; full suite green
28+
29+
## Results
30+
31+
(Add after completion)
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ verified: dll + readme + logo, MIT license, version 1.0.0-beta.3.
1616
succeeded ("Your package was pushed" to nuget.pkg.github.com/TimeWarpEngineering)
1717
- [x] Release notes state the API is entirely new (beta.2 was the deleted
1818
`FlexNode`/`FlexLayoutEngine` implementation) and list known gaps.
19-
- [ ] Verify the package installs into a consumer project (needs a PAT with
20-
read:packages; push to the feed itself is confirmed via the workflow log)
19+
- [x] Verify the package installs into a consumer project — verified 2026-07-04
20+
against nuget.org with 1.0.0-beta.4 (plain `dotnet add package`, no PAT):
21+
the readme example runs and produces the documented output. (The repo has
22+
since moved to public + nuget.org Trusted Publishing via tasks 141/143;
23+
beta.4 superseded beta.3 as the first nuget.org release.)
2124

2225
## Toward stable 1.0 (follow-ups, not blockers for beta.3)
2326

@@ -27,6 +30,10 @@ verified: dll + readme + logo, MIT license, version 1.0.0-beta.3.
2730
- [ ] Text-measurement test helper to unlock skipped IntrinsicSize tests
2831
- [ ] Enable GenerateDocumentationFile so the package ships XML docs
2932

30-
## Results
33+
## Results (2026-07-04)
3134

32-
(Add after completion)
35+
Complete. beta.3 published to GitHub Packages on 2026-07-03; the follow-up
36+
1.0.0-beta.4 (2026-07-04) became the first nuget.org release through the new
37+
Trusted Publishing pipeline, and consumer installation was verified against
38+
nuget.org. Stable-1.0 follow-ups moved to their own tasks (139 done, 141 done,
39+
142 done, 143 in progress).

kanban/in-progress/143-timewarp-software-regen-trigger.md renamed to kanban/done/143-timewarp-software-regen-trigger.md

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ site without waiting for the nightly cron backstop.
6060
- [x] Dry-run: `workflow_dispatch` the pipeline without a release; then cut the next
6161
release and verify the dispatch lands (timewarp-software Actions shows a
6262
`rebuild` repository_dispatch run) and the package page appears
63-
- [ ] After task 142 lands, verify the flexbox skill shows on
64-
https://timewarp.software/ skills index (or add this repo to
65-
`extraSkillRepos` if it remains off nuget.org)
63+
- [x] After task 142 lands, verify the flexbox skill shows on
64+
https://timewarp.software/ skills index — VERIFIED 2026-07-04: skill live at
65+
/skills/flexbox/SKILL.md and in the skills index; package page live at
66+
/packages/timewarp.flexbox/
6667

6768
## Dependencies
6869

@@ -97,3 +98,31 @@ Remaining items require owner action: make the repo public, register
9798
TimeWarp.Flexbox for nuget.org Trusted Publishing under TimeWarp.Enterprises,
9899
then cut the next release and verify the package page + flexbox skill appear
99100
on https://timewarp.software/.
101+
102+
## Release-chain verification (2026-07-04, v1.0.0-beta.4)
103+
104+
- Release pipeline green end-to-end in Actions: OIDC login, version check,
105+
pack, "Your package was pushed" to nuget.org, "timewarp-software rebuild
106+
dispatched" (org App token mint worked).
107+
- Package live on nuget.org: listed=true, gallery page 200, consumer install
108+
verified (plain dotnet add package; readme example output exact).
109+
- Site rebuild runs complete successfully on each dispatch.
110+
- REMAINING: nuget.org's SEARCH index (which drives the site's owner-search
111+
catalog) lags for brand-new package ids - still 0 hits ~2h after push.
112+
Once it indexes, any rebuild (manual dispatch or the nightly cron) picks up
113+
the package page and the flexbox skill automatically. Verify then check the
114+
final box below.
115+
116+
## Results (2026-07-04)
117+
118+
Complete and verified end-to-end with v1.0.0-beta.4:
119+
release event -> OIDC Trusted Publishing -> push to nuget.org -> rebuild
120+
dispatch -> site rebuild -> https://timewarp.software/ shows the
121+
timewarp.flexbox package page and the flexbox skill.
122+
123+
Follow-up discovered in the process: the dispatch races NuGet's SEARCH index
124+
(the site catalog's data source) — a brand-new package id took ~3.5h to become
125+
searchable, so the release-triggered rebuilds ran too early and a manual
126+
re-dispatch was needed. Fix proposed on the receiver side (poll the search
127+
index for the payload's exact version before building, 30-min bound, nightly
128+
backstop unchanged): timewarp-software PR #23.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Task 144 - API Surface Tightening for 1.0
2+
3+
## Summary
4+
5+
The package currently exports 93 public types, of which only ~a dozen are the
6+
intended consumer API. The rest are engine internals that are public only
7+
because the Yoga port translated C++ file-by-file. 1.0.0 freezes the public
8+
surface under semver, so this must happen BEFORE the stable release —
9+
internalizing anything afterward is a breaking change. This is the only
10+
blocker for 1.0 (see task 147).
11+
12+
## Intended public surface (keep public)
13+
14+
`Node`, `Style`, `Config`, `CalculateLayout`, `LayoutResults`,
15+
`StyleLength`, `StyleSizeLength`, `YGValue`, `YGSize`, `FloatOptional`,
16+
`YogaAssertException`, the delegates (`MeasureFunc`, `BaselineFunc`,
17+
`DirtiedFunc`, `CloneNodeFunc`, `YogaLogHandler`), and the enums
18+
(`FlexDirection`, `Justify`, `Align`, `Wrap`, `Edge`, `Gutter`, `Dimension`,
19+
`PhysicalEdge`, `Direction`, `PositionType`, `Overflow`, `Display`,
20+
`BoxSizing`, `Unit`, `MeasureMode`, `NodeType`, `Errata`,
21+
`ExperimentalFeature`, `LogLevel`). Review each remaining public member on
22+
these types too (e.g. `Node.CloneChildrenIfNeeded`, layout setters) — some
23+
are engine plumbing.
24+
25+
## Todo List
26+
27+
- [x] Internalize the algorithm machinery: `AbsoluteLayout`, `AlignUtils`,
28+
`Baseline`, `BoundAxis`, `Cache`, `CalculateLayoutCore`, `FlexBasis`,
29+
`FlexDirectionUtils`, `FlexDistribution`, `FlexLine`, `JustifyContent`,
30+
`LayoutHelpers`, `MeasureNode`, `PixelGrid`, `TrailingPosition`,
31+
`SizingMode`(+extensions), `LayoutData`, `LayoutPassReason`(+extensions)
32+
- [x] Internalize storage/infra: `StyleValuePool`, `StyleValueHandle`,
33+
`SmallValueBuffer`(+generic), `LayoutableChildren`, `CachedMeasurement`,
34+
`Comparison`, `YogaEnums`(+extensions), `OrdinalCountAttribute`,
35+
`YogaEvent`/`LayoutData` event plumbing, `YogaLog`, `YogaAssert`
36+
- [x] **Eliminate the mutable public static wiring**: `FlexBasis.CalculateLayoutInternal`
37+
is a public static settable delegate — any consumer can reassign it and break
38+
the engine globally. Make the wiring internal (or replace the delegate
39+
indirection with a direct internal call; it exists only to break a
40+
circular file dependency from the C++ port)
41+
- [x] Add `[assembly: InternalsVisibleTo("TimeWarp.Flexbox.Tests")]` — the unit
42+
tests exercise the machinery directly (keep them; they're ported Yoga tests)
43+
- [x] Confirm naming decisions that 1.0 freezes: `YGValue`/`YGSize` Yoga-parity
44+
names (recommended: keep — they signal lineage), `CalculateLayout.Calculate`
45+
static entry point vs. a `Node.CalculateLayout()` instance convenience
46+
(consider adding the instance method; additive later, but nicer at 1.0)
47+
- [x] Rebuild + full suite (1335/0/3 pins behavior), AOT smoke, `dotnet pack`
48+
and inspect the package's public API (e.g. dotnet-inspect) to confirm the
49+
final surface
50+
- [x] Update the flexbox skill / readme if any referenced type changed visibility
51+
52+
## Notes
53+
54+
- The 530 generated conformance tests use only the consumer API — they are the
55+
guarantee that internalization broke nothing behavioral.
56+
- After this task, the public surface should be reviewable in one screenful.
57+
58+
## Results (2026-07-04)
59+
60+
Public surface reduced from 93 to 39 types: 19 enums, 6 value types
61+
(YGValue, YGSize, FloatOptional, StyleLength, StyleSizeLength,
62+
ExperimentalFeatureSet), 5 delegates, 9 classes (Node, Style, Config,
63+
CalculateLayout, LayoutResults, Comparison, StyleLength/StyleSizeLength
64+
extensions, YogaAssertException). All algorithm machinery, storage internals,
65+
event system, and debug utilities are now internal; the mutable
66+
FlexBasis.CalculateLayoutInternal delegate is internal with the type.
67+
68+
Deviations from the plan, deliberate:
69+
- `Comparison` kept public: tiny, useful (IsUndefined/IsDefined for NaN
70+
semantics), documented in the flexbox skill.
71+
- `ExperimentalFeatureSet` kept public (readonly struct surfaced by
72+
Config.EnabledExperiments).
73+
74+
API improvements while the surface was open:
75+
- `Node.GetChild(int)` now returns `Node` (was ILayoutableNode); the duplicate
76+
`GetChildNode` removed; ILayoutableNode members explicitly implemented.
77+
- Added `Node.CalculateLayout(availableWidth = NaN, availableHeight = NaN,
78+
ownerDirection = LTR)` instance convenience; docs/skill updated.
79+
- InternalsVisibleTo("timewarp-flexbox-tests") added; MockNode test helper
80+
internalized — which removed 5 phantom Fixie-discovered entries (suite is
81+
now a clean 1333 passed / 0 failed / 0 skipped; previously 1335+3 included
82+
MockNode's methods).
83+
84+
Verified: gated Release build zero warnings, suite 1333/0/0, AOT smoke native
85+
binary PASS, layout demo runs, dotnet format stable, ganda audit 22/22.
86+
Naming frozen deliberately: YGValue/YGSize Yoga-parity names kept.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Task 145 - Port Remaining Hand-Written Yoga Unit Tests
2+
3+
## Summary
4+
5+
Task 138 ported the generated conformance suite (all 25 files) and the four
6+
high-priority hand-written suites (Measure, MeasureCache, MeasureMode,
7+
Relayout). The remaining hand-written Yoga unit tests are still unported —
8+
coverage gaps, not known bugs. Recommended before 1.0 but not blocking
9+
(task 147 may proceed in parallel).
10+
11+
## Source Files
12+
13+
Yoga clone `tests/` directory (clone facebook/yoga; see the conventions and
14+
API mapping in `kanban/done/138-integration-testing.md` and the agent prompt
15+
patterns used there):
16+
17+
- YGBaselineFuncTest.cpp, YGAlignBaselineTest.cpp (baseline callbacks)
18+
- YGAspectRatioTest.cpp
19+
- YGEdgeTest.cpp, YGComputedMarginTest.cpp, YGComputedPaddingTest.cpp
20+
- YGHadOverflowTest.cpp
21+
- YGRoundingFunctionTest.cpp, YGRoundingMeasureFuncTest.cpp, YGScaleChangeTest.cpp
22+
- YGZeroOutLayoutRecursivelyTest.cpp
23+
- YGPersistenceTest.cpp, YGPersistentNodeCloningTest.cpp
24+
- YGDirtiedTest.cpp, YGDirtyMarkingTest.cpp (partially covered by NodeTests)
25+
- YGNodeChildTest.cpp, YGCloneNodeTest.cpp, YGNodeCallbackTest.cpp
26+
- FlexGapTest.cpp, YGAutoMinSizeTest.cpp, YGDefaultValuesTest.cpp
27+
28+
## Todo List
29+
30+
- [x] Port the suites above to `test/timewarp-flexbox-tests/` (Fixie
31+
conventions, snake_case Yoga names, 2-space indent)
32+
- [x] Omit tests using unsupported features (GTEST_SKIP, experimental features
33+
other than WebFlexBasis) and record omissions
34+
- [x] Expect failures to be real port defects — do not bend tests; fix the
35+
engine against the C++ (the task-138 pattern: every failure found a
36+
genuine mistranslation)
37+
- [x] Full suite green; note the new test count
38+
39+
## Results
40+
41+
(Add after completion)
42+
43+
## Results (2026-07-04)
44+
45+
All 21 suites ported (123 new tests; suite grew 1333 -> 1456, all passing).
46+
Ported via three parallel worktree agents; every failure was treated as an
47+
engine defect and fixed against the C++. FIVE genuine engine defects found
48+
and fixed:
49+
50+
1. PixelGrid rounding compared the fractional part as float, not double -
51+
`RoundValueToPixelGrid(-3.5001, 1, false, false)` returned -3 instead of
52+
-4 (float cast collapsed the 1e-4 tolerance boundary). Now uses the
53+
double InexactEquals overload like C++.
54+
2. ZeroOutLayoutRecursively only zeroed dimensions; C++ resets the ENTIRE
55+
layout (positions, margins, cached measurements) via `getLayout() = {}`
56+
and calls cloneChildrenIfNeeded before recursing. Also fixed the missing
57+
performLayout guard at the display:none call site (C++ avoids mutating
58+
during measure-only passes to prevent leaked hasNewLayout flags).
59+
3. Node.Clone() kept the source's Owner; C++ YGNodeClone clears it (a clone
60+
starts detached).
61+
4. CloneChildrenIfNeeded missed the branch where a cloned child is itself
62+
display:contents (its children must be cloned recursively).
63+
5. CleanupContentsNodesRecursively diverged wholesale: missing
64+
didPerformLayout parameter, missing CloneContentsChildrenIfNeeded /
65+
CloneChildrenIfNeeded calls, only zeroed dimensions instead of full
66+
layout reset, and wrongly recursed into regular (non-contents) children.
67+
Rewritten to C++ parity; call sites pass the correct flag per C++.
68+
69+
Three stale hand-written tests that codified the old behaviors were updated.
70+
71+
Recorded omissions: 16 YGAutoMinSizeTest tests (auto-min-size extension:
72+
errata bit + min-content measure funcs not in the engine - candidate for a
73+
backlog task), 3 free-semantics tests (no GC equivalent), 1 duplicate
74+
already in NodeTests, 1 null-return GetChild assertion (C# throws).
75+
76+
Verified: gated Release build clean, suite 1456/0/0, AOT smoke PASS,
77+
dotnet format stable, ganda audit 22/22.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Task 146 - Text-Measurement Helper and IntrinsicSize Tests
2+
3+
## Summary
4+
5+
~15 of Yoga's generated IntrinsicSize conformance tests are skipped because
6+
they need a text-measurement context: Yoga's test harness attaches an
7+
"intrinsic text" measure function (via `YGNodeSetContext` + a shared measure
8+
helper in `tests/util/TestUtil`) that simulates word-wrapping text. Build the
9+
C# equivalent and unlock those tests. Recommended before 1.0 but not blocking.
10+
11+
## Todo List
12+
13+
- [x] Study Yoga's text-measure test helper (gentest fixtures use an
14+
"intrinsic" measure function that wraps words at ~10px per char, and the
15+
C++ tests pass the text via node context)
16+
- [x] Implement an equivalent helper in the test project (e.g.
17+
`IntrinsicTextMeasure.MeasureFunc` reading `Node.Context` as the text)
18+
- [x] Extend `runfiles/port-generated-tests.cs` to translate
19+
`YGNodeSetContext(node, (void*)"...")` + `YGNodeSetMeasureFunc(node,
20+
IntrinsicSizeMeasure)` instead of skipping those tests
21+
- [x] Re-run the converter for IntrinsicSize; fix any engine divergences the
22+
new tests expose (compare against C++ as usual)
23+
- [x] Full suite green; update skill/readme "known gaps" wording once covered
24+
25+
## Results (2026-07-04)
26+
27+
Complete. `test/timewarp-flexbox-tests/Generated/IntrinsicSizeMeasure.cs` is a
28+
faithful port of Yoga's tests/util/TestUtil.cpp IntrinsicSizeMeasure (10px per
29+
char, word-wrap height simulation, text via Node.Context). The converter now
30+
translates `YGNodeSetContext(node, (void*)"...")` -> `node.Context = "..."`
31+
and the IntrinsicSizeMeasure SetMeasureFunc pattern, and emits 2-space
32+
indentation matching .editorconfig.
33+
34+
IntrinsicSize conversion went from 5 to 17 tests (12 text-measurement tests
35+
unlocked); the remaining 28 exclusions are all upstream GTEST_SKIPs
36+
(intrinsic sizing keywords Yoga itself disables). ALL 12 new tests passed on
37+
the first run - no engine changes were needed for intrinsic text measurement.
38+
Suite: 1468/0/0; format stable; audit 22/22. Neither the readme nor the skill
39+
listed this as a gap, so no doc wording changes were needed (the 1.0 release
40+
notes simply won't carry the beta.4 caveat).
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Task 147 - Release 1.0.0 (Stable)
2+
3+
## Summary
4+
5+
Publish the first stable release. The engine quality is release-ready
6+
(Yoga-conformance-verified, AOT-guaranteed, benchmarked, zero-dependency,
7+
documented, pipeline proven with beta.3/beta.4). The single hard blocker is
8+
task 144 (API surface tightening) because 1.0.0 freezes the public API under
9+
semver. Tasks 145/146 are recommended-but-optional coverage work — either land
10+
them first or note the gaps in the release notes.
11+
12+
## Dependencies
13+
14+
- **Task 144 (BLOCKER)** — API surface tightening; do not tag 1.0.0 before it
15+
- Task 145 (recommended) — remaining Yoga unit tests
16+
- Task 146 (recommended) — intrinsic text-measurement coverage
17+
- Some beta soak time is prudent: beta.4 shipped 2026-07-04 with zero external
18+
usage feedback so far
19+
20+
## Todo List
21+
22+
- [ ] Confirm task 144 merged; public API reviewed and final
23+
- [ ] Decide on 145/146: land first, or list as known gaps in release notes
24+
- [ ] Bump `<Version>` in `source/Directory.Build.props` to `1.0.0`
25+
- [ ] Update readme + skill install snippets (drop `--prerelease`; version 1.0.0)
26+
- [ ] PR dev -> master, green CI, merge
27+
- [ ] `gh release create v1.0.0` (NOT --prerelease). Release notes: stable API
28+
commitment statement; conformance/AOT/perf highlights; anything excluded
29+
(FixFlexBasisFitContent, intrinsic text if 146 not done); breaking changes
30+
vs beta.4 from the 144 internalization (beta consumers using internals)
31+
- [ ] Verify the chain: nuget.org (stable channel now — the site's
32+
`prerelease=false` owner search picks it up), site rebuild (the PR #23
33+
wait-gate handles search-index lag), package page shows stable version
34+
- [ ] Consumer install check without `--prerelease`
35+
36+
## Notes
37+
38+
- First STABLE version: the site's stable-channel search
39+
(`prerelease=false`) will list it — until now flexbox only existed in the
40+
prerelease channel.
41+
- Semver discipline starts here: internalization or renames after this
42+
require 2.0.0.
43+
44+
## Results
45+
46+
(Add after completion)

0 commit comments

Comments
 (0)