Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .githooks/post-commit.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/usr/bin/env -S dotnet --
#:property TreatWarningsAsErrors=false
#:property CodeAnalysisTreatWarningsAsErrors=false
#:package TimeWarp.Amuru
#:property NoWarn=CA2007

Expand Down
2 changes: 2 additions & 0 deletions .githooks/post-merge.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/usr/bin/env -S dotnet --
#:property TreatWarningsAsErrors=false
#:property CodeAnalysisTreatWarningsAsErrors=false
#:package TimeWarp.Amuru
#:property NoWarn=CA2007

Expand Down
86 changes: 62 additions & 24 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
name: CI/CD
name: CI/CD Workflow

on:
push:
branches: [master, main]
branches: [master]
paths:
- 'source/**'
- 'test/**'
- 'tests/**'
- 'benchmarks/**'
- 'tools/**'
- '.github/workflows/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
pull_request:
branches: [master, main]
branches: [master]
paths:
- 'source/**'
- 'test/**'
- 'tests/**'
- 'benchmarks/**'
- 'tools/**'
- '.github/workflows/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
Expand All @@ -22,33 +28,65 @@ on:
workflow_dispatch:

jobs:
build-and-publish:
ci:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # Required for NuGet Trusted Publishing (OIDC)

steps:
- uses: actions/checkout@v4

- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for version detection

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

- name: Restore
run: dotnet restore

- name: Build
run: dotnet build --configuration Release --no-restore

- name: Test
run: dotnet test --configuration Release --no-build

- name: Publish to GitHub Packages
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'

- name: NuGet login (OIDC Trusted Publishing)
if: github.event_name == 'release'
id: nuget-login
uses: nuget/login@v1
with:
user: TimeWarp.Enterprises

# Cross-repo repository_dispatch (timewarp-software rebuild) cannot use the
# default GITHUB_TOKEN. A short-lived installation token is minted from the
# org's TimeWarp Rebuild Dispatcher GitHub App (org variable REBUILD_APP_ID +
# org secret REBUILD_APP_PRIVATE_KEY). If the app is not configured the step
# is skipped and the dispatch degrades to a warning (site rebuilds nightly).
- name: Mint rebuild dispatch token
if: github.event_name == 'release' && vars.REBUILD_APP_ID != ''
id: rebuild-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.REBUILD_APP_ID }}
private-key: ${{ secrets.REBUILD_APP_PRIVATE_KEY }}
owner: TimeWarpEngineering
repositories: timewarp-software

- name: Run CI Pipeline
env:
GH_TOKEN: ${{ steps.rebuild-token.outputs.token }}
run: |
if [ "${{ github.event_name }}" == "release" ]; then
dotnet run tools/dev-cli/dev.cs -- workflow --api-key "${{ steps.nuget-login.outputs.NUGET_API_KEY }}"
else
dotnet run tools/dev-cli/dev.cs -- workflow
fi

- name: AOT smoke test
run: |
dotnet nuget push artifacts/packages/*.nupkg \
--source "https://nuget.pkg.github.com/TimeWarpEngineering/index.json" \
--api-key ${{ secrets.GITHUB_TOKEN }} \
--skip-duplicate
dotnet publish tests/timewarp-flexbox-aot-smoke/timewarp-flexbox-aot-smoke.csproj \
--configuration Release -r linux-x64 -o artifacts/aot-smoke
./artifacts/aot-smoke/TimeWarp.Flexbox.AotSmoke

- name: Upload Artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: Packages-${{ github.run_number }}
path: artifacts/packages/*.nupkg
if-no-files-found: ignore
7 changes: 7 additions & 0 deletions .timewarp/dev.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
// Per-repo configuration for TimeWarp.Flexbox dev-cli
"checkVersionConfig": {
"checkVersionStrategy": "nuget-search",
"packages": "TimeWarp.Flexbox"
}
}
16 changes: 9 additions & 7 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,27 @@

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

<!-- Report analyzer diagnostics in build output -->
<ReportAnalyzer>true</ReportAnalyzer>
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>

<!-- Required for IDE0005 (remove unnecessary usings) to run in build -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>

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

<!-- Code analyzers applied to all projects -->
Expand Down
14 changes: 12 additions & 2 deletions benchmarks/timewarp-flexbox-benchmarks/program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
namespace TimeWarp.Flexbox.Benchmarks;

using BenchmarkDotNet.Running;
using TimeWarp.Flexbox.Benchmarks;

BenchmarkRunner.Run<LayoutBenchmarks>();
/// <summary>
/// Benchmark entry point.
/// </summary>
public static class Program
{
/// <summary>
/// Runs the layout benchmarks.
/// </summary>
public static void Main() => BenchmarkRunner.Run<LayoutBenchmarks>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ code is written with 4-space indentation while `.editorconfig` mandates 2-space,
- [x] Run `dotnet format` across the solution and commit the result as a standalone
formatting-only commit (no logic changes mixed in) — commit eb29f1a, 88 files,
~15.5k IDE0055 errors eliminated; suite verified unchanged (1335/0/3)
- [ ] Fix or explicitly suppress remaining analyzer diagnostics. Remaining with gates on
- [x] Fix or explicitly suppress remaining analyzer diagnostics. Remaining with gates on
(~120 unique errors as of 2026-07-03):
- **IDE1006 (~83, DECISION NEEDED):** ported code uses `_camelCase` private fields
paired with PascalCase properties (`_direction`/`Direction`); .editorconfig forbids
Expand All @@ -24,11 +24,12 @@ code is written with 4-space indentation while `.editorconfig` mandates 2-space,
- **IDE0078/0072/0010/0011/0251/0370/0004 (~37):** pattern matching, switch
exhaustiveness, braces, readonly members — no batch fixer; hand-fix per file
(`dotnet format style --diagnostics ...` only resolved 4 files)
- [ ] Re-enable `TreatWarningsAsErrors`, `CodeAnalysisTreatWarningsAsErrors`, and
- [x] Re-enable `TreatWarningsAsErrors`, `CodeAnalysisTreatWarningsAsErrors`, and
`EnforceCodeStyleInBuild` in `Directory.Build.props` (remove the temporary relaxation
block referencing this task)
- [ ] Verify plain `dotnet build` and `dotnet test` pass with gates re-enabled
- [ ] Verify CI workflow goes green
- [x] Verify plain `dotnet build` and `dotnet test` pass with gates re-enabled
- [x] Verify CI workflow goes green (verified locally with the exact CI commands;
CI itself runs on the PR)

## Notes

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

## Results
## Results (2026-07-04)

(Add after completion)
Complete. TreatWarningsAsErrors, CodeAnalysisTreatWarningsAsErrors, and
EnforceCodeStyleInBuild are restored to true in Directory.Build.props; plain
`dotnet build -c Release` succeeds with zero warnings/errors, suite unchanged
(1335/0/3), dotnet format stable, ganda audit 22/22.

- All ~83 IDE1006 field-naming violations fixed to the house standard (ALL
fields PascalCase, no underscore prefixes, matching sibling repos): trivial
backing-field/property pairs became auto-properties; properties with logic
use the C# `field` keyword (as timewarp-terminal does); type-mismatched
backings renamed descriptively (e.g. FlexHandle, MarginHandles,
ChildrenInternal).
- All mechanical diagnostics fixed (braces, switch exhaustiveness, pattern
matching, readonly members, redundant casts/suppressions).
- GenerateDocumentationFile=true enabled (required for IDE0005), CS1591 added
to global NoWarn per the terminal pattern - the package now ships XML docs.
- RS0030 (banned Console) suppressed only around YogaLog.DefaultLog with
justification: it is the default logger sink and the library stays
zero-dependency.
- test/Directory.Build.props extended per sibling convention (snake_case Yoga
test names, delegate-signature params, package-injected Fixie.Main.cs).
- Scripts (runfiles, git hooks, sample) are file-based apps that inherit the
repo gates when invoked; they carry `#:property TreatWarningsAsErrors=false`
so warnings stay visible but non-blocking. Product code is fully gated.
- Benchmarks program converted to Program.Main style; demo's ASCII grid
converted to a jagged array (CA1814).
66 changes: 66 additions & 0 deletions kanban/done/141-validate-aot-compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Task 141 - Validate AOT Compatibility

## Summary

Validate that TimeWarp.Flexbox is fully compatible with Native AOT compilation and
trimming, mark the package accordingly, and add a guard so compatibility cannot
silently regress. A layout engine is a natural fit for AOT consumers (CLI tools,
games, mobile via NativeAOT); the library should advertise and guarantee it.

Initial scan (2026-07-03) is promising: the library is pure computation with no
reflection, no `Enum.GetValues`, no serialization, no dynamic code — enum iteration
uses `Unsafe.As` over static ordinal counts, and callbacks are plain delegates.

## Todo List

- [x] Set `<IsAotCompatible>true</IsAotCompatible>` on
`source/timewarp-flexbox/timewarp-flexbox.csproj` (implies `IsTrimmable` and
enables the trim/AOT/single-file analyzers at build time)
- [x] Build and resolve any IL2xxx (trim) / IL3xxx (AOT) analyzer warnings — ZERO produced
(note: `TreatWarningsAsErrors` is currently relaxed per task 139 — check the
build output explicitly rather than relying on a red build)
- [x] Add an AOT smoke test: a minimal console consumer published with
`<PublishAot>true</PublishAot>` that builds a layout tree (grow, wrap, RTL,
absolute), calculates layout, and asserts a few computed values — run the
native binary and check its output/exit code
- [x] Wire the AOT smoke test into CI (workflow.yml) so regressions fail the build;
publish time is the main cost, so consider running it only on PRs to master
- [ ] (skipped, optional) Verify benchmark numbers under AOT out of curiosity (BenchmarkDotNet supports
a NativeAOT toolchain) — optional, informational
- [x] Document AOT/trimming support in the readme and add
`<PackageTags>...aot...</PackageTags>` if validated
- [x] Confirm the `TimeWarp.Build.Tasks` / analyzer packages (PrivateAssets=all)
contribute nothing to the consumer's closure (they should not, but verify the
nuspec has no dependency leakage)

## Notes

- Only the library (`source/timewarp-flexbox`) needs to be AOT-compatible; tests,
benchmarks, and tools do not.
- `Event/Event.cs` (YogaEvent) and the measure/baseline delegates are plain C#
delegates — AOT-safe, but the analyzers will confirm.
- `Config.Context` / `Node.Context` are `object?` bags; fine for AOT as long as the
library never reflects over them (it does not).
- If analyzers surface anything in `StyleValuePool`/`SmallValueBuffer` (bit
manipulation, `Unsafe`), these are AOT-safe patterns; warnings there would come
from the analyzer being conservative — prefer targeted `UnconditionalSuppressMessage`
with justification over blanket suppressions.

## Results (2026-07-04)

Validated. The library is fully Native AOT and trimming compatible.

- `IsAotCompatible=true` on the library: trim/AOT/single-file analyzers produce
ZERO diagnostics (with warnings-as-errors active, so enforced). Package tags
gained `aot;trimming`.
- New `tests/timewarp-flexbox-aot-smoke`: a zero-dependency PublishAot console
app exercising grow, row positions, RTL, absolute insets, and wrap+gap with
exact-value assertions. Verified under JIT (dotnet run) and as a native ELF
binary (2.0 MB, linux-x64): "AOT smoke: PASS (all layout checks)", exit 0.
- CI: new "AOT smoke test" step in workflow.yml publishes and runs the native
binary on every workflow run. Project added to the .slnx.
- nuspec verified: empty dependency group (analyzers/build-tasks do not leak),
and the package now ships XML docs (from task 139's GenerateDocumentationFile).
- Readme documents the AOT/trimming guarantee.
- Skipped (optional): BenchmarkDotNet NativeAOT toolchain run — informational
only; JIT numbers already recorded in task 138.
77 changes: 77 additions & 0 deletions kanban/done/142-create-flexbox-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Task 142 - Create Flexbox Skill

## Summary

Author `skills/flexbox/SKILL.md` teaching AI agents how to use TimeWarp.Flexbox,
following the sibling-repo convention (timewarp-terminal `skills/terminal/SKILL.md`,
timewarp-nuru `skills/nuru/SKILL.md`, timewarp-amuru `skills/amuru/SKILL.md`):
YAML frontmatter (`name`, `description` tuned for auto-triggering), repository and
package links, a "When to Use What" table, then task-oriented guidance with
compiling code examples. Target length in line with siblings (~350-500 lines).

## Target Files

| Type | Path |
| ----- | --------------------------- |
| Skill | `skills/flexbox/SKILL.md` |

## Todo List

- [x] Frontmatter: `name: flexbox`; description that triggers on "flexbox layout in
C#", "compute layout without a UI framework", "Yoga layout for .NET",
"position/size a tree of boxes", etc.
- [x] Core model section: build a `Node` tree (`InsertChild`), set styles via
`Node.Style`, run `CalculateLayout.Calculate(root, availW, availH, Direction)`,
read results from `Node.Layout` (`GetPosition(PhysicalEdge)`,
`GetDimension(Dimension)`); `float.NaN` = undefined/unconstrained
- [x] CSS-to-C# mapping table: every CSS flexbox property to its C# call
(`width: 100px` -> `SetDimension(Dimension.Width, StyleSizeLength.Points(100))`,
`margin: 10px` -> `SetMargin(Edge.All, StyleLength.Points(10))`,
`gap` -> `SetGap(Gutter.All, ...)`, enums for direction/justify/align/wrap/
position-type/overflow/display/box-sizing)
- [x] Gotchas section (the things an agent will get wrong):
- Defaults are Yoga's, not web CSS: `flex-direction: column`, `flex-shrink: 0`,
`align-content: flex-start`; `Config.UseWebDefaults` for web behavior
- `StyleLength` vs `StyleSizeLength` (edges/gaps vs dimensions/flex-basis)
- Owner semantics: a child belongs to one parent; re-inserting an owned child
throws (`YogaAssertException`) - `RemoveChild` first
- Style mutations auto-dirty the tree; re-call `Calculate` to get fresh results
- Positions are relative to the parent; accumulate for absolute coordinates
- [x] Recipes: sidebar+content shell, wrapping card grid with gap, centered overlay
via absolute insets, RTL, custom measured leaf (SetMeasureFunc with
MeasureMode semantics), incremental re-layout loop
- [x] Verify every code example compiles and produces the outputs shown (reuse the
readme-example verification approach; `samples/layout-demo/layout-demo.cs` is
a source of known-good snippets)
- [x] Point to the visual demo and the Generated conformance tests as further
reference material
- [ ] (deferred to task 143) Publication to https://timewarp.software/ happens automatically once the repo
qualifies for the site's catalog: the site aggregates `skills/*/SKILL.md` from
family repos' default branches at build time. Repo qualification and the
release-triggered rebuild are task 143's scope; verify the skill appears on the
site's skills index after both land.

## Notes

- The audit scaffold already created `skills/` (currently only `.gitkeep`).
- Keep the skill self-contained: agents may load it without repo access, so inline
the essential API surface rather than deferring to source files.
- The `description` frontmatter drives triggering accuracy - study the sibling
descriptions (terminal, nuru, amuru) for phrasing that names both the library and
the generic tasks it solves.

## Results (2026-07-04)

`skills/flexbox/SKILL.md` created (~270 lines), following the sibling
convention (terminal/nuru/amuru): trigger-tuned frontmatter, When to Use What
table, core five-step model, full CSS-to-C# mapping table (both length types),
Yoga-vs-web defaults table with the UseWebDefaults escape hatch, reading
results + AbsolutePosition helper, six recipes (app shell, wrap grid, absolute
overlay, RTL, measure function with MeasureMode semantics, incremental
re-layout), pitfalls (owner semantics, NaN, length types, one-config-per-tree),
installation, and further-reference pointers.

Every code snippet was compiled and executed against the library; all output
comments in the skill are actual engine output (verified via a scratch console
consumer). Site publication happens automatically once task 143 lands (the
site aggregates skills/*/SKILL.md from qualifying repos at build time).
Loading
Loading