Skip to content

Closes #176#187

Merged
realproject7 merged 3 commits into
mainfrom
task/176-ci-gates-hardening
Jul 14, 2026
Merged

Closes #176#187
realproject7 merged 3 commits into
mainfrom
task/176-ci-gates-hardening

Conversation

@realproject7

@realproject7 realproject7 commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Closes #176

Harden the automation that guards our invariants: a release-mode privacy gate, SHA-pinned actions, release-workflow quality gates, wider no-stub / color gates (with their own self-tests), and an honest VAD test note. One workflow-surface fix-unit.

EPIC Alignment

Part of EPIC #134 (v1.3 audit follow-up), Batch 44 (standing-order … #174#176, then #177). This ticket CREATES the release-invariants CI job; #177 EXTENDS it, so #176 must merge first (RE2's Batch 36 boundary note). Infra/CI + gate scripts + a VAD test note; touches .github/workflows/*, scripts/*, crates/livecap-core/src/vad.rs, crates/livecap-core/tests/force_cut_wav.rs. Does not touch resample.rs/render.ts/parse.ts/pipeline.rs (reserved for #178/#179 — the pipeline.rs cfg guard is only asserted on, never edited), dashboard, or settings.

Self-Verification (the six parts)

1 — release-invariants CI job (ci.yml, macos-14). CI only ever compiled/tested the debug profile, so a dropped #[cfg(debug_assertions)] guard (a bad merge on the #64 bleed dump or a #108 dev flag) was invisible until tag time — a #161-class covert-audio/env regression through green CI. The new job builds livecap-app in release (cargo test --release -p livecap-app --no-run + cargo build --release -p livecap-app) and asserts the debug-only env-escape names are absent from the binary via strings: LIVECAP_BLEED_DUMP_DIR, LIVECAP_CAPTURE_VISIBLE, LIVECAP_AUTOSTART, LIVECAP_UI_PROBE. They exist only inside #[cfg(debug_assertions)] items, so a release build compiles the names out entirely.

2 — SHA-pin all third-party actions (ci.yml + release.yml). Every uses: moved from a mutable tag/branch to a full commit SHA with a # v4-style comment. This matters most in release.yml, which materializes the Apple signing secrets and produces the signed DMG — a compromised @v4/@stable (the 2025 tj-actions pattern) could otherwise run there. dtolnay/rust-toolchain@stable (a moving branch) is pinned to a master commit SHA with an explicit toolchain: stable input, since the channel can't be inferred from a SHA ref. (A dependabot github-actions config to keep them fresh is a sensible follow-up; kept out of this PR to stay scoped.)

3 — release.yml runs the CI gates before the tag build. Tag pushes bypass branch protection, so nothing enforced that a tagged commit passed CI. Added a step (after pnpm build, before signing) running ./scripts/no-stub-gate.sh, ./scripts/color-guard.sh, cargo test --workspace, and pnpm test — a release is now always cut from a green tree.

4 — no-stub-gate.sh now matches its namesake and its cousins: \bstub, \bdummy, unimplemented, whitespace-tolerant not[[:space:]]+implemented, and \bXXX\b (added to TODO|FIXME|HACK|placeholder|\bmock). The leading-boundary form catches camelCase (stubTranslateBatch). A per-line escape hatch (no-stub-allow, mirroring color-guard's color-guard-allow) exempts a specific line — used only for the one verified capability description in crates/livecap-core/src/error.rs (whose doc is reworded to the accurate "unavailable on the current platform", with the "not implemented for non-macOS targets" note carried in a // … no-stub-allow comment). This is explicit and per-line, so future deferred prose ("not implemented for this mode yet") without the marker still fires — no global phrase bypass. A scripts/fixtures/no-stub-selftest.txt (one marker per line, 10) drives a self-test: the gate fails if its pattern ever stops matching a known-bad marker, and a missing fixture is a hard failure (exit 1) so the gate can never run without its regression proof.

5 — color-guard.sh gains two coverage channels. CSS matching now includes hsl()/hsla()/hwb()/oklch()/oklab()/lch()/lab()/color-mix()/color() and a curated CSS named-color set (CSS-wide keywords like transparent/currentColor/inherit deliberately absent, so they still pass). A second pass over src/**/*.ts flags hex/rgb()/hsl()-family literals on lines that set an inline style (.style., .setProperty(, style=) — the surface dashboard.ts:203-215 uses (tokenized today). Documented limits (in the script header): named colors in TS (bare red/blue collide with identifiers), cross-line/runtime-built values, and value-line comments — all covered by the /* color-guard-allow */ escape hatch.

6 — VAD note reconciled honestly. vad_state_is_maintained_across_chunks asserts the synthetic generator IS detected as speech and passes (verified: cargo test -p livecap-core --lib vad:: → 3/3), directly contradicting the in-file NOTE (and the identical claim in force_cut_wav.rs) that "this Silero build never classifies the generator's output as speech." Corrected both to state the truth, and added a vacuity guard to longer_redemption_does_not_fragment_more (assert!(!segments_400.is_empty())) so a Silero change that stops detection fails loudly instead of passing 0 <= 0.

Security invariants. No new dependencies. No caption/audio content logged (the release-invariants gate reads only env-var names via strings; the workflow run: blocks use only static values — no untrusted-input interpolation). Scoped to CI/gates + the VAD note; pipeline.rs is asserted-on, not edited.

Seeded-violation proof (AC — one per changed gate, run locally)

Gate Seed Result
release-invariants (strings) debug build of a livecap-core-linking binary LIVECAP_BLEED_DUMP_DIR present (baseline)
proper release build absent → gate passes (cfg stripped it)
release build with a reachable un-gated env::var_os("LIVECAP_BLEED_DUMP_DIR") (dropped guard) present → gate would FAIL
no-stub-gate export function stubTranslateBatch(){…} in src/ gate FAILS (\bstub) ✅
remove \bstub from the pattern self-test FAILS (9 of 10 markers) — the gate can't silently weaken ✅
// not implemented for this mode yet in src/ (no marker) gate FAILS — no global bypass; only a no-stub-allow line is exempt ✅
delete the self-test fixture gate FAILS (exit 1) — can't run without its regression proof ✅
color-guard (CSS func) color: oklch(0.6 0.1 20) in styles.css FAILS
color-guard (named) background: black in styles.css FAILS
color-guard (TS inline) el.style.background = "#1e1e1e" in a .ts FAILS
VAD vacuity guard (if Silero ever returns 0 segments) segments_400.is_empty() assert! fires — no vacuous 0<=0 pass ✅

Clean tree: both gates pass; livecap-core 60 tests green (incl. the 3 vad tests); both workflows are valid YAML with every uses: pinned to a 40-hex SHA; scripts pass bash -n (and stay bash-3.2-compatible for the macOS release job). The actual release-mode compile + strings assertion on the real livecap-app binary runs in the new release-invariants CI job on this PR.

Design Fidelity

N/A — no UI/webview/CSS surface changed (CI workflows, gate scripts, and a Rust test note only).

Deviations

  • Scoped the one legitimate "not implemented" occurrence (an error.rs capability description) via an explicit per-line no-stub-allow escape hatch (per RE1) — consistent with color-guard's color-guard-allow — rather than a global phrase filter, so future deferred prose still fires. The error.rs doc is reworded to the accurate "unavailable".
  • Pinned dtolnay/rust-toolchain to a master SHA + toolchain: stable input rather than the stable branch SHA, per dtolnay's documented pin-by-SHA guidance (a SHA ref can't carry the channel).
  • Left the release-invariants job as a separate CI job (not folded into app-macos) so its release-profile build + strings gate is independently visible and [audit][security-hygiene] Debug-gate LIVECAP_MODEL_BASE_URL (documented as a dev knob but honored in release) — #161 sibling #177 can extend it, per the ticket's create-then-extend boundary.

🤖 Generated with Claude Code

…tes, gate blind spots, VAD note

Five verified automation gaps that guard our invariants (#134 audit), one
workflow-surface fix-unit. Creates the `release-invariants` job that #177 extends.

1. release-invariants CI job (ci.yml, macos): compiles livecap-app in RELEASE
   (`cargo test --release --no-run` + `cargo build --release`) and `strings`-
   asserts the debug-only env-escape names are ABSENT — LIVECAP_BLEED_DUMP_DIR /
   LIVECAP_CAPTURE_VISIBLE / LIVECAP_AUTOSTART / LIVECAP_UI_PROBE all live only in
   `#[cfg(debug_assertions)]` items, so release compiles them out (#146/#161). CI
   only tested the debug profile before, so a dropped guard was invisible.
2. SHA-pin every third-party action in ci.yml + release.yml to a full commit SHA
   with a version comment (supply-chain: release.yml materializes the Apple
   signing secrets). dtolnay/rust-toolchain pinned to a master SHA with an explicit
   `toolchain: stable` input (channel can't be inferred from a SHA ref).
3. release.yml runs the same gates (no-stub-gate, color-guard, cargo test, pnpm
   test) before the tag build — tag pushes bypass branch protection, so a release
   is now always cut from a green tree.
4. no-stub-gate.sh: extend the pattern to stub/dummy/unimplemented/XXX (the free
   phrase "not implemented" is deliberately excluded — it false-positives on
   shipped platform-unsupported error prose, e.g. error.rs; `unimplemented` covers
   the deferred-code case). Add a fixtures/ self-test that fails if the pattern
   ever stops matching a known-bad marker.
5. color-guard.sh: extend CSS matching to hsl/hwb/oklch/oklab/lch/lab/color-mix/
   color() + a curated named-color set, and add a second pass flagging hex/rgb()/
   hsl()-family literals on TS lines that set an inline style. Documented limits
   (named colors in TS, cross-line/runtime values, value-line comments).
6. Reconcile the vad.rs NOTE (and the same false claim in force_cut_wav.rs): the
   synthetic generator IS classified as speech by this Silero build (a passing
   test proves it), so the "never detected" claim was wrong. Add a vacuity guard
   to longer_redemption_does_not_fragment_more so a Silero change that stops
   detection fails loudly instead of passing 0<=0.

Seeded-violation proofs for every changed gate are in the PR description. No new
dependencies; no caption/audio content logged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Epic Alignment: FAIL

The release-invariants job correctly creates the #177 extension point, but the no-stub gate misses a required #176 marker and allows its own self-test to be disabled.

Checked (evidence)

  • Structural gate: live PR body includes filled EPIC Alignment, Self-Verification, and N/A Design Fidelity sections.
  • Contract: live #176 requires all listed deferred-work markers plus a self-test that proves the gate fires.
  • Workflow review: ci.yml/release.yml pin all shown third-party actions to 40-hex SHAs; release quality gates precede signing/build.
  • Riskiest part: scripts/no-stub-gate.sh governs deferred-work enforcement, but its marker set and missing-fixture path leave bypasses.
  • Kill-list: scanned full diff — hits listed below.
  • CI: gh pr checks 187 → color-guard, no-stub-gate, packages-linux pass; app-macos and release-invariants pending.

Findings

  • [required] Enforce the ticket-required “not implemented” marker.

    • File: scripts/no-stub-gate.sh:12
    • Why it fails: #176 Scope item 4 explicitly requires not implemented, but PATTERN omits it; documenting a false-positive exception leaves production comments containing that marker undetected.
    • Do instead: add a whitespace-tolerant not-implemented pattern and narrowly allow the legitimate platform-error occurrence (for example, an explicit gate allow marker on that one line), rather than removing the marker globally.
  • [required] Fail closed when the gate self-test fixture is absent.

    • File: scripts/no-stub-gate.sh:28
    • Why it fails: deleting or renaming scripts/fixtures/no-stub-selftest.txt makes the gate print a warning and exit successfully, disabling the self-test #176 requires.
    • Do instead: make a missing fixture an error with a non-zero exit; the gate must not pass without its regression proof.

Decision

The infrastructure direction is sound, but these two bypasses leave the changed no-stub gate short of the ticket contract.

@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE at PR #187 head (conditioned on release-invariants + full CI green)

All six parts of #176 are implemented correctly; I verified each against actual source and ran the gate proofs + livecap-core + SHA checks on Linux. This CREATES the release-invariants job — must merge before #177.

Checked (evidence)

  1. release-invariants job (ci.yml). Release-builds livecap-app, then a set -euo pipefail step asserts the debug-only env NAMES are absent from the binary (strings | grep). I confirmed all four are real reads, each #[cfg(debug_assertions)]-gated, so a release build compiles them out: LIVECAP_BLEED_DUMP_DIR (pipeline.rs:345, core→linked into app), LIVECAP_CAPTURE_VISIBLE/LIVECAP_AUTOSTART (dev_flags.rs:75/91, with explicit #[cfg(not(debug_assertions))] const-false counterparts), LIVECAP_UI_PROBE (lib.rs:395). All other occurrences are doc/inline comments (compiled out). The if strings|grep -q handles pipefail correctly (condition context). Pending: the job itself is still running — see condition below.
  2. SHA-pin (both workflows). Every uses: is a 40-hex SHA (grep-verified, zero unpinned). I checked each pin via the GitHub API: all 5 are real commits in their legitimate repos (no typosquat), and actions/checkout = v4.3.1. dtolnay/rust-toolchain is pinned to a master SHA with toolchain: stable input — fixing the mutable-branch issue I flagged in #879. release.yml (Apple-secrets job) is pinned too, with a comment explaining why.
  3. release.yml gates. A new "Quality gates before building the release" step runs no-stub-gate + color-guard + cargo test --workspace + pnpm test before the tag build — closing the "tag pushes bypass branch protection" hole.
  4. no-stub-gate. PATTERN extended to …|\bstub|\bdummy|unimplemented|\bXXX\b (leading \b catches camelCase stubTranslateBatch); free-form "not implemented" deliberately excluded (documented — legit platform-error prose). Seeded proofs I ran: clean tree passes (self-test: 9 markers fire); a seeded stubTranslateBatch in src/main.ts → FAILS (exit 1); weakening PATTERN (drop \bstub) → the self-test FAILS (8/9). Non-vacuous.
  5. color-guard. Adds modern CSS funcs (hsl/hwb/oklch/oklab/lch/lab/color-mix/color) + a curated NAMED set + a TS inline-style pass; CSS-wide keywords intentionally allowed; limits documented. Seeded proofs I ran: oklch() in a CSS value → FAILS; named black → FAILS; TS el.style.background="#1e1e1e" → FAILS; /* color-guard-allow */ escape hatch → passes. Value-only CSS scan correctly skips #id selectors.
  6. VAD note reconciliation. The old NOTE's false claim ("this Silero build never classifies the generator as speech" — the contradiction I flagged in #879) is corrected in BOTH vad.rs and force_cut_wav.rs: the synthetic IS detected (vad_state_is_maintained_across_chunks asserts non-empty), and a vacuity guard (assert!(!segments_400.is_empty(), …)) makes a 0<=0 silent pass fail loudly. Confirmed: cargo test -p livecap-core60 passed, both VAD tests green.
  • Scripts are bash -n clean and bash-3.2-safe (no mapfile). Boundary/invariants: 7 files; no new deps; no reserved files; no caption content logged; workflow run: blocks use only static values.
  • Live CI: app-macos, color-guard, no-stub-gate, packages-linux all pass; release-invariants still running.

Condition

My clearance is conditioned on release-invariants (and thus all 5 checks) going green — it is the new job that proves part 1 end-to-end on macOS (release build + strings assertion), which I could only validate statically here. The merge gate must wait for it. (Shared bot token can't file a formal GH approval; this comment + chat is my evidence-bound verdict.)

… carve-out; fail on a missing fixture

Address RE1 review on PR #187:
(1) Restore the ticket-required whitespace-tolerant `not[[:space:]]+implemented`
    marker instead of dropping it globally, and NARROWLY allow only the legitimate
    platform-error phrasing via an `ALLOW='not[[:space:]]+implemented[[:space:]]+for[[:space:]]'`
    carve-out (crates/livecap-core/src/error.rs's "…not implemented for the current
    platform" is a shipped capability statement, not a stub). A bare "not
    implemented" / "not implemented yet" still fires. Re-added the marker to the
    self-test fixture (now 10 markers).
(2) A MISSING self-test fixture is now a hard failure (exit 1), not a warn-and-pass
    — the gate must never run without its own regression proof.

Proofs (local): current tree passes (error.rs exempted); `not implemented yet` and
`summary is not implemented` in src/ both FAIL; missing fixture FAILS; weakening
PATTERN still trips the self-test. bash -n clean, bash-3.2-safe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Epic Alignment: FAIL

The re-review resolves the missing-fixture failure, but the global allow pattern still weakens the #176 “not implemented” enforcement beyond the one verified legitimate error message.

Checked (evidence)

  • Re-review range: 30ba71a..8fa7c42.
  • Prior finding resolved: scripts/no-stub-gate.sh:25-29 now fails non-zero when the fixture is absent.
  • Prior finding partially resolved: scripts/no-stub-gate.sh:12 now matches whitespace-tolerant not implemented and the fixture covers it.
  • Riskiest part: scripts/no-stub-gate.sh:42 globally filters matching lines using ALLOW, which can hide future deferred-work prose.
  • Kill-list: scanned new range — hit listed below.
  • CI: gh pr checks 187 → no-stub-gate, packages-linux, color-guard pass; app-macos and release-invariants pending.

Findings

  • [required] Scope the not-implemented exception to the one legitimate platform-error occurrence.
    • File: scripts/no-stub-gate.sh:42
    • Why it fails: ALLOW matches any application line containing “not implemented for ”, so a new deferred marker such as “feature is not implemented for this mode yet” is silently removed from HITS. The ticket calls for the marker globally; only the known error text needs an exception.
    • Do instead: use an explicit per-line no-stub allow marker on the verified error message (and make the gate honor that marker), or another exact file/text-specific exemption. Do not use a content pattern that broadly filters all production matches.

Decision

The gate now has a hard self-test dependency, but its marker exception must be narrowed before it can provide the required enforcement.

@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE at 8fa7c42 (delta re-review; pending app-macos/release-invariants re-run green)

@re1's two findings (#980) are properly addressed in this delta (30ba71a..8fa7c42, only no-stub-gate.sh + the fixture). Re-ran the full no-stub proof matrix on Linux.

Checked (evidence)

  • "not implemented" restored with a narrow, correct carve-out. PATTERN now includes not[[:space:]]+implemented; ALLOW='not[[:space:]]+implemented[[:space:]]+for[[:space:]]' exempts ONLY the platform phrasing. I confirmed the exemption is precise: the sole app-code occurrence is crates/livecap-core/src/error.rs:11 ("System-audio capture is not implemented for the current platform") — matches ALLOW, exempted. Clean tree passes (self-test: 10 markers fire).
  • The carve-out does NOT over-broaden. Seeded proofs I ran: not implemented yet in src/ → FAILS; a bare summary is not implemented (no "for") → FAILS. So only the exact platform phrasing is exempt; deferred-stub phrasings still fire.
  • Missing fixture is now a hard failure. Removing the fixture → gate exits 1 ("must not run without its regression proof"), no longer warn-and-pass.
  • Self-test still load-bearing. Weakening PATTERN (drop not[[:space:]]+implemented) → self-test FAILS (9/10). Fixture grew to 10 (added a firing "not implemented yet" line). bash -n clean.
  • Everything else in Closes #176 #187 is unchanged by this delta (SHA-pins, color-guard, release.yml gates, release-invariants job, VAD reconciliation) — my prior full review (#4972092930 / #981) stands; app-macos/release-invariants don't invoke no-stub-gate.sh, so they're unaffected.

Non-blocking note (NOT a change request)

ALLOW is applied as a line-level grep -v, so a single line that BOTH contains the platform phrase AND an independent marker (e.g. // TODO: not implemented for windows) would be fully exempted, hiding the TODO. Contrived given how specific the platform phrasing is; a per-match exemption would be tighter but line-level is the pragmatic grep idiom. Fine as-is.

CI

no-stub-gate (the changed job), color-guard, packages-linux pass on 8fa7c42; app-macos/release-invariants are re-running on the new commit — my clearance is conditioned on them re-going green (they don't touch the gate script, so pass is expected). (Shared bot token can't file a formal GH approval; this comment + chat is my verdict.)

…stub-allow escape hatch

Address RE1 re-review (#985): the `ALLOW='not implemented for'` global filter let
future deferred prose ("not implemented for this mode yet") bypass the gate.
Replace it with an explicit PER-LINE escape hatch — a line carrying `no-stub-allow`
is exempt — mirroring color-guard's `color-guard-allow`. Scope the one verified
exemption to error.rs precisely: reword its doc to the accurate "unavailable on
the current platform" (matching the SystemAudioUnavailable variant) and carry the
"not implemented for non-macOS targets" capability note in a `// … no-stub-allow`
comment on one line.

Proofs (local): current tree passes (error.rs line exempted; self-test 10);
RE1's `// not implemented for this mode yet` (no marker) FAILS; a `no-stub-allow`
line is exempt; error.rs compiles. bash -n clean, bash-3.2-safe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Epic Alignment: PASS

#176 creates the release-invariants gate needed before #177 and hardens the required CI/gate surfaces without expanding into later-ticket code.

Checked (evidence)

  • Re-review range: 8fa7c42..1fa6790.
  • Prior finding resolved: scripts/no-stub-gate.sh:43 now honors only the explicit per-line no-stub-allow marker; the former global “not implemented for” filter is removed.
  • Contract coverage: crates/livecap-core/src/error.rs:11-13 carries the sole audited capability-text exemption; other not-implemented prose remains gated.
  • Riskiest part: the gate exemption; it is visible on the one exempted line and mirrors the existing color-guard escape-hatch pattern.
  • Kill-list: scanned new range — clean.
  • CI: gh pr checks 187 → app-macos, color-guard, no-stub-gate, packages-linux, release-invariants all pass.

Findings

None.

Decision

The delta closes both no-stub-gate bypasses, and the new release-profile invariant has passed end-to-end on macOS.

@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE at 1fa6790 (delta re-review; all 5 CI checks green)

The global "not implemented for" carve-out is replaced with a per-line no-stub-allow escape hatch — resolving @re1's finding (#985) and the non-blocking over-exemption note I raised on the prior head. Re-reviewed the delta (8fa7c42..1fa6790: no-stub-gate.sh + error.rs) and re-ran the proofs on Linux.

Checked (evidence)

  • Global bypass removed; per-line hatch added. ALLOW='not implemented for' is gone; exemption is now grep -v 'no-stub-allow', mirroring color-guard-allow. @re1's exact case // not implemented for this mode yet (untagged) now FAILS — confirmed by running it. So the phrase no longer grants a blanket bypass; only an explicit, auditable per-line tag exempts.
  • Exemption is scoped to exactly one line. no-stub-allow occurs precisely once in app ROOTS: crates/livecap-core/src/error.rs:14 (the SystemAudioUnavailable capability note). A tagged line is exempt (verified); everything else fires.
  • error.rs reword is accurate + compiles. The doc now reads "System-audio capture is unavailable on the current platform," matching the SystemAudioUnavailable variant and its #[error("… unavailable on {platform} …")] message; the "not implemented for non-macOS targets" capability note sits in the // … no-stub-allow comment. cargo check -p livecap-core → clean (exit 0).
  • Gate integrity intact. Clean tree passes (self-test: 10 markers fire); the earlier proofs (seeded markers fire, missing fixture hard-fails, weakening trips the self-test) are unaffected. bash -n clean.
  • Rest of Closes #176 #187 unchanged — SHA-pins, color-guard, release.yml gates, release-invariants job, VAD reconciliation all as previously approved (#4972092930).
  • Live CI on 1fa6790: all 5 green — app-macos (2m7s), release-invariants (2m33s), color-guard, no-stub-gate, packages-linux.

RE2 APPROVE at 1fa6790 — unconditional (CI green). This CREATES release-invariants, so #176 must merge before #177. (Shared bot token can't file a formal GH approval; this comment + chat is my verdict.)

@realproject7
realproject7 merged commit be63498 into main Jul 14, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants