Skip to content

[Draft] Readme WIP - #1

Merged
davidortinau merged 1 commit into
masterfrom
davidortinau/readme
May 14, 2020
Merged

davidortinau merged 1 commit into
masterfrom
davidortinau/readme

Conversation

@davidortinau

Copy link
Copy Markdown
Contributor

No description provided.

@davidortinau
davidortinau requested a review from samhouts May 11, 2020 15:29
@davidortinau davidortinau self-assigned this May 11, 2020
@davidortinau
davidortinau marked this pull request as ready for review May 14, 2020 16:15
@davidortinau
davidortinau merged commit 1de3d17 into master May 14, 2020
@PureWeen
PureWeen deleted the davidortinau/readme branch August 27, 2020 19:54
PureWeen pushed a commit that referenced this pull request Feb 4, 2021
Fix DeviceInfo: Returning wrong device name
mattleibow pushed a commit that referenced this pull request Jun 18, 2022
* Initial commit

* Got windows projects compiling

* First pass as removing un-used code

* Moved portable graphics code into Xamarin.Graphics.Core

* More renaming.

* More renaming.

* More renaming.

* Fixed shared project issue.

* More renaming.

* More renaming.

* More cleanup/refactoring.

* Removed stroke location.

* More naming cleanups

* More cleanup.

* Switched to structs for Size, Rectangle and Point

* Cleanup Android project.

* Cleanup mac naming.

* Fixed iOS naming.

* Scrubbed elevenworks from files.

* More scrubbing

* More cleanup

* Renamed files

* Renamed files.
jonathanpeppers added a commit that referenced this pull request Sep 14, 2022
/Users/builder/azdo/_work/3/s/bin/dotnet/packs/Microsoft.MacCatalyst.Sdk/15.4.1153-rc.2/targets/Xamarin.Shared.Sdk.targets(371,3): error : Unable to merge the file 'Contents/Resources/Assets.car', it's different between the input app bundles. [/Users/builder/azdo/_work/3/s/src/Controls/samples/Controls.Sample.Sandbox/Maui.Controls.Sample.Sandbox.csproj::TargetFramework=net7.0-maccatalyst]
/Users/builder/azdo/_work/3/s/bin/dotnet/packs/Microsoft.MacCatalyst.Sdk/15.4.1153-rc.2/targets/Xamarin.Shared.Sdk.targets(371,3): error : App bundle file #1: /Users/builder/azdo/_work/3/s/src/Controls/samples/Controls.Sample.Sandbox/bin/Release/net7.0-maccatalyst/maccatalyst-x64/Maui.Controls.Sample.Sandbox.app/Contents/Resources/Assets.car [/Users/builder/azdo/_work/3/s/src/Controls/samples/Controls.Sample.Sandbox/Maui.Controls.Sample.Sandbox.csproj::TargetFramework=net7.0-maccatalyst]
/Users/builder/azdo/_work/3/s/bin/dotnet/packs/Microsoft.MacCatalyst.Sdk/15.4.1153-rc.2/targets/Xamarin.Shared.Sdk.targets(371,3): error : App bundle file #2: /Users/builder/azdo/_work/3/s/src/Controls/samples/Controls.Sample.Sandbox/bin/Release/net7.0-maccatalyst/maccatalyst-arm64/Maui.Controls.Sample.Sandbox.app/Contents/Resources/Assets.car [/Users/builder/azdo/_work/3/s/src/Controls/samples/Controls.Sample.Sandbox/Maui.Controls.Sample.Sandbox.csproj::TargetFramework=net7.0-maccatalyst]
@github-actions github-actions Bot locked and limited conversation to collaborators Dec 27, 2023
JanKrivanek added a commit that referenced this pull request Apr 15, 2026
- Fix #1 (CRITICAL): Shell injection in evaluate job - use env var
  indirection for skills_path and rename GITHUB_TOKEN to COPILOT_TOKEN
- Fix #4: PowerShell here-string injection - use env:SKILL_DIRS
- Fix #5: Code-fence injection - escape triple backticks in output
- Fix #6: Delete old agent .md files (keep .agent.md versions)
- Fix #7: Shell injection in static-check heredoc - use env var
- Fix #8: Direct expression interpolation - use env: block for event props
- Fix #9: Heredoc EOF spoofing - use random delimiter
- Fix #10: Missing pipe-escaping for skillName/scenarioName
- Fix #11: Renamed Copilot token env var to COPILOT_TOKEN
- Fix #13: Token rotation entropy - mix in github.run_id
- Fix #14: Fix misleading inline comment for icon logic
Alex-Tsvetanov added a commit to Alex-Tsvetanov/maui that referenced this pull request Jul 2, 2026
Catalyst reuses the iOS handlers, so fixes dotnet#1 (button measure), dotnet#2 (button/radio
font 14pt), dotnet#4 (shape HOrizontalOptions=Start) and dotnet#5 (pointer_gesture FontSize=24)
carry over. Verified cpp-vs-MAUI on Catalyst:
  - shapes:          pixel-perfect match (left-aligned Ellipse/RoundRect/pentagram/Line)
  - pointer_gesture: pixel-perfect match (24pt labels)
  - button:          text-button font/sizing now match MAUI
Recaptured all 3 columns (cpp/maui/xaml) for: button image_button radio_button_group
path_gallery rectangle_gallery composition_gallery shapes transform_playground
pointer_gesture.

Known remaining macOS-specific diff (flagged): image_button 'settings' renders too
tall on Catalyst (unconstrained image height) vs MAUI's compact bar.
PureWeen added a commit that referenced this pull request Jul 16, 2026
…ry inline-findings.json write (#36002)

> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## What & why

Fixes to the Copilot PR-reviewer pipeline, driven by analyzing **64
recent `maui-copilot` PR-reviewer CI sessions** (most-recent run per PR,
last 7 days). Three independent analysis agents mined disjoint batches
and **converged on the same top two systemic issues** — strong signal
these are structural, not anecdotal. Fixes #1 and #2 are
**prompt/instruction-only**; the PR additionally ships
**regression-guard evals** for both, plus a **token-auth migration** of
the eval harness workflow (see Scope & safety).

### 1. `gh` CLI is unauthenticated by design, but the prompt doesn't say
so — ~61/64 sessions
The `CopilotReview` task intentionally strips all GitHub tokens
(`copilot ...
--secret-env-vars=GH_TOKEN,COPILOT_GITHUB_TOKEN,GITHUB_TOKEN`), so `gh
pr view` / `gh issue view` / `gh api` always fail inside the agent run.
But `pr-preflight.md` still *leads* with those commands, so nearly every
session burns 1-3 turns watching them fail and pivoting — and several
then **wrongly log an "environment blocker" or lower review
confidence**.

**Fix:** `pr-preflight.md` now opens with an explicit "Environment &
Authentication" note stating that unauthenticated `gh` is **expected,
not a blocker** (don't stop, don't reduce confidence), and provides
**local-`git` + unauthenticated public-REST (`curl`)** recipes that work
in CI. The original `gh` recipes are retained for local `pr-review` runs
where a token is present.

### 2. Agent refuses to write the REQUIRED `inline-findings.json` —
~34/64 sessions
The expert-review step over-generalizes a host "do not write output
files" guardrail and refuses to write `inline-findings.json`, dumping
the JSON into chat instead. There is **no fallback** —
`post-inline-review.ps1` finds no file (`Test-Path` fails) and **inline
review comments are silently dropped** in roughly half of sessions. This
is the most damaging correctness issue found.

**Fix:** the STEP 5b prompt in `Review-PR.ps1` and the
`maui-expert-reviewer` agent now **explicitly authorize and mandate**
writing the file, state that the general "don't write review output"
guidance does **not** apply to this required artifact, and forbid
substituting a chat-text dump (with an orchestrator-writes-it-itself
fallback instruction).

## Files
- `.github/pr-review/pr-preflight.md` — environment note + local-first /
`curl` recipes (#1)
- `.github/scripts/Review-PR.ps1` — strengthened STEP 5b inline-findings
mandate (#2, prompt string only)
- `.github/agents/maui-expert-reviewer.md` — reinforce required file
write, no chat-dump (#2)
- `.github/skills/pr-review/tests/eval.gh-auth.vally.yaml` — **new**
regression guard for #1
- `.github/skills/code-review/tests/eval.inline-findings.vally.yaml` —
**new** regression guard for #2
- `.github/workflows/skill-validation.yml` — **token-auth migration**
for the eval harness (see Scope & safety)

## Scope & safety
- **Fixes #1 and #2 are prompt/instruction text only.** No pipeline
control-flow or security-boundary changes — the #1 note merely
*documents* the existing token-stripping (reinforces it, never weakens
it). `Review-PR.ps1`'s change is confined to a here-string prompt;
`pwsh` parse check passes and `$PRNumber` interpolation is preserved.
- **`skill-validation.yml` is a token-provisioning migration (not prompt
text).** The prior `COPILOT_GITHUB_TOKEN*` secrets were rotated out with
the removal of `gh-aw-agents`, so the eval harness now sources Copilot
auth from the **`copilot-pat-pool`** environment (`COPILOT_PAT_0..9`,
index/run-id modulo selection, masked before `GITHUB_OUTPUT`). This is a
**like-for-like** auth-source swap — same `pull_request_target` exposure
model as before, no new secret exposure and no security loosening. ⚠️
**Merge prerequisite:** the `copilot-pat-pool` environment must be
populated with `COPILOT_PAT_0..9` (and must NOT carry
required-reviewer/wait-timer protection rules, which would hang the
`pull_request_target` job) so eval goes green immediately after merge.

## Evals (regression guards for these fixes)
Now that we run
[`vally`](https://www.npmjs.com/package/@microsoft/vally-cli) eval
suites, each fix ships with a guard that reproduces the exact failure
mode it fixes. Both pass `vally lint --strict` and run on this PR (each
lives under its skill's `tests/` dir, which flags that skill as
changed).

- `.github/skills/pr-review/tests/eval.gh-auth.vally.yaml` (#1) — the
vally eval step is already tokenless, so it **natively reproduces** the
CI condition where `gh` is unauthenticated. The agent must classify that
as **expected, not a blocker**, pivot to the local-first
`git`/anonymous-REST path, and **not lower its review confidence**.
Structural floor: the agent must end with `GH_AUTH_BLOCKER: no` — a
*necessary, not sufficient* signal — with an LLM judge scoring the
reasoning.
- `.github/skills/code-review/tests/eval.inline-findings.vally.yaml`
(#2) — pins a worktree to a real regression commit so the agent has a
genuine diff, then must **write** `inline-findings.json` (canonical
`path`/`line`/`body` schema) to the path the pipeline reads from disk
and prove it landed (read back, echo `FILE_OK:[…`). An agent that
refuses (`"prohibited"`) and chat-dumps the JSON has no file to echo and
fails. An LLM judge scores finding quality, non-refusal, and requires
transcript evidence of an actual write tool call (not just a fabricated
`FILE_OK:` line).

Each suite keeps two graders — one structural floor + one LLM judge.
Both floors are *satisfiable by a partial regression* (the prompt hands
the agent the giveaway token), so `scoring.threshold` is set to **0.7**
(above the house 0.6): with the unweighted `mean(floor, judge_norm)`
aggregate, a floor-1.0-but-judge-failing run scores `(1.0 + 0.25)/2 =
0.625`, which 0.6 would **pass** but 0.7 **fails**. This makes the **LLM
judge load-bearing** (must reach ≥3/5) rather than letting the spoofable
floor decide. Live-validated: good path 1.00, partial-regression 0.625.

## Follow-ups (identified, not in this PR)
Same analysis surfaced: PowerShell-vs-bash redirection footguns in
`try-fix` recipes; test-result classification keyed off exit code
instead of TRX `failed=` count; winners crowned with no regression
evidence; gate=FAILED conflating real regressions with
wrong-platform/ineffective tests; and the agent re-deriving the true PR
diff base each run. These can be addressed in separate PRs.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jul 29, 2026
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Description of Change

Hardens both scheduled CI-fixer workflows after two production failures
following #36775:

- [main run
30269817669](https://github.com/dotnet/maui/actions/runs/30269817669)
lost all 50 broad-search issue bodies to the integrity filter, so the
agent had no usable fresh-candidate evidence.
- [net11 run
30269934433](https://github.com/dotnet/maui/actions/runs/30269934433)
prepared a valid one-file update for PR #36619, but capture-time
validation compared its branch with `main`. The resulting 3,377-file
stale-base divergence produced an oversized allowed-files request,
killed the Safe Outputs backend, and still left a green run with empty
output.

This change:

1. Builds a deterministic pre-agent snapshot containing only open issues
with the caller's exact label (`ci-scan` or `ci-scan-net11`), optionally
scoped to a dispatch issue number. Issue counts, titles, and bodies are
bounded and sanitized; title/body content is explicitly untrusted inert
data.
2. Removes broad live issue discovery from the agent tool surface and
makes the bounded snapshot authoritative.
3. Pins the capture-time MCP gateway base before startup with the
supported `pre-agent-steps` hook, while independently pinning apply-time
handlers. The net11 workflow now validates against `net11.0`, not
repository-default `main`.
4. Gates code transport on append-only ancestry, merge-free history,
allowed paths, at most 3 commits, 20 files, and 256 KiB. Existing PR
advances use the saved PR head so only the intended delta is
transported.
5. Registers required safe outputs before emission and reconciles them
with authoritative `agent_output.json` after the agent exits. Backend
errors, malformed expectations, or missing required captures fail the
agent job; a legitimate no-op has no mutation expectation and remains
green.
6. Adds hermetic PowerShell and Vally coverage for both incidents while
preserving the separate ownership gate.

### Follow-up hardening (commit `1564b2b8b2`)

A first adversarial review round found and fixed six defects in the work
above:

1. **Issue stranding.** The candidate query fetched a single
newest-first page bounded by `MaxIssues` (20). With 51 open `ci-scan`
and 58 open `ci-scan-net11` issues, the oldest issues could never enter
any run's window. The query is now oldest-first (`sort=created`,
`direction=asc`) and fully paginated, so bounding happens after the
complete eligible set is known. Only the bounded batch is emitted, so
the snapshot payload size is unchanged.
2. **Silent truncation.** The snapshot now reports `totalMatched` and
`truncated` alongside `count`, so a bounded batch can never be read as
"no other candidates exist".
3. **Unenforceable twin ownership.** The ownership rule was prose-only,
but the agent can no longer read raw labels once the `issues` toolset is
removed, so it was undecidable at runtime. Ownership is now decided
deterministically in the prefetch and reported as
`excludedDualLabelled`. (The first attempt applied exclusion
symmetrically and introduced a regression — corrected in `5bb908cade`
below.)
4. **Scoped-read hard failure.** A 404 on a dispatch-scoped issue read
aborted the entire prefetch. The scoped read now tolerates failure and
skips, instead of taking down discovery for every other candidate.
5. **`dry_run` false red.** Preview runs prepare mutation expectations
but intentionally emit nothing, so reconciliation failed a run that
behaved correctly. Reconciliation is now skipped on `dry_run=true` via a
host-evaluated Actions `if:` expression rather than a script switch, so
the guard cannot be disabled by the agent.
6. **Case-insensitive path matching.** The transport allowlist used
`-match`, so `src/core/...` passed a gate written for `src/Core/...`. It
now uses `-cmatch`, matching Git's case-sensitive path semantics.

### Follow-up regression fixes (commits `5bb908cade`, `80fe851229`)

A second adversarial review round audited the commit above and found two
further defects — one introduced by the round-1 fix, one latent in the
original work.

**`5bb908cade` — dual-labelled issues were stranded by both twins.**

Ownership is **asymmetric**: both prompts agree the net11 twin owns an
issue carrying *both* labels (`ci-status-fix.md` skips them because "the
net11.0 workflow owns those"; `ci-status-fix-net11.md` skips only issues
carrying `ci-scan` **but NOT** `ci-scan-net11`). But fix #3 wired the
exclusion **symmetrically**. Because net11's exact-label filter already
drops `ci-scan`-only issues, the sole net effect of its exclusion was to
drop the dual-labelled issues it owns — so such an issue was processed
by **neither** twin and stranded permanently, reproducing the very bug
class fix #1 closed. No dual-labelled issue exists today, so this was
latent rather than live.

The exclusion is removed from the net11 twin only; main's is correct and
necessary. The asymmetry is documented at both call sites. The original
test only exercised the main configuration — and a unit test cannot
observe a *wiring* mistake in the workflow source regardless — so both
layers were added: a unit test asserting the net11 configuration retains
dual-labelled issues, and a source-level parity guard asserting main
excludes `ci-scan-net11` while net11 passes no `-ExcludeIssueLabel` at
all.

**`80fe851229` — the transport gate rejected every FRESH create-PR.**

`Test-CiFixTransport.ps1` always bound `-PullRequestNumber` when calling
`Register-CiFixSafeOutputExpectation.ps1`, including on the
`create_pull_request` path where no PR exists yet and the parameter sits
at its unset default of `0`. The registrar declares `[ValidateRange(1,
[int]::MaxValue)]` on that parameter, so binding `0` fails during
parameter binding — before the registrar's own "non-PR types don't need
a number" branch (which lists `create_pull_request` as exactly such a
type) can run.

Every FRESH transport therefore died with `Cannot validate argument on
parameter 'PullRequestNumber'`, even for a valid one-file, one-commit,
append-only, in-allowlist diff. Both prompts invoke exactly that form,
so the fixer could not open a new PR at all. Advancing an existing PR
was unaffected, which is why the staged proofs — a no-op and an
existing-PR advance — never surfaced it. `-PullRequestNumber` is now
bound only when actually set.

This shipped undetected because no test ever exercised a *successful*
`create_pull_request`; the only such test asserts a rejection, and it
passed for an unrelated reason. A FRESH-path test now asserts the
transport succeeds, reports a null `pullRequestNumber`, and registers a
matching expectation.

Both regression fixes are mutation-verified: reverting either makes its
new test fail.

### Follow-up CI wiring (commit `9520f4407e`)

A third review round found that none of the Pester coverage above was
gated by CI: nothing under `.github/workflows` or `eng/pipelines`
referenced `Invoke-Pester` or these test files, so a regression in the
transport/expectation logic could only surface during a live scheduled
Actions run — exactly how `80fe851229` shipped.

The earlier deferral reason no longer held. It claimed a blanket gate
over `.github/scripts/**` would immediately fail on pre-existing
failures in `Fix-MilestoneDrift.Tests.ps1`; re-measured at this head the
full suite is **631/631 green**, and stays green with `gh` stubbed to
fail and both `GH_TOKEN` and `GITHUB_TOKEN` cleared. The suite is
hermetic, so it is safe to gate.

`.github/workflows/powershell-script-tests.yml` runs the whole
`.github/scripts` suite on any PR touching those paths:

- **`pull_request`, not `pull_request_target`.** The job executes
PowerShell authored by the PR, so it must run with no base-repo secrets
and a read-only token. `permissions: contents: read`, and checkout uses
`persist-credentials: false`.
- **Pester pinned to 5.9.0**, so an upstream release cannot silently
change discovery or assertion behavior.
- **The run step deliberately does not `Set-StrictMode`.** StrictMode
set in the host session leaks into every test body Pester dot-sources
and turns 16 otherwise-passing tests red for reasons unrelated to the
code under test. This was caught while validating the gate.
- **Fails on zero discovered tests**, so the gate cannot pass vacuously
if the path or filter ever breaks.

Mutation-verified: re-introducing the unconditional `-PullRequestNumber`
binding fails `accepts a FRESH create_pull_request transport that has no
PR number yet`, so the gate is load-bearing.

### Follow-up security-review fixes (commit `cb31b580af`)

A 4-model adversarial **security** review (#36842 review by @kubaflo)
ran against head `9520f4407e` and produced four findings. Each was
independently re-verified against that head before acting.

**1. `update_pull_request` was the one mutating handler not scoped to
this workflow's own PRs.** Every other mutating handler is locked to
`required-title-prefix` + `required-labels`; `update_pull_request` alone
shipped `target: "*"` with `allow_body: true` and neither constraint, so
a prompt-injected agent could replace the body of **any** PR in the
repo, up to `max: 3` per run. The tightened patch caps do not apply —
this is not a patch operation.

The source carried a NOTE asserting that gh-aw v0.82.14 "silently drops
`required-*`" for this output. **That claim is wrong.** Recompiling with
the repository-pinned compiler emits both keys into
`GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG`, and gh-aw additionally generates
the enforcement text `Only PRs with labels [agentic-workflows] can be
updated. Only PRs with title prefix "[ci-fix] " ...` into the agent
constraint string. `--strict` rejects unknown safe-output fields
(verified by compiling a deliberately bogus key), so these are
schema-supported rather than ignored. Both constraints are now set on
both twins, and the stale NOTE plus the matching Hard Rule 6 prompt text
are corrected.

**2. Reconciliation was one-directional and skippable.** It only
detected *registered-but-not-captured*, so extra captured items were
ignored; worse, it returned `exit 0` before checking anything when no
expectation existed — precisely the run shape an out-of-band emitter
produces. Reconciliation now also checks the reverse direction: for
every mutating output type, captured must not exceed registered, and
that check runs **even when the expectation set is empty**. Diagnostic
types (`missing_tool` / `missing_data` / `noop` / `report_incomplete`)
are excluded because they are emitted outside Hard Rule 11 and must not
redden a legitimate run.

The expectation set is now resolved once with a plain `find`. The
previous `find ... -print -quit | grep -q .` probe can surface a SIGPIPE
(141) under `pipefail`, which would read as "no expectations" and skip
reconciliation entirely — an exposure this change would otherwise have
multiplied.

**3. `-AllowFailure` made a transient outage look like "no ci-fix
work".** The scoped-dispatch and priority-watch issue reads collapsed
*every* non-transient failure to an empty result, so a
401/403/429/5xx/network failure was indistinguishable from "this issue
is not in scope". A new `-AllowNotFound` suppresses only a **confirmed
HTTP 404** and propagates everything else; both discovery reads now use
it. Broad `-AllowFailure` is retained only where the caller tracks its
own "known" flag and fails closed from it (`Get-HeadCheckState`,
`Get-PullRequestBody`).

**4. Mutable action reference.** `powershell-script-tests.yml` used
`actions/upload-artifact@v4` while SHA-pinning everything else; pinned
to `043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1`, the SHA already
used ~15× in this repo and recorded in the gh-aw manifest.

**Not actioned (with reasons).** The reviewer's suggestion to gate the
`safe_outputs` job on `needs.agent.result == 'success'` targets a
gh-aw-generated job condition (`if: (!cancelled()) && needs.agent.result
!= 'skipped' && ...`) that exists only in the compiled lock; changing it
would require hand-editing generated output, which this repo forbids.
The reviewer's own analysis agrees the real boundary is the
config-locked handler plus the read-only agent job, both unweakened
here. The unconfirmed `update_branch: false` → item-level-`true`
override remains unverified upstream behavior; the config sets `false`.

Locks were regenerated with the repository-pinned **gh-aw v0.82.14**; no
`.lock.yml` was hand-edited, and the lock diff is confined to the
metadata hashes, the three safe-output config copies, the generated
constraint string, and the reconciliation step.

### Security Model

- Issue titles and bodies are never executed or interpolated into
commands.
- Evidence is capped at 20 issues, 256 title characters, and 12,000 body
characters. Pagination widens only the in-memory eligibility scan, not
the emitted payload.
- No direct or post-agent GitHub write path was added; all writes still
use supported gh-aw Safe Outputs.
- Oversized, unrelated, stale-base, merge, or non-append-only transports
fail before a mutation output is emitted.
- **Scope of the 20-file gate.** gh-aw v0.82.14 rejects
`max-patch-files` on `push-to-pull-request-branch` and does not
propagate it from top-level `safe-outputs`, so the file-count bound is
enforced only by `Test-CiFixTransport.ps1` before emission. Treat it as
defense-in-depth, not as a privileged push-handler boundary. The
handler's own `max-patch-size` cap still applies and measures the
increment against `origin/<branch>`.
- **Scope of the expectation/reconciliation mechanism.** The agent can
write to `/tmp/gh-aw/`, so expectation files are cooperative-agent
diagnostics that catch backend failures and dropped captures — not a
boundary against a hostile agent. The privileged boundary remains
gh-aw's own safe-output handlers.
- **Scope of `dry_run`.** `dry_run=true` is an in-prompt preview
instruction, not gh-aw `staged:` mode, so it is not a framework-level
write barrier. For a preview that blocks GitHub API calls at the
framework level, use `gh aw trial`, as the workflow notes direct.

### Validation

- PowerShell/Pester: **657/657 passed** for the full `.github/scripts`
suite now gated in CI (also green with `gh` stubbed to fail and
`GH_TOKEN`/`GITHUB_TOKEN` cleared, confirming hermeticity). Coverage
includes the exact 3,377-file stale-base fixture and regression tests
for pagination/ordering, truncation totals, both directions of
twin-label ownership, twin wiring parity, scoped-404 skip,
case-sensitive path rejection, and the FRESH create-PR transport. The
`cb31b580af` round adds coverage that asserts the **compiled lock**
scopes every mutating handler to this workflow's own PRs, and that
**executes the compiled `post-steps:` reconciliation shell** against
fixtures — closing the previously-listed gap that this bash had no unit
coverage.
- gh-aw **v0.82.14** strict compile/validation: **2 workflows, 0 errors,
0 warnings**; both `.lock.yml` files regenerate byte-identically from
source (re-verified after `cb31b580af`).
- `git diff --check`: clean.
- Strict Vally lint: capability and ownership specs valid.
- Vally capability eval: **98.3%** across 30 trials; all four
incident-focused scenarios passed **12/12** trials.
- Vally ownership gate: **100%** across 12 trials.
- ⚠️ The Vally figures above were measured on the first two commits and
have **not** been re-run against the four follow-up commits. Treat them
as evidence for the original change only. Review also found the four new
stimuli are satisfiable by a content-free response, because each
stimulus scores a weighted mean across graders and the two mechanical
graders can outvote a failing rubric grader — see "Known gaps" below.
- Independent review: three multi-reviewer adversarial rounds. Round 1
produced the six findings fixed in `1564b2b8b2`; round 2 audited that
commit and produced the two regressions fixed in `5bb908cade` and
`80fe851229`; round 3 produced the missing CI gate wired in
`9520f4407e`; a fourth, security-focused 4-model round produced the four
findings addressed in `cb31b580af`. This supersedes the earlier "no
defects found" claim, which reflected a narrower review pass.
- Pipeline security grep checks: no changed-file violations.
- Poutine: no findings in the changed workflows; reported only unrelated
repository baseline findings.
- Zizmor: no warning/error-severity findings; 8 low-confidence
informational findings in v0.82.14-generated MCP heredocs.
- Actionlint: only the four known v0.82.14 generated-expression schema
mismatches (`secret_verification_result` and
`github.aw.import-inputs.random_seed`, once per workflow).

### Known gaps (not addressed here)

- **Vally grader strictness.** Raising per-stimulus thresholds (or
making the rubric grader independently gating) requires re-running the
live evals to confirm legitimate responses still pass; since
`skill-validation.yml` gates PRs on these evals, that is deliberately
left to a follow-up rather than tuned blind.

### Staged Fork Proof

The fork-only workflow is guarded to `PureWeen/maui`, requires
`dry_run=true`, uses global `safe-outputs.staged: true`, and performs no
real writes.

| Scenario | Run | Result |
| --- | --- | --- |
| Main exact-label evidence survives 50/50 live-search filtering and
emits a legitimate no-op |
[30295671565](https://github.com/PureWeen/maui/actions/runs/30295671565)
| Green |
| Net11 saved-head one-file delta captures and previews both PR push and
body update |
[30294895100](https://github.com/PureWeen/maui/actions/runs/30294895100)
| Green |
| Exact wrong-base fixture exposes 3,378 changed files and deliberately
ends non-green |
[30295671617](https://github.com/PureWeen/maui/actions/runs/30295671617)
| Expected failure |

The successful net11 proof shows `DEFAULT_BRANCH=net11.0` in the
capture-time MCP gateway and apply-time handler, previews only
`src/Essentials/test/UnitTests/ForkValidationTransport.txt`, and leaves
fork PR #169's head, body, labels, draft state, and updated timestamp
unchanged.

These staged runs predate the four follow-up commits and were not re-run
against them. Note that none of them exercised a FRESH create-PR, which
is why the `80fe851229` defect survived them; that path is now covered
by the Pester suite.

### gh-aw v0.83.1 / #36772

#36772 is a broader fleet upgrade and is intentionally not bundled here.
Both v0.82.14 and v0.83.1 schemas reject
`push-to-pull-request-branch.base-branch`, even though runtime code
recognizes that field. This PR instead uses the supported pre-agent
environment hook plus existing Safe Outputs configuration, so the
focused production fix does not depend on that upgrade.

### What NOT to Do

- Do not restore broad live issue search; the bounded exact-label
snapshot is the authoritative candidate source.
- Do not compare a net11 PR branch with repository-default `main`.
- Do not trim an oversized/unrelated diff to make it pass the transport
gate.
- Do not convert a missing required safe output or backend error into
`noop` or a direct write.
- Do not read a bounded snapshot as an exhaustive one; check
`truncated`/`totalMatched`.
- Do not make twin label exclusion symmetric. Main excludes
`ci-scan-net11`; net11 excludes nothing. Symmetry strands every
dual-labelled issue.
- Do not bind `-PullRequestNumber` on the `create_pull_request` path;
there is no PR yet and the registrar validates the range.
- Do not hand-edit either `.lock.yml`; regenerate both from source with
the pinned gh-aw v0.82.14.
- Do not add `Set-StrictMode` to the Pester gate's run step; it leaks
into dot-sourced test bodies and fails 16 unrelated tests.
- Do not switch the Pester gate to `pull_request_target`; it executes
PR-authored PowerShell and must stay unprivileged.

### Issues Fixed

Follow-up to #36775.

### Follow-up: dry-run seam (commit `7bec5da438`)

A fourth adversarial review round questioned whether the reconciliation
added in item 5 above is fail-closed at the mutation boundary. Split in
two, because only one half is author-controllable.

**The `dry_run` skip was a real seam — and item 5's fix is superseded.**

Item 5 resolved the false red by skipping reconciliation entirely on
`dry_run=true` (`if: always() && github.event.inputs.dry_run !=
'true'`). That went too far: the privileged `safe_outputs` job has no
dry-run predicate of its own, so a mutating output emitted *against* the
dry-run contract was both applied and completely unreported. It is the
one run shape where the check went from detective to absent.

The step now runs `always()` and **inverts** under dry_run rather than
disappearing. A dry run promises to emit nothing, so its mutating
allowance is zero no matter how many expectations were registered while
validating the candidate diff; only the forward
registered-but-not-captured direction — which a correct dry run
legitimately trips, and which was the original reason for the skip —
stays suspended. The dry-run signal is read from a step `env:` populated
by Actions (`CI_FIX_DRY_RUN: ${{ github.event.inputs.dry_run }}`), so
item 5's actual guarantee (host-evaluated, not agent-disableable) is
preserved.

**Gating `safe_outputs` on `needs.agent.result == 'success'` is not
expressible from source.**

That job condition is compiler-generated by gh-aw:

```yaml
# ci-status-fix.lock.yml
if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
```

Pinned gh-aw v0.82.14 exposes no frontmatter knob over it, and the only
alternative is hand-editing the lock — which this repo forbids and which
would silently vanish on the next `gh aw compile`. The reconciliation
step is therefore deliberately **detective** with respect to agent-job
failure, and now says so inline, alongside the real preventive boundary:
the SHA-pinned, config-locked handler that scopes every mutating output
to this workflow's own `[ci-fix]`-prefixed, `agentic-workflows`-labelled
PRs within per-run caps.

**Validation.** Full `.github/scripts` suite **665/665** (657 + 8 new).
The new tests execute the **compiled lock script** under dry_run for
both twins — asserting the `.md` would not prove the generated lock
equivalent — reusing the existing `Invoke-Reconcile` harness with a new
`-DryRun` switch. Mutation-verified in both directions: restoring the
registration-count allowance under dry_run fails exactly the 2
emit-under-dry-run tests; removing the forward-check suspension fails
exactly the 2 correct-dry-run tests. Locks regenerated with pinned gh-aw
v0.82.14 and byte-stable on recompile; `gh aw validate` 0 errors; `git
diff --check` clean.

### Review follow-up (commit `0ed9cd964f`)

A later round noted that `report_incomplete` is excluded from the
reconciliation, so a `dry_run` could still create that one diagnostic
issue. Accurate — and broader than raised, since `noop` is configured
`report-as-issue: true` and files an issue too. It stays that way
deliberately, and the code previously justified the carve-out only for
legitimate runs while saying nothing about `dry_run`.

Suppressing them would be a regression, not a fix. This step is
**detective**, so listing those types could not prevent the write — only
redden the run after the issue was already filed. And
`report_incomplete` is emitted from the snapshot guard, which runs
*before* any Step 0 dry-run gate: it is how a preview reports that it
could not proceed. A write-free canary that cannot report its own
blocker is strictly worse than one that files a diagnostic, and a dry
run is precisely when a broken snapshot most needs to be heard. The
contract enforced here is **"emit no MUTATION", not "emit no
telemetry"**.

That reasoning now lives in the reconciliation script itself in both
twins, and the existing dry-run diagnostics case was extended to cover
the exact type raised (`create_report_incomplete_issue`, alongside
`noop`). Mutation-verified — adding those types to the zero-allowance
loop fails the case on both twins, so a future "fix" for this note trips
a test that explains why it is wrong. No behaviour change; suite still
665/665, `gh aw compile --strict` 0 errors and idempotent.

---------

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfd33e26-0ff8-45d4-9ef3-72a4ea1f93cf
Copilot-Session: 9f984b5b-21bf-49ac-b131-04128a97e5e5
Copilot-Session: c8152ccc-ac22-4fed-8633-4f1d720d653c
Alex-Tsvetanov added a commit to Alex-Tsvetanov/maui that referenced this pull request Aug 26, 2026
Catalyst reuses the iOS handlers, so fixes dotnet#1 (button measure), dotnet#2 (button/radio
font 14pt), dotnet#4 (shape HOrizontalOptions=Start) and dotnet#5 (pointer_gesture FontSize=24)
carry over. Verified cpp-vs-MAUI on Catalyst:
  - shapes:          pixel-perfect match (left-aligned Ellipse/RoundRect/pentagram/Line)
  - pointer_gesture: pixel-perfect match (24pt labels)
  - button:          text-button font/sizing now match MAUI
Recaptured all 3 columns (cpp/maui/xaml) for: button image_button radio_button_group
path_gallery rectangle_gallery composition_gallery shapes transform_playground
pointer_gesture.

Known remaining macOS-specific diff (flagged): image_button 'settings' renders too
tall on Catalyst (unconstrained image height) vs MAUI's compact bar.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants