Skip to content

Commit 6cfde07

Browse files
author
Claude (Initial Force WPF Bot)
committed
fix(packaging): use net10.0 TFM (not net10.0-windows)
Two real bugs from the first real-build run: 1. NU1012: 'lib/net10.0-windows/_._ is included under TFMs which are missing a platform version'. The .NET SDK requires a Windows OS version in any net10.0-windows TFM (e.g. net10.0-windows10.0.17763.0). Upstream WPF DLLs are compiled for net10.0 (no -windows suffix); the wrapper packages should match. Switched to net10.0 in: - InitialForce.WPF.csproj - InitialForce.WPF.RuntimeOverride.csproj - InitialForce.WPF.targets (_IFWpfRuntimeDir path) - InitialForce.WPF.RuntimeOverride.props (_IFWpfRORuntimeDir path) - build.yml staging path - lib/net10.0-windows/ directories renamed to lib/net10.0/ 2. WindowsBase + System.Xaml were located in PresentationCore.Tests/ bin output (Tests reference assemblies, not the real ones). Tightened the locate-DLLs PowerShell to only search artifacts/bin/<name>/<arch>/ and explicitly exclude ref/ and obj/ paths. Test fixes: search-and-replace 'net10.0-windows' to 'net10.0' in test_initial_force_wpf_csproj.py and test_runtime_override_csproj.py to match the new TFM. 779 tests still pass.
1 parent efcc35f commit 6cfde07

20 files changed

Lines changed: 2470 additions & 33 deletions

.github/workflows/build.yml

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,21 @@ jobs:
200200
$names = @("PresentationCore", "PresentationFramework", "WindowsBase", "System.Xaml")
201201
$found = @{}
202202
foreach ($n in $names) {
203-
$hit = Get-ChildItem -Recurse artifacts -Filter "$n.dll" -ErrorAction SilentlyContinue |
204-
Where-Object { $_.FullName -match "Release" -and $_.FullName -notmatch "\\ref\\|\\obj\\" } |
203+
# Canonical path: artifacts\bin\<exact-name>\<arch>\Release\<tfm>\<name>.dll
204+
# Restrict the search to the assembly's own bin tree so Tests/ref/obj
205+
# copies cannot be picked up by accident.
206+
$root = "artifacts\bin\$n\${{ matrix.arch }}\Release"
207+
if (-not (Test-Path $root)) {
208+
Write-Error "Output dir $root does not exist; upstream build did not produce $n"
209+
Get-ChildItem -Recurse artifacts\bin -Filter "$n.dll" | Format-Table FullName
210+
exit 1
211+
}
212+
$hit = Get-ChildItem -Recurse $root -Filter "$n.dll" -ErrorAction SilentlyContinue |
213+
Where-Object { $_.FullName -notmatch "\\ref\\|\\obj\\" } |
205214
Select-Object -First 1
206215
if (-not $hit) {
207-
Write-Error "Could not locate $n.dll under artifacts/"
208-
Get-ChildItem -Recurse artifacts -Filter "*.dll" | Select-Object -First 30 | Format-Table FullName
216+
Write-Error "Could not locate $n.dll under $root"
217+
Get-ChildItem -Recurse $root -Filter "*.dll" | Format-Table FullName
209218
exit 1
210219
}
211220
Write-Host "Found $n.dll at $($hit.FullName)"
@@ -221,7 +230,7 @@ jobs:
221230
- name: Stage DLLs into InitialForce.WPF runtime tree
222231
run: |
223232
$rid = if ("${{ matrix.arch }}" -eq "x64") { "win-x64" } else { "win-arm64" }
224-
$dest = "packaging\InitialForce.WPF\runtimes\$rid\lib\net10.0-windows"
233+
$dest = "packaging\InitialForce.WPF\runtimes\$rid\lib\net10.0"
225234
New-Item -ItemType Directory -Path $dest -Force | Out-Null
226235
foreach ($pair in @(
227236
@{ name = "PresentationCore"; path = "$env:PRESENTATIONCORE_DLL" },
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# Round 1 — Agent A: New Files Inventory
2+
**Branch:** `if/main` vs `upstream/release/10.0`
3+
**Total added files:** 159
4+
5+
---
6+
7+
## Summary Table
8+
9+
| Category | Path glob | File count | Purpose |
10+
|---|---|---|---|
11+
| GitHub CI/CD | `.github/workflows/` | 11 workflows | Fork-specific GitHub Actions automation pipeline |
12+
| GitHub meta | `.github/CODEOWNERS`, `.github/ISSUE_TEMPLATE/` | 5 | Ownership rules + issue templates for the autonomous pipeline |
13+
| Fork config | `.if-fork/` | 14 | Policy YAML, patch ledger, Claude prompt library |
14+
| Python tools | `tools/` | 19 | CLI scripts powering CI workflows (ledger signing, review, regression checks) |
15+
| Python tests | `tests/` | 55 | pytest suite verifying all `tools/` scripts and CI workflow contracts |
16+
| .NET smoke tests | `test/InitialForce.WpfSmoke/` | 22 | NUnit smoke + BenchmarkDotNet perf harness for the WPF fork |
17+
| .NET hello-world app | `test/InitialForce.WpfHelloWorld/` | 6 | Minimal WPF app used as a build/smoke sanity target |
18+
| NuGet packaging | `packaging/` | 11 | `InitialForce.WPF` and `InitialForce.WPF.RuntimeOverride` package projects |
19+
| Documentation | `docs/` | 8 | Operator runbook, risk register, decision log, known limitations |
20+
| Perf baseline | `perf/` | 1 | Example BenchmarkDotNet JSON baseline for regression checks |
21+
| Root-level | `NOTICE.md`, `pyproject.toml` | 2 | Legal attribution + Python project descriptor |
22+
23+
---
24+
25+
## Group Details
26+
27+
### `.github/workflows/` — 11 files
28+
CI/CD workflows implementing the fully autonomous upstream-PR review-and-merge pipeline.
29+
30+
| File | Description |
31+
|---|---|
32+
| `build.yml` | Builds the fork on every PR to `if/main`/`if/staging` |
33+
| `nightly-rebase.yml` | Scheduled (03:07 UTC daily) rebase onto `upstream/release/10.0` |
34+
| `pr-discovery.yml` | Scans upstream `dotnet/wpf` for new community PRs and creates ledger entries |
35+
| `pr-ingestion.yml` | Triggered by `pr-discovered` dispatch; pulls PR diff and kicks off review |
36+
| `pr-review.yml` | Runs 2× independent Claude Opus reviewers, then merges verdicts |
37+
| `release.yml` | Packs and publishes `InitialForce.WPF` NuGet packages (gated to `@oysteinkrog`) |
38+
| `upstream-stable-adoption.yml` | Applies PRs that graduated from staging to `if/main` |
39+
| `weekly-differential.yml` | Weekly diff of fork vs upstream to surface drift |
40+
| `autonomy-check.yml` | Periodic health-check of the autonomous pipeline itself |
41+
| `test-autonomy-check.yml` | CI job that runs the pytest suite for `tools/` |
42+
| `claude-on-failure.yml` | Fires Claude analysis on any failed workflow run |
43+
44+
### `.github/ISSUE_TEMPLATE/` — 4 files + `CODEOWNERS`
45+
Structured issue templates used by automated workflows to report pipeline events to human operators.
46+
47+
| File | Description |
48+
|---|---|
49+
| `cherry-pick-failure.yml` | Filed when a graduated PR fails to apply cleanly |
50+
| `perf-regression.yml` | Filed when BenchmarkDotNet detects a regression vs baseline |
51+
| `review-disagreement.yml` | Filed when the two Opus reviewers cannot reach consensus |
52+
| `operator-followup.yml` | General escalation template for items requiring human action |
53+
| `CODEOWNERS` | Enforces `@oysteinkrog` review for policy files, ledger, prompt library, and release/review workflows |
54+
55+
### `.if-fork/` — 14 files
56+
Fork-specific configuration and Claude prompt library.
57+
58+
| File | Description |
59+
|---|---|
60+
| `config.yaml` | Canonical policy file: denylist, tier thresholds, allowlists; all Claude workflows read this |
61+
| `patch-ledger.jsonl` | Append-only hash-chained JSONL ledger recording every discovered/reviewed/applied PR |
62+
| `patch-state.json` | Regenerated summary of current ledger state (graduated/applied counts by tier) |
63+
| `seed-input.json` | Bulk-import seed list of upstream PRs to pre-populate the ledger |
64+
| `prompts/preamble.md` | 12 hard prohibitions inherited by every Claude prompt in this repo |
65+
| `prompts/pr-review-1.md`, `pr-review-2.md` | Independent reviewer prompts (roles differ to avoid anchoring) |
66+
| `prompts/cherry-pick.md` | Prompt driving the graduation/cherry-pick workflow |
67+
| `prompts/rebase.md` | Prompt for the nightly rebase job |
68+
| `prompts/failure-analysis.md` | Prompt for post-failure root-cause analysis |
69+
| `prompts/release-notes.md` | Prompt generating release notes from ledger diffs |
70+
| `prompts/resolve-rebase-conflict.md` | Prompt for conflict resolution during rebase |
71+
| `prompts/pr-discovery.md` | Prompt for scanning upstream and scoring new PRs |
72+
| `prompts/audit-logger.md` | Prompt for structured audit logging of Claude actions |
73+
74+
### `tools/` — 19 files
75+
Python and shell scripts that implement the logic referenced by GitHub Actions workflows.
76+
77+
| File | Description |
78+
|---|---|
79+
| `ledger-event.py` | Appends a signed, hash-chained event to `patch-ledger.jsonl` |
80+
| `ledger-validate.py` | Validates ledger hash chain integrity |
81+
| `ledger_schema.py` | Shared schema constants (valid event types, required fields) |
82+
| `merge-verdicts.py` | Combines two reviewer JSON verdicts → `approved`/`escalated`/`rejected` |
83+
| `check-regression.py` | Compares BenchmarkDotNet JSON vs baseline; exits non-zero on regression |
84+
| `check-graduated.py` | Checks whether a PR has reached graduation tier in the ledger |
85+
| `check-denylist.py` | Checks a PR diff against the config denylist patterns |
86+
| `check-config-schema.py` | Validates `config.yaml` against `config-schema.json` |
87+
| `check-prompt-schema.py` | Validates that all `.if-fork/prompts/*.md` files declare required headers |
88+
| `diff-smoke-results.py` | Diffs fork vs upstream smoke test XML results; exits non-zero on regressions |
89+
| `dispatch-approved.py` | Fires a `repository_dispatch` to trigger the graduation workflow |
90+
| `regenerate-state.py` | Rebuilds `patch-state.json` from the full ledger |
91+
| `seed-ledger.py` | Seeds the ledger from `seed-input.json` (one-time bootstrap) |
92+
| `config-schema.json` | JSON Schema for `config.yaml` |
93+
| `cherry-pick-pre-flight.sh` | Bash: detects if upstream PR is already absorbed before cherry-pick |
94+
| `compute-version.ps1` | PowerShell: computes NuGet package version from git history |
95+
| `verify-msquic-pattern.ps1` + `.Tests.ps1` | PowerShell: verifies msquic usage pattern; Pester test |
96+
97+
### `tests/` — 55 files
98+
pytest suite providing unit and integration coverage for all `tools/` scripts and workflow contracts.
99+
100+
| Sub-group | File count | Coverage |
101+
|---|---|---|
102+
| `tests/fixtures/` | 23 | JSON/JSONL/XML/YAML/patch fixture files for deterministic test inputs |
103+
| `tests/test_ledger_*.py` | 4 | Ledger schema, event structure, hash-chain validation, state regeneration |
104+
| `tests/test_*_workflow.py` | 13 | Contract tests for each GitHub Actions workflow (event names, gate chains, required steps) |
105+
| `tests/test_check_*.py` | 5 | Unit tests for `check-*` tools |
106+
| `tests/test_*.py` (other) | 10 | Merge-verdicts, diff-smoke-results, cherry-pick pre-flight, csproj validation |
107+
| `conftest.py` | 1 | Pytest autouse fixtures normalising CI vs local environment |
108+
109+
### `test/InitialForce.WpfSmoke/` — 22 files
110+
NUnit smoke test suite and BenchmarkDotNet perf harness running against the actual WPF fork assemblies.
111+
112+
| File | Description |
113+
|---|---|
114+
| `Smoke/SmokeBase.cs` | NUnit base class: STA-thread application host for all smoke tests |
115+
| `Smoke/PixelDiffHelper.cs` + `PixelDiffTests.cs` | Pixel-level screenshot comparison (golden-image regression detection) |
116+
| `Smoke/AnimationTests.cs` | Verifies WPF animation timing and completion |
117+
| `Smoke/DataBindingTests.cs` | Data-binding correctness tests |
118+
| `Smoke/VirtualizingPanelTests.cs` | Virtualizing panel layout correctness |
119+
| `Smoke/FrugalListTests.cs`, `ListCollectionViewTests.cs`, etc. | Targeted regression guards for fork-specific optimizations |
120+
| `Perf/PerfHarness.cs` + `PerfProgram.cs` | BenchmarkDotNet harness; results fed to `check-regression.py` |
121+
| `Perf/BenchmarkConfig.cs` | BenchmarkDotNet configuration (warmup, iterations, exporters) |
122+
123+
### `test/InitialForce.WpfHelloWorld/` — 6 files
124+
Minimal single-window WPF application used as a build and smoke sanity target.
125+
126+
### `packaging/` — 11 files
127+
Two SDK-style NuGet package projects with no source code — they package the fork's WPF assemblies.
128+
129+
| Package | Description |
130+
|---|---|
131+
| `InitialForce.WPF` | Primary consumer package; delivers fork WPF assemblies via `buildTransitive` MSBuild props/targets |
132+
| `InitialForce.WPF.RuntimeOverride` | Companion package that redirects the .NET runtime to use the fork's assemblies at runtime |
133+
134+
### `docs/` — 8 files
135+
136+
| File | Description |
137+
|---|---|
138+
| `operator-runbook.md` | Steady-state operations guide (6–10 h/month estimate, incident response) |
139+
| `DECISION_LOG.md` | Append-only log of architectural/governance decisions (human + Claude) |
140+
| `KNOWN_RISKS.md` | Detailed risk catalogue with mitigation strategies |
141+
| `risk-register.md` | Condensed on-call cheat sheet mapping risks to detection signals and response steps |
142+
| `BOOTSTRAP_STATUS.md` | Current status of the initial fork bootstrap process |
143+
| `autonomy-check-usage.md` | Usage guide for the autonomy-check workflow |
144+
| `known-limitations.md` | Documented limitations of the autonomous pipeline |
145+
| `manual-candidates.md` | PRs identified as requiring manual review rather than autonomous processing |
146+
147+
### Root-level and misc — 3 files
148+
149+
| File | Description |
150+
|---|---|
151+
| `NOTICE.md` | MIT license attribution acknowledging `dotnet/wpf` and .NET Foundation origin |
152+
| `pyproject.toml` | Python project descriptor for `wpf-fork-tools`; declares dependencies for `tools/` scripts |
153+
| `perf/baseline-example.json` | Example BenchmarkDotNet JSON baseline (Intel i7-10700K, .NET 10, Windows 11) |
154+
155+
---
156+
157+
## Notes
158+
159+
- The `data/` directory exists locally (contains review reports from this session) but has **no files tracked in `if/main`** — it is ephemeral/gitignored and should not be documented as a fork artifact.
160+
- All 159 added files are unique to the InitialForce fork; none appear in `dotnet/wpf release/10.0`.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Round-1 Agent B: Modified & Deleted Files Inventory
2+
3+
**Scope:** `git diff --name-status upstream/release/10.0..if/main`
4+
**Focus:** M (modified) and D (deleted) paths only — new files (A) are covered by Agent A.
5+
6+
---
7+
8+
## Summary
9+
10+
| Category | Count |
11+
|---|---|
12+
| Modified files (M) | 3 |
13+
| Deleted files (D) | 5 |
14+
| Fork commits | 24 |
15+
16+
---
17+
18+
## Modified Files
19+
20+
### 1. `.github/ISSUE_TEMPLATE/config.yml``+2 / -21`
21+
22+
Stripped all upstream contact links (dotnet/runtime, winforms, efcore, roslyn, aspnetcore, SDK) and disabled blank issues. Replaced with a two-line stub: `blank_issues_enabled: false` / `contact_links: []`. The fork has its own issue templates (cherry-pick-failure, operator-followup, perf-regression, review-disagreement) added as new files alongside this change.
23+
24+
### 2. `.gitignore``+10 / -0`
25+
26+
Appended a new section ("Initial Force WPF fork additions") adding Python toolchain artifacts: `__pycache__/`, `*.pyc`, `*.pyo`, `.pytest_cache/`, `.mypy_cache/`, `.ruff_cache/`, `.venv/`, `*.egg-info/`. These correspond to the Python-based CI tooling (`pyproject.toml`, `tests/` directory) added by the fork. The upstream content is untouched.
27+
28+
### 3. `README.md``+4 / -0`
29+
30+
Prepended a 4-line fork banner at the very top of the file:
31+
32+
```
33+
> **Initial Force WPF fork** — this is a community fork of `dotnet/wpf` maintained by
34+
> [Initial Force AS](https://initialforce.com) for our internal use, with additional
35+
> community PRs cherry-picked under a 2× independent automated review gate.
36+
> See [`docs/BOOTSTRAP_STATUS.md`](docs/BOOTSTRAP_STATUS.md) and
37+
> [`NOTICE.md`](NOTICE.md) for details. The upstream README follows.
38+
```
39+
40+
Followed by a horizontal rule. All upstream content is preserved verbatim below the separator.
41+
42+
---
43+
44+
## Deleted Files
45+
46+
| Path | Upstream lines | Reason for deletion |
47+
|---|---|---|
48+
| `.github/PULL_REQUEST_TEMPLATE.md` | 23 | Upstream PR template for dotnet/wpf contributors. Deleted because the fork does not accept external PRs in the same manner; the autonomous workflow uses `pr-ingestion.yml` instead. |
49+
| `.github/workflows/backportPRs.yml` | 28 | Upstream workflow to backport PRs between dotnet/wpf branches via `/backport` comments. Not applicable — the fork has its own cherry-pick pipeline (`pr-ingestion.yml`, `pr-discovery.yml`). |
50+
| `.github/workflows/locker.yml` | 36 | Upstream workflow that locked stale issues/PRs daily. The fork's issue lifecycle is managed differently (autonomous-check / operator-followup templates). |
51+
| `.github/workflows/main.yml` | 13 | Upstream inter-branch merge workflow (triggered on push to `release/**`). Superseded by the fork's `build.yml` and `nightly-rebase.yml`. |
52+
| `CODEOWNERS` | 6 | Upstream file assigning `@dotnet/wpf-developers` as default reviewer for all paths. Replaced by `.github/CODEOWNERS` (a new file added by the fork, covering fork-specific paths and the Initial Force team). |
53+
54+
---
55+
56+
## Fork Commit Log — 24 commits grouped by theme
57+
58+
### Bootstrap (1 commit)
59+
- `958b648` Bootstrap Initial Force WPF autonomous-fork tooling
60+
61+
### Security / hardening fixes (12 commits — largest theme)
62+
- `fc915e0` fix(pr-review.yml): kill-switch enforcement, merge-verdict needs gate, fail-closed double-review (CRIT-1/4, HIGH-1)
63+
- `8fd70da` fix(build.yml): drop invalid job-level runner.os reference (was failing GA parse)
64+
- `490d84c` fix(pr-ingestion.yml): kill-switch + ledger CLI + SHA smuggle + denylist range + needs + run race + automerge default (CRIT-1/2, HIGH-3..6, MED-6)
65+
- `81c68a8` fix(check-regression.py): zero-baseline, new-scenarios, empty-comparison fail-closed (MED-1)
66+
- `75da9cb` fix(release.yml + autonomy-check.yml): kill-switch + ledger CLI + tag reach/sig + needs + sha256 + default-deny (CRIT-1/2, HIGH-5/7, MED-3, LOW-3)
67+
- `cefd5d2` fix(packaging): RID-condition WPF asset removal, fail-closed for unsupported RIDs, drop OverwriteReadOnlyFiles (HIGH-8, LOW-2)
68+
- `dd185e6` fix(ledger): push commits, fail-closed GPG in CI, shared schema, line-hash trailer (CRIT-2/3, HIGH-2, MED-4/5, LOW-1)
69+
- `7dcc5c2` test: align build_workflow + smoke_harness tests with post-fixup state
70+
- `28c02e8` fix(ledger_schema): add rebase_failed, failure_analyzed, pre_flight_failed, review_single_path_warning event types
71+
- `faaf9fa` test: add behavioral test for workflow event names match VALID_EVENTS schema (Review-6 finding)
72+
- `886f107` fix(ledger-event): handle single-line ledger + rebase-conflict in retry (Review-4 findings)
73+
- `d55e876` fix(workflows): gate-in-needs invariant + ledger CLI + HIGH-6 run-watch + event mismatches + verdict CLI + concurrency (CRIT-1/2, HIGH-6, multiple)
74+
75+
### Docs / operator runbook (1 commit)
76+
- `cdee60d` docs(operator-runbook): fix command errors (I-1 NuGet scope, I-3/I-10/I-7/I-2 args, label OR search, version_override->tag, recovery procedures)
77+
78+
### Test / schema (4 commits)
79+
- `bfe14ea` test(event-schema): document operator-only/Phase-0 future-use events as unused-OK
80+
- `a3783e5` fix: scrub local-dev path leaks before public push
81+
- `d04c26b` chore: remove inherited upstream workflows (not relevant to autonomous fork)
82+
- `75da9cb` *(also counted above)*
83+
84+
### Python / CI tooling (4 commits)
85+
- `56f5196` fix(ci): types-PyYAML, mypy tools-only, build placeholder marker, build-and-test needs cherry-pick
86+
- `66c614a` style: wrap long lines to satisfy ruff E501 (line-length=100)
87+
- `4b29772` fix(ci): use RSA-2048 for ephemeral GPG key (ed25519 batch syntax invalid)
88+
- `f0fe81c` fix(ci): add jsonschema, ephemeral GPG key for ledger tests
89+
- `3b174ba` fix(build.yml): make Strawberry Perl install idempotent
90+
- `f75f8b0` test(conftest): scrub CI env per-test by default
91+
- `64b0ad2` fix(conftest): remove unused 'import os'
92+
93+
> Note: some commits span multiple themes; the grouping above reflects their primary focus.
94+
95+
---
96+
97+
## Key Observations
98+
99+
1. **No WPF source files were modified or deleted.** Every M/D path is in `.github/` or a top-level meta-file. The fork leaves all of `src/`, `eng/`, and WPF runtime code untouched relative to `upstream/release/10.0`.
100+
101+
2. **Deletions are upstream-workflow cleanup.** The five deleted files are all upstream GitHub automation (PR template, backport bot, locker, inter-branch merge, dotnet CODEOWNERS) that have no relevance to an autonomous commercial fork.
102+
103+
3. **Modifications are additive or narrowing.** README and `.gitignore` are purely additive. The issue template config is narrowed (blank issues disabled, upstream contact links removed).
104+
105+
4. **24 commits are exclusively fork infrastructure.** All commits concern CI/CD workflows, Python test harness, packaging, ledger/audit tooling, and documentation — zero upstream WPF code changes introduced by the fork itself.

0 commit comments

Comments
 (0)