13 Commits
Author SHA1 Message Date
Joseph MattielloandClaude Fable 5.1 d7482ba3ec ci(app): Azure Artifact Signing for every Windows binary; retire the SignPath stub (#1150)
Every exe and dll in the Windows app packages (virtualjaguar.exe, librashader.dll,
the software-opengl/ copy and the bundled Mesa DLLs, x64 and arm64) and both Inno
Setup installers are Authenticode-signed with Azure Artifact Signing: SHA-256,
RFC 3161 timestamp from timestamp.acs.microsoft.com, OpenID Connect login
(azure/login), no certificate material or client secret anywhere.

- .github/actions/windows-sign: composite action (azure/login v3.1.0 and
  azure/artifact-signing-action v2.0.0, both pinned by commit) that decides from
  its inputs whether it can sign, prints a ::notice:: and does nothing otherwise,
  signs, then re-reads every file with Get-AuthenticodeSignature and fails when
  one is not Valid and timestamped.
- app-package.yml: the Windows leg becomes build (native runner per arch, install
  tree -> intermediate windows-stage-<arch>) -> windows-package (ONE x64 job for
  both architectures: the signing action does not run on Windows ARM runners,
  its dlib is x64/x86 only) -> windows-smoke (native runner per arch, Authenticode
  re-checked on the DOWNLOADED artifact, portable zip, silent install/uninstall).
  SignPath stub (inputs, secrets, env, steps) removed; secrets are now
  AZURE_CLIENT_ID / AZURE_TENANT_ID / AZURE_SUBSCRIPTION_ID plus the variables
  AZURE_SIGNING_ENDPOINT / AZURE_SIGNING_ACCOUNT / AZURE_SIGNING_PROFILE.
- app.yml and release.yml grant id-token: write to the package job; app.yml
  passes the three secrets so same-repo PR packages are signed like a release.
- Release gate: a second expectation, WINDOWS_APP_EXPECT, for the app zips and
  installers; release.yml passes `signed` when all six Azure values exist. The
  core DLLs, cue2cdi and the jagcd tools keep EXPECT_WINDOWS (unsigned) until
  their jobs sign too. The zip check now covers every PE file inside, summarised
  in one cell. stage.sh skips the windows-stage-* intermediates.
- app/packaging/windows/verify-signatures.ps1: the shared check (files, folders,
  zips; -Expect signed|unsigned|report; table in the job summary).
- package.sh: the local sign hook signed the x64 installer name for both arches.
- Docs: app/packaging/README.md (secrets/variables, layout, Mesa decision, quota,
  the one-time Azure setup incl. roles and federated-credential subjects),
  docs/release-process.md.
- release-packages.cjs: the three new jobs join the completeness list (15).

Refs #1150

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-03 19:48:49 -05:00
Joseph MattielloandClaude Fable 5.1 c2fd2001e7 test(ci): release contract test follows the staged publish shape
release.yml flattens once in the stage job (scripts/release/stage.sh keeps
the ci-core-*, librashader-* and signing-status-* intermediates out) and the
gate, release and nightly jobs download the staged artifact, so the test
asserts that instead of two per-publisher ci-core exclusions.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-02 22:06:42 -05:00
Joe Mattiello a5593e55d9 test(tom): preserve Atari Karts HC regression coverage and doc correction (#1096)
* test(tom): guard Atari Karts racing line after HC fix (#1085)

* ci: allow scoped temporary Linux runners for Karts validation
2026-10-26 10:06:58 -04:00
Joseph Mattiello 304867bd48 fix(ci): scope checkout ownership trust for core artifact staging 2026-10-20 19:01:25 -04:00
Joseph Mattiello 3c683169ef ci(core): share compile-only release builds and preserve CI artifacts 2026-10-20 18:39:49 -04:00
Joseph Mattiello 2e46ca4492 ci(app): share exact-head release packages with matching PRs 2026-10-20 18:05:38 -04:00
Joseph Mattiello b466c0372f fix: address Kimi review findings on PR #594
- nightly-staleness.yml: compare against the `nightly` tag's target commit
  date, not the release object's published_at/created_at. release.yml
  updates the release in place (gh release edit --clobber), so those
  timestamps are stamped once and never move -- the watchdog would have
  started crying wolf on every scheduled run ~24h after this merged. The
  tag ref IS force-moved on every publish, so it's the correct signal.
- README.md: Team Tap and Pro Controller were still marked "Not emulated"
  in the peripheral table, contradicting this release. Fixed both, added
  the 6D flight stick and paddle-ADC rows the table was missing entirely,
  and named all three in the peripherals intro paragraph.
- docs/RELEASE_NOTES_v3.4.0.md: added a "(Corrected 2026-08-19: ...)"
  marker where a prior docs-only fix (f2d8457) silently changed the notes'
  claimed $F17C00 return value from 0x0000 to 0xFF. Verified the shipped
  v3.4.0 code always returned 0xFF (the fixing commit predates the v3.4.0
  tag) -- the correction was accurate, it just lacked a transparency marker
  on a tagged release's record.
- jaguar-toolchain-image.yml: workflow_dispatch had no branch guard, so a
  manual dispatch from any branch could publish :latest -- contradicting
  the file's own stated invariant that only develop ever does. Added
  `if: github.ref == 'refs/heads/develop'` on the job.
- kimi_review.py: large PRs were truncated by raw character count on a
  `git diff` whose file order is roughly path-alphabetical, so a PR with
  many docs/.github/Makefile changes (which sort before src/) could lose
  every src/ change to the budget before the model ever saw one -- exactly
  what happened reviewing this PR. Diff is now built file-by-file in
  priority order (src/**/*.c and *.h first, then other sources, then
  tests, then docs/CI/build last), grouped by directory so a changed .c
  lands next to its changed .h and next to its other changed same-folder
  siblings, and accumulated against the budget per-file so truncation
  always falls on a clean file boundary instead of mid-hunk. The PR note
  and the prompt sent to the model both now name which files got cut
  instead of just saying "large PR".
2026-08-25 15:21:52 -04:00
Joe Mattiello a89dc194c1 fix(ci): say on the PR when no review was posted (#455)
The reviewer soft-fails by design, so a run that posts nothing still goes
green.  The only signal was a ::warning:: in the job log, which nobody
reads unless they already suspect a problem -- and on the PR itself an
unreviewed PR is indistinguishable from one the reviewer had nothing to
say about.

That is not hypothetical: #447, #450 and #451 sat unreviewed for a day
because the plan hit its quota (HTTP 403 access_terminated_error), and
the three PRs looked exactly like PRs that had passed review.

So the persistent failures -- quota, auth, request shape -- now leave a
sticky, marker-tagged note on the PR saying no review happened and why.
It updates in place, because every push re-runs the reviewer and a
reviewer that is down stays down for the cycle.  Transient network
failures deliberately do NOT post: a DNS blip is not worth a comment,
and the ::warning:: still covers it.

Two smaller holes closed while here:

- a failed 'gh pr comment' after a successful review generated the whole
  review and then discarded it silently; that path now annotates too;
- a successful review retracts any earlier unavailable-notice, so a PR
  cannot carry both.

pr_note() never raises: it runs on the failure path of an advisory job,
and a broken notice must not become a second, louder failure.
2026-08-15 01:47:26 -04:00
Joseph MattielloandClaude Opus 5 2d26ece801 fix(ci): match the model NAME, not the word "model", in the 400 handler
The gate added earlier ("model" in detail) was insufficient, and the proof
is the very error it was written for: the API's temperature rejection reads
"invalid temperature: only 1 is allowed for this model" -- it contains the
word, so a recurrence would once again be reported as a bad model name.

Now the branch requires the configured KIMI_MODEL to appear in the
response, or an explicit phrase ("model not found", "unknown model",
"invalid model", ...). Routing checked against every payload this endpoint
has actually produced:

  quota 403        -> billing         wrong-host 401 -> auth/host
  temperature 400  -> request shape   model 400/404  -> model name
  bare 404         -> base URL

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-14 22:24:39 -04:00
Joseph MattielloandClaude Opus 5 2583440d33 fix(ci): tell a Kimi quota 403 apart from a wrong-host 403
The 401/403 handler printed "Auth failed against <base>" plus the
subscription-key-vs-Moonshot-host explanation for every 403. A quota 403
("You've reached your usage limit for this billing cycle",
type access_terminated_error) is not an auth problem at all: the key
authenticated, the plan is simply spent.

That is the same failure this file already had once -- an error handler
asserting a cause the response contradicts -- and it sends the reader to
the key and KIMI_API_BASE when neither is wrong. Quota 403s now say so,
and mention that every push to an open PR spends quota, which is the part
that surprises people running a sweep across several branches.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-14 22:23:52 -04:00
Joseph MattielloandClaude Opus 5 407c55fcec fix(ci): raise the Kimi read timeout, and make it configurable
#449 -- the largest of the six open PRs -- failed at exactly 181s with
`TimeoutError: The read operation timed out`. 180s is not enough for a
long-context model reasoning over a diff near MAX_DIFF_CHARS (180 KB).

Default is now 600s, overridable with KIMI_TIMEOUT_S. The job is advisory
and soft-fails, so a generous ceiling costs only runner minutes on a
request that stalls, whereas too tight a ceiling costs the review itself.

Found because the annotation added in #452 worked: the run was green, and
the ##[warning] named the timeout instead of leaving a silent pass.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-14 22:18:49 -04:00
Joe Mattiello 3bb0ea6c06 fix(ci): make the Kimi reviewer actually run (#452)
Every Kimi review since #444 failed with HTTP 400 (`invalid temperature: only 1 is allowed for this model`) while the job stayed green, so six open PRs showed a passing reviewer and no review.

- Drop the hard-coded `temperature: 0.2`; the Kimi coding models accept only the default.
- Stop blaming the model name for every 400. That handler asserted `Model '<name>' was rejected` regardless of the response, so the log misdirected diagnosis for the workflow's entire life. Now gated on the body naming the model, with a base-URL hint for a bare 404.
- Emit `::warning::` on both the HTTP and the network failure paths. Soft-fail is right for an advisory reviewer, but without a signal a broken bot is indistinguishable from a healthy one.
- Pin `refs/pull/N/merge` on checkout. The default differs per event: merge ref for `pull_request`, but the DEFAULT branch for `issue_comment` (master, which has neither this workflow nor the script). This also lets an open PR pick up a base-branch reviewer fix without a rebase.
- Delete `.coderabbit.yaml`. CodeRabbit is a GitHub App needing an install on the `libretro` org; it has never commented here, so the config was dead weight that read like a live integration.

Verified end-to-end on this PR: three Kimi reviews posted across three pushes. The first one caught a real gap in the fix itself (the network-failure path had no annotation), which is included above.
2026-08-14 22:12:02 -04:00
Joe MattielloandClaude Opus 5 1aa9c43d84 ci: add CodeRabbit config and a Kimi second-opinion PR reviewer (#444)
* ci: add CodeRabbit config and a Kimi second-opinion PR reviewer

Copilot review is unavailable — every recent PR got "Copilot was unable to
review this pull request because the user who requested the review has reached
their quota limit", which is silence, not approval. Two independent reviewers so
one running dry is not the same as a PR going unreviewed.

.coderabbit.yaml — tuned to what actually breaks here rather than generic
defaults. Per-path instructions for the C89/MSVC rules (mid-block declarations
first), the big-endian GET/SET macro contract, "never trust a source comment for
hardware behaviour, cite the JTRM or a netlist", the savestate-field trap behind
#327/#400, the audio pair rule from PR #170, and the CI ephemeral-port flake
(#356). Generated and vendored trees are excluded from review: cpuemu.c alone is
~1.8 MB of machine-generated 68K dispatch, and the bios sources are bin2c hex
tables. The exemption list matches scripts/c89-lint.sh's skip_file.

.github/workflows/kimi-review.yml + .github/scripts/kimi_review.py — dependency
-free second opinion using the KIMI_KEY_* secrets. Runs on PRs into
develop/master and on "@kimi review". Self-contained rather than a third-party
action so the auth shape stays visible in one file and there is no action
version to drift.

Assumption worth flagging: KIMI_KEY_VALUE is sent as the bearer token and
KIMI_KEY_ID as an identifying header. If the provider issues an AK/SK pair
needing a signed request, the job prints an explicit 401 diagnostic naming the
one function to change. Endpoint and model are overridable via the KIMI_API_BASE
and KIMI_MODEL repo variables without touching code.

The job fails SOFT throughout: an advisory review must never red-X a PR that
compiles and passes its tests.

Verified locally: python compiles, workflow YAML parses, and the diff filter was
exercised against two real commits -- it passes #442's five files through and
reduces the generated-matrix commit 3c4992d to nothing.

Co-Authored-By: Claude Opus 5 <[email protected]>

* ci(kimi): point at the Kimi for Coding endpoint, not the Moonshot one

The first live run 401'd with "Invalid Authentication". The key was fine and
the protocol shape was fine -- the host was wrong.

Keys minted in the Kimi Code console (kimi.com/code/console) belong to the
"Kimi for Coding" SUBSCRIPTION, which is served from api.kimi.com/coding and is
a different product from the pay-per-token Moonshot platform at
api.moonshot.ai. A key from one 401s against the other. Defaults now:

  KIMI_API_BASE  https://api.kimi.com/coding/v1   (OpenAI-compatible base;
                 an Anthropic-compatible base exists at .../coding/)
  KIMI_MODEL     k3-256k

Both remain repo variables, so retargeting needs no code change. The 401 and
400/404 diagnostics now name the specific cause -- which host a key belongs to,
and the tier-dependent model list (k3, k3-256k, kimi-for-coding,
kimi-for-coding-highspeed) -- so the next misconfiguration is self-describing
instead of a bare status code.

Co-Authored-By: Claude Opus 5 <[email protected]>

---------

Co-authored-by: Claude Opus 5 <[email protected]>
2026-08-14 00:24:15 -04:00