Skip to content

Commit f9d80d0

Browse files
Merge pull request #10 from TimeWarpEngineering/dev
feat: task 139/141/142/143 - style gates, AOT validation, flexbox skill, release pipeline; 1.0.0-beta.4
2 parents 1dfad96 + c7242e4 commit f9d80d0

56 files changed

Lines changed: 1972 additions & 963 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.githooks/post-commit.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/usr/bin/env -S dotnet --
2+
#:property TreatWarningsAsErrors=false
3+
#:property CodeAnalysisTreatWarningsAsErrors=false
24
#:package TimeWarp.Amuru
35
#:property NoWarn=CA2007
46

.githooks/post-merge.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/usr/bin/env -S dotnet --
2+
#:property TreatWarningsAsErrors=false
3+
#:property CodeAnalysisTreatWarningsAsErrors=false
24
#:package TimeWarp.Amuru
35
#:property NoWarn=CA2007
46

.github/workflows/workflow.yml

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1-
name: CI/CD
1+
name: CI/CD Workflow
22

33
on:
44
push:
5-
branches: [master, main]
5+
branches: [master]
66
paths:
77
- 'source/**'
88
- 'test/**'
9+
- 'tests/**'
10+
- 'benchmarks/**'
11+
- 'tools/**'
912
- '.github/workflows/**'
1013
- 'Directory.Build.props'
1114
- 'Directory.Packages.props'
1215
pull_request:
13-
branches: [master, main]
16+
branches: [master]
1417
paths:
1518
- 'source/**'
1619
- 'test/**'
20+
- 'tests/**'
21+
- 'benchmarks/**'
22+
- 'tools/**'
1723
- '.github/workflows/**'
1824
- 'Directory.Build.props'
1925
- 'Directory.Packages.props'
@@ -22,33 +28,65 @@ on:
2228
workflow_dispatch:
2329

2430
jobs:
25-
build-and-publish:
31+
ci:
2632
runs-on: ubuntu-latest
2733
permissions:
2834
contents: read
29-
packages: write
30-
35+
id-token: write # Required for NuGet Trusted Publishing (OIDC)
36+
3137
steps:
32-
- uses: actions/checkout@v4
33-
38+
- name: Checkout repository
39+
uses: actions/checkout@v4
40+
with:
41+
fetch-depth: 0 # Required for version detection
42+
3443
- name: Setup .NET
3544
uses: actions/setup-dotnet@v4
3645
with:
3746
dotnet-version: '10.0.x'
38-
39-
- name: Restore
40-
run: dotnet restore
41-
42-
- name: Build
43-
run: dotnet build --configuration Release --no-restore
44-
45-
- name: Test
46-
run: dotnet test --configuration Release --no-build
47-
48-
- name: Publish to GitHub Packages
49-
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
47+
48+
- name: NuGet login (OIDC Trusted Publishing)
49+
if: github.event_name == 'release'
50+
id: nuget-login
51+
uses: nuget/login@v1
52+
with:
53+
user: TimeWarp.Enterprises
54+
55+
# Cross-repo repository_dispatch (timewarp-software rebuild) cannot use the
56+
# default GITHUB_TOKEN. A short-lived installation token is minted from the
57+
# org's TimeWarp Rebuild Dispatcher GitHub App (org variable REBUILD_APP_ID +
58+
# org secret REBUILD_APP_PRIVATE_KEY). If the app is not configured the step
59+
# is skipped and the dispatch degrades to a warning (site rebuilds nightly).
60+
- name: Mint rebuild dispatch token
61+
if: github.event_name == 'release' && vars.REBUILD_APP_ID != ''
62+
id: rebuild-token
63+
uses: actions/create-github-app-token@v2
64+
with:
65+
app-id: ${{ vars.REBUILD_APP_ID }}
66+
private-key: ${{ secrets.REBUILD_APP_PRIVATE_KEY }}
67+
owner: TimeWarpEngineering
68+
repositories: timewarp-software
69+
70+
- name: Run CI Pipeline
71+
env:
72+
GH_TOKEN: ${{ steps.rebuild-token.outputs.token }}
73+
run: |
74+
if [ "${{ github.event_name }}" == "release" ]; then
75+
dotnet run tools/dev-cli/dev.cs -- workflow --api-key "${{ steps.nuget-login.outputs.NUGET_API_KEY }}"
76+
else
77+
dotnet run tools/dev-cli/dev.cs -- workflow
78+
fi
79+
80+
- name: AOT smoke test
5081
run: |
51-
dotnet nuget push artifacts/packages/*.nupkg \
52-
--source "https://nuget.pkg.github.com/TimeWarpEngineering/index.json" \
53-
--api-key ${{ secrets.GITHUB_TOKEN }} \
54-
--skip-duplicate
82+
dotnet publish tests/timewarp-flexbox-aot-smoke/timewarp-flexbox-aot-smoke.csproj \
83+
--configuration Release -r linux-x64 -o artifacts/aot-smoke
84+
./artifacts/aot-smoke/TimeWarp.Flexbox.AotSmoke
85+
86+
- name: Upload Artifacts
87+
if: always()
88+
uses: actions/upload-artifact@v4
89+
with:
90+
name: Packages-${{ github.run_number }}
91+
path: artifacts/packages/*.nupkg
92+
if-no-files-found: ignore

.timewarp/dev.jsonc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
// Per-repo configuration for TimeWarp.Flexbox dev-cli
3+
"checkVersionConfig": {
4+
"checkVersionStrategy": "nuget-search",
5+
"packages": "TimeWarp.Flexbox"
6+
}
7+
}

Directory.Build.props

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,27 @@
2424

2525
<!-- Code quality, analyzers, and warning configuration -->
2626
<PropertyGroup Label="Code Quality and Analysis">
27-
<!-- TEMPORARY: style/analyzer gates relaxed while the Yoga C++ port is in progress.
28-
The ported code does not yet conform to .editorconfig (indentation, naming).
29-
Restore per kanban/to-do/139-restore-style-enforcement.md -->
30-
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
27+
<!-- Treat all warnings as errors -->
28+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
3129
<WarningLevel>5</WarningLevel>
32-
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
30+
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
3331
<EnableNETAnalyzers>true</EnableNETAnalyzers>
3432
<AnalysisMode>All</AnalysisMode>
3533
<AnalysisLevel>latest-all</AnalysisLevel>
3634

3735
<!-- Report analyzer diagnostics in build output -->
3836
<ReportAnalyzer>true</ReportAnalyzer>
39-
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
37+
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
38+
39+
<!-- Required for IDE0005 (remove unnecessary usings) to run in build -->
40+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
4041

4142
<!-- Suppress specific warnings -->
4243
<!-- CA1014: CLS compliance not required for this library -->
4344
<!-- CA1724: Type name conflicts (acceptable in this context) -->
4445
<!-- CA1812: False positives for DI-instantiated classes -->
45-
<NoWarn>$(NoWarn);CA1014;CA1724;CA1812</NoWarn>
46+
<!-- CS1591: XML doc comments not required on all public members -->
47+
<NoWarn>$(NoWarn);CA1014;CA1724;CA1812;CS1591</NoWarn>
4648
</PropertyGroup>
4749

4850
<!-- Code analyzers applied to all projects -->
Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1+
namespace TimeWarp.Flexbox.Benchmarks;
2+
13
using BenchmarkDotNet.Running;
2-
using TimeWarp.Flexbox.Benchmarks;
34

4-
BenchmarkRunner.Run<LayoutBenchmarks>();
5+
/// <summary>
6+
/// Benchmark entry point.
7+
/// </summary>
8+
public static class Program
9+
{
10+
/// <summary>
11+
/// Runs the layout benchmarks.
12+
/// </summary>
13+
public static void Main() => BenchmarkRunner.Run<LayoutBenchmarks>();
14+
}

kanban/to-do/139-restore-style-enforcement.md renamed to kanban/done/139-restore-style-enforcement.md

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ code is written with 4-space indentation while `.editorconfig` mandates 2-space,
1414
- [x] Run `dotnet format` across the solution and commit the result as a standalone
1515
formatting-only commit (no logic changes mixed in) — commit eb29f1a, 88 files,
1616
~15.5k IDE0055 errors eliminated; suite verified unchanged (1335/0/3)
17-
- [ ] Fix or explicitly suppress remaining analyzer diagnostics. Remaining with gates on
17+
- [x] Fix or explicitly suppress remaining analyzer diagnostics. Remaining with gates on
1818
(~120 unique errors as of 2026-07-03):
1919
- **IDE1006 (~83, DECISION NEEDED):** ported code uses `_camelCase` private fields
2020
paired with PascalCase properties (`_direction`/`Direction`); .editorconfig forbids
@@ -24,11 +24,12 @@ code is written with 4-space indentation while `.editorconfig` mandates 2-space,
2424
- **IDE0078/0072/0010/0011/0251/0370/0004 (~37):** pattern matching, switch
2525
exhaustiveness, braces, readonly members — no batch fixer; hand-fix per file
2626
(`dotnet format style --diagnostics ...` only resolved 4 files)
27-
- [ ] Re-enable `TreatWarningsAsErrors`, `CodeAnalysisTreatWarningsAsErrors`, and
27+
- [x] Re-enable `TreatWarningsAsErrors`, `CodeAnalysisTreatWarningsAsErrors`, and
2828
`EnforceCodeStyleInBuild` in `Directory.Build.props` (remove the temporary relaxation
2929
block referencing this task)
30-
- [ ] Verify plain `dotnet build` and `dotnet test` pass with gates re-enabled
31-
- [ ] Verify CI workflow goes green
30+
- [x] Verify plain `dotnet build` and `dotnet test` pass with gates re-enabled
31+
- [x] Verify CI workflow goes green (verified locally with the exact CI commands;
32+
CI itself runs on the PR)
3233

3334
## Notes
3435

@@ -39,6 +40,30 @@ code is written with 4-space indentation while `.editorconfig` mandates 2-space,
3940
- `agents.md` states the repo standard is 2-space indentation; if 4-space is chosen instead,
4041
update `agents.md` and `.editorconfig` together.
4142

42-
## Results
43+
## Results (2026-07-04)
4344

44-
(Add after completion)
45+
Complete. TreatWarningsAsErrors, CodeAnalysisTreatWarningsAsErrors, and
46+
EnforceCodeStyleInBuild are restored to true in Directory.Build.props; plain
47+
`dotnet build -c Release` succeeds with zero warnings/errors, suite unchanged
48+
(1335/0/3), dotnet format stable, ganda audit 22/22.
49+
50+
- All ~83 IDE1006 field-naming violations fixed to the house standard (ALL
51+
fields PascalCase, no underscore prefixes, matching sibling repos): trivial
52+
backing-field/property pairs became auto-properties; properties with logic
53+
use the C# `field` keyword (as timewarp-terminal does); type-mismatched
54+
backings renamed descriptively (e.g. FlexHandle, MarginHandles,
55+
ChildrenInternal).
56+
- All mechanical diagnostics fixed (braces, switch exhaustiveness, pattern
57+
matching, readonly members, redundant casts/suppressions).
58+
- GenerateDocumentationFile=true enabled (required for IDE0005), CS1591 added
59+
to global NoWarn per the terminal pattern - the package now ships XML docs.
60+
- RS0030 (banned Console) suppressed only around YogaLog.DefaultLog with
61+
justification: it is the default logger sink and the library stays
62+
zero-dependency.
63+
- test/Directory.Build.props extended per sibling convention (snake_case Yoga
64+
test names, delegate-signature params, package-injected Fixie.Main.cs).
65+
- Scripts (runfiles, git hooks, sample) are file-based apps that inherit the
66+
repo gates when invoked; they carry `#:property TreatWarningsAsErrors=false`
67+
so warnings stay visible but non-blocking. Product code is fully gated.
68+
- Benchmarks program converted to Program.Main style; demo's ASCII grid
69+
converted to a jagged array (CA1814).
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Task 141 - Validate AOT Compatibility
2+
3+
## Summary
4+
5+
Validate that TimeWarp.Flexbox is fully compatible with Native AOT compilation and
6+
trimming, mark the package accordingly, and add a guard so compatibility cannot
7+
silently regress. A layout engine is a natural fit for AOT consumers (CLI tools,
8+
games, mobile via NativeAOT); the library should advertise and guarantee it.
9+
10+
Initial scan (2026-07-03) is promising: the library is pure computation with no
11+
reflection, no `Enum.GetValues`, no serialization, no dynamic code — enum iteration
12+
uses `Unsafe.As` over static ordinal counts, and callbacks are plain delegates.
13+
14+
## Todo List
15+
16+
- [x] Set `<IsAotCompatible>true</IsAotCompatible>` on
17+
`source/timewarp-flexbox/timewarp-flexbox.csproj` (implies `IsTrimmable` and
18+
enables the trim/AOT/single-file analyzers at build time)
19+
- [x] Build and resolve any IL2xxx (trim) / IL3xxx (AOT) analyzer warnings — ZERO produced
20+
(note: `TreatWarningsAsErrors` is currently relaxed per task 139 — check the
21+
build output explicitly rather than relying on a red build)
22+
- [x] Add an AOT smoke test: a minimal console consumer published with
23+
`<PublishAot>true</PublishAot>` that builds a layout tree (grow, wrap, RTL,
24+
absolute), calculates layout, and asserts a few computed values — run the
25+
native binary and check its output/exit code
26+
- [x] Wire the AOT smoke test into CI (workflow.yml) so regressions fail the build;
27+
publish time is the main cost, so consider running it only on PRs to master
28+
- [ ] (skipped, optional) Verify benchmark numbers under AOT out of curiosity (BenchmarkDotNet supports
29+
a NativeAOT toolchain) — optional, informational
30+
- [x] Document AOT/trimming support in the readme and add
31+
`<PackageTags>...aot...</PackageTags>` if validated
32+
- [x] Confirm the `TimeWarp.Build.Tasks` / analyzer packages (PrivateAssets=all)
33+
contribute nothing to the consumer's closure (they should not, but verify the
34+
nuspec has no dependency leakage)
35+
36+
## Notes
37+
38+
- Only the library (`source/timewarp-flexbox`) needs to be AOT-compatible; tests,
39+
benchmarks, and tools do not.
40+
- `Event/Event.cs` (YogaEvent) and the measure/baseline delegates are plain C#
41+
delegates — AOT-safe, but the analyzers will confirm.
42+
- `Config.Context` / `Node.Context` are `object?` bags; fine for AOT as long as the
43+
library never reflects over them (it does not).
44+
- If analyzers surface anything in `StyleValuePool`/`SmallValueBuffer` (bit
45+
manipulation, `Unsafe`), these are AOT-safe patterns; warnings there would come
46+
from the analyzer being conservative — prefer targeted `UnconditionalSuppressMessage`
47+
with justification over blanket suppressions.
48+
49+
## Results (2026-07-04)
50+
51+
Validated. The library is fully Native AOT and trimming compatible.
52+
53+
- `IsAotCompatible=true` on the library: trim/AOT/single-file analyzers produce
54+
ZERO diagnostics (with warnings-as-errors active, so enforced). Package tags
55+
gained `aot;trimming`.
56+
- New `tests/timewarp-flexbox-aot-smoke`: a zero-dependency PublishAot console
57+
app exercising grow, row positions, RTL, absolute insets, and wrap+gap with
58+
exact-value assertions. Verified under JIT (dotnet run) and as a native ELF
59+
binary (2.0 MB, linux-x64): "AOT smoke: PASS (all layout checks)", exit 0.
60+
- CI: new "AOT smoke test" step in workflow.yml publishes and runs the native
61+
binary on every workflow run. Project added to the .slnx.
62+
- nuspec verified: empty dependency group (analyzers/build-tasks do not leak),
63+
and the package now ships XML docs (from task 139's GenerateDocumentationFile).
64+
- Readme documents the AOT/trimming guarantee.
65+
- Skipped (optional): BenchmarkDotNet NativeAOT toolchain run — informational
66+
only; JIT numbers already recorded in task 138.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Task 142 - Create Flexbox Skill
2+
3+
## Summary
4+
5+
Author `skills/flexbox/SKILL.md` teaching AI agents how to use TimeWarp.Flexbox,
6+
following the sibling-repo convention (timewarp-terminal `skills/terminal/SKILL.md`,
7+
timewarp-nuru `skills/nuru/SKILL.md`, timewarp-amuru `skills/amuru/SKILL.md`):
8+
YAML frontmatter (`name`, `description` tuned for auto-triggering), repository and
9+
package links, a "When to Use What" table, then task-oriented guidance with
10+
compiling code examples. Target length in line with siblings (~350-500 lines).
11+
12+
## Target Files
13+
14+
| Type | Path |
15+
| ----- | --------------------------- |
16+
| Skill | `skills/flexbox/SKILL.md` |
17+
18+
## Todo List
19+
20+
- [x] Frontmatter: `name: flexbox`; description that triggers on "flexbox layout in
21+
C#", "compute layout without a UI framework", "Yoga layout for .NET",
22+
"position/size a tree of boxes", etc.
23+
- [x] Core model section: build a `Node` tree (`InsertChild`), set styles via
24+
`Node.Style`, run `CalculateLayout.Calculate(root, availW, availH, Direction)`,
25+
read results from `Node.Layout` (`GetPosition(PhysicalEdge)`,
26+
`GetDimension(Dimension)`); `float.NaN` = undefined/unconstrained
27+
- [x] CSS-to-C# mapping table: every CSS flexbox property to its C# call
28+
(`width: 100px` -> `SetDimension(Dimension.Width, StyleSizeLength.Points(100))`,
29+
`margin: 10px` -> `SetMargin(Edge.All, StyleLength.Points(10))`,
30+
`gap` -> `SetGap(Gutter.All, ...)`, enums for direction/justify/align/wrap/
31+
position-type/overflow/display/box-sizing)
32+
- [x] Gotchas section (the things an agent will get wrong):
33+
- Defaults are Yoga's, not web CSS: `flex-direction: column`, `flex-shrink: 0`,
34+
`align-content: flex-start`; `Config.UseWebDefaults` for web behavior
35+
- `StyleLength` vs `StyleSizeLength` (edges/gaps vs dimensions/flex-basis)
36+
- Owner semantics: a child belongs to one parent; re-inserting an owned child
37+
throws (`YogaAssertException`) - `RemoveChild` first
38+
- Style mutations auto-dirty the tree; re-call `Calculate` to get fresh results
39+
- Positions are relative to the parent; accumulate for absolute coordinates
40+
- [x] Recipes: sidebar+content shell, wrapping card grid with gap, centered overlay
41+
via absolute insets, RTL, custom measured leaf (SetMeasureFunc with
42+
MeasureMode semantics), incremental re-layout loop
43+
- [x] Verify every code example compiles and produces the outputs shown (reuse the
44+
readme-example verification approach; `samples/layout-demo/layout-demo.cs` is
45+
a source of known-good snippets)
46+
- [x] Point to the visual demo and the Generated conformance tests as further
47+
reference material
48+
- [ ] (deferred to task 143) Publication to https://timewarp.software/ happens automatically once the repo
49+
qualifies for the site's catalog: the site aggregates `skills/*/SKILL.md` from
50+
family repos' default branches at build time. Repo qualification and the
51+
release-triggered rebuild are task 143's scope; verify the skill appears on the
52+
site's skills index after both land.
53+
54+
## Notes
55+
56+
- The audit scaffold already created `skills/` (currently only `.gitkeep`).
57+
- Keep the skill self-contained: agents may load it without repo access, so inline
58+
the essential API surface rather than deferring to source files.
59+
- The `description` frontmatter drives triggering accuracy - study the sibling
60+
descriptions (terminal, nuru, amuru) for phrasing that names both the library and
61+
the generic tasks it solves.
62+
63+
## Results (2026-07-04)
64+
65+
`skills/flexbox/SKILL.md` created (~270 lines), following the sibling
66+
convention (terminal/nuru/amuru): trigger-tuned frontmatter, When to Use What
67+
table, core five-step model, full CSS-to-C# mapping table (both length types),
68+
Yoga-vs-web defaults table with the UseWebDefaults escape hatch, reading
69+
results + AbsolutePosition helper, six recipes (app shell, wrap grid, absolute
70+
overlay, RTL, measure function with MeasureMode semantics, incremental
71+
re-layout), pitfalls (owner semantics, NaN, length types, one-config-per-tree),
72+
installation, and further-reference pointers.
73+
74+
Every code snippet was compiled and executed against the library; all output
75+
comments in the skill are actual engine output (verified via a scratch console
76+
consumer). Site publication happens automatically once task 143 lands (the
77+
site aggregates skills/*/SKILL.md from qualifying repos at build time).

0 commit comments

Comments
 (0)