Commit Graph
166 Commits
Author SHA1 Message Date
Kai (Tam Nhu) TranandGitHub 1f736b2da9 fix(ci): smoke-test failure check + relax service-key guard (REV9, REV10) (#1276)
REV9 — docker-release.yml smoke-test: add HEALTHY flag to the boot-and-wait
loop. Previously a timeout (status stays 'starting'/'missing' for all 12
iterations) exited the loop silently, letting port probes be the only net.
Now if the loop exits without HEALTHY=1 the step fails immediately with
container logs and state dump. network-contract.sh already has the HEALTHY
pattern — no change needed there.

REV10 — breaking-change-guard.yml: drop the OLD_KEYS != NEW_KEYS comparison.
That check treated ANY change to the service-key set as breaking — including
adding a harmless sidecar. The DNS contract only requires services.ccs to
exist; other services are irrelevant to the 'ccs' hostname on ccs-net. Keep
only the "ccs key must exist" check; remove the unused OLD_KEYS extraction.
2026-05-17 05:26:42 -04:00
Kai (Tam Nhu) TranandGitHub a4e16e0b09 fix: tighten parity check + catch service-key rename (#1261 loop 3) (#1274)
* fix(test): tighten compose-parity image-name match to exact expected values (REV7)

Replace loose substring grep (grep -q "ccs") with exact equality checks
against declared EXPECTED_CANONICAL_IMAGE and EXPECTED_INTEGRATED_IMAGE
constants. The integrated compose builds locally as ccs-cliproxy:latest
(not ghcr.io/kaitranntt/ccs), so both expected names are explicitly
documented at the top of the assertion block.

Fixes: a drift in integrated compose to a wrong owner/registry could
slip through the old "grep -q ccs" check; now any name other than the
declared constant is a hard failure.

* feat(ci): breaking-change-guard catches services.ccs rename — public DNS contract (REV8)

Docker's service-name DNS uses the compose service KEY as the hostname.
Sibling containers on ccs-net reach CCS via http://ccs:8317; renaming
services.ccs: to anything else silently breaks that contract even when
image name, network name, and container_name are unchanged.

Add check 4 to the guard:
- Extract top-level service keys from both base and HEAD versions of
  docker/compose.yaml using awk (no external YAML parser required).
- Fail if the "ccs" key is absent from HEAD.
- Fail if the sorted set of service keys differs from base.

Wraps inside the existing git cat-file guard so new-file PRs skip it.
2026-05-17 05:12:04 -04:00
Kai (Tam Nhu) TranandGitHub 78004746be fix: address upstream reviewer findings + failing CI checks (#1261 loop 1) (#1271)
* fix(ci): breaking-change-guard skips missing base files (CI-1)

Guard each git-show call with git cat-file -e existence check before
attempting to read docker/compose.yaml from the base branch. When the
file doesn't exist on the base (new file in this PR), the script would
crash with "fatal: path exists on disk but not in origin/dev". New
files can't cause a contract regression, so we exit early with
breaking=0.

* style: prettier reformat unrelated drift (CI-2)

Two files had minor formatting drift from earlier umbrella PRs:
- src/cliproxy/quota/quota-manager.ts
- src/management/checks/image-analysis-check.ts

No logic changes — formatter-only pass to unblock CI format:check.

* fix(test): update trusted-author gate count to 4 in ci-workflow test (CI-3)

PR #1260 added a compose-parity job to ci.yml. That job runs on
self-hosted runners using PR-provided checkout, so it legitimately
requires the trusted-author guard (same as validate, build, test).

The test expected 3 occurrences; the correct count is now 4:
  validate (matrix), build, test, compose-parity.

* fix(ci): smoke-test passes compose path + image ref to network-contract (REV-1)

network-contract.sh signature is: <compose-file> [image-ref]
The smoke-test job was calling it as:
  bash tests/docker/network-contract.sh "${{ steps.image.outputs.ref }}"
which placed the image ref in the compose-file position ($1), causing
the script to try docker compose -f <image-ref> which fails.

Corrected to:
  bash tests/docker/network-contract.sh docker/compose.yaml "${{ steps.image.outputs.ref }}"

Also removes publish-dashboard from smoke-test.needs (REV-2): when
publish-dashboard is SKIPPED on prerelease events, GitHub Actions
propagates the skip to downstream jobs, so smoke-test and
promote-mutable-tags were silently skipped on every rc.N publish.
smoke-test only verifies the integrated image; it has no dependency
on the legacy dashboard image job.

* docs(docker): annotate /root/.ccs path in compose volume (REV-3 clarification)

The reviewer raised a concern that the compose volume mounts /root/.ccs
but the entrypoint might default to /home/node/.ccs. This is a false
positive: the integrated image uses entrypoint-integrated.sh (not
entrypoint.sh), which runs under supervisord with user=root and
explicitly mkdir -p /root/.ccs. HOME is /root inside the container.
The volume mount at /root/.ccs is correct.

Added an inline comment documenting the reasoning so future reviewers
do not confuse entrypoint.sh (legacy dashboard image) with
entrypoint-integrated.sh (integrated image).

* fix(ci): gate docs-parity pull_request job to trusted authors

docs-parity.yml runs on a self-hosted runner and checks out PR code.
The self-hosted-runner-policy test requires any such workflow to include
the trusted-author guard. The workflow was missing the guard, causing
bun test:fast to fail with 1 failure.

Allow push events (no author check needed — push is to own branch)
and trusted-contributor PRs only.
2026-05-16 13:57:50 -04:00
Kai (Tam Nhu) TranandGitHub 107b5b5db4 fix(docker): apply red-team findings — drop :full, rc.1 soak, healthcheck, signing (#1251) (#1262)
* docs(quickstart): fix raw URL for corporate-proxy fallback (H1)

* feat(docker)!: drop :full image variant — use sibling containers on ccs-net (Q3)

No AI CLIs (claude-code/gemini-cli/grok-cli/opencode) are bundled in the image.
Use sibling containers attached to ccs-net instead.
See docker/README.md#connect-your-app-to-cliproxy.

Also removes bash from apk deps (entrypoint uses #!/bin/sh — L2).

ci(docker): publish only immutable :<ver> tag pre-smoke; promote-mutable-tags
job adds :latest/:MAJOR/:MINOR aliases only after smoke tests pass (H3)

ci(docker): smoke-test-compose-url runs network-contract.sh against the
downloaded /tmp/ccs-compose.yaml instead of re-cloning the repo (H4)

ci(docker): sign published images with cosign keyless OIDC + attach
provenance/SBOM via build-push-action (M8)

test(docker): network-contract.sh now accepts compose-file and image-ref
positional args; replaces python3 healthcheck parser with jq (L4)

* chore(release): cut every main release as rc.N prerelease, manual promote flow (H2)

- .releaserc.cjs: main branch now uses prerelease 'rc' channel — every
  semantic-release cut becomes vX.Y.Z-rc.N
- add promote-release.yml: workflow_dispatch flips rc → stable via
  'gh release edit --prerelease=false'; triggers docker promote-mutable-tags
- add docs/release-process.md: full soak + promote procedure, rollback steps,
  cosign verification command
- releaseNotesGenerator: add revert section, document chore hidden behaviour (L8)

* fix(docker): healthcheck probes both dashboard and cliproxy ports (M1)

compose.yaml healthcheck now checks :3000 and :8317 concurrently with
a 4.5s internal timeout, within Docker's 5s timeout budget.

Also:
- docs(docker): document npm lockfile ephemeral tradeoff above install
  layer; note size-budget regression test as the practical safeguard (M3)
- docs(docker): drop :full row from Choosing an image table; add sibling
  container note pointing to connect-your-app-to-cliproxy (Q3/docs)
- docs(docker): remove :full docker run block; fix release-tag sentence (Q3)
- docs(docker): fix raw URL in migration section (H1 parity)
- docs(docker): add Volume warning — 'down -v' deletes named volumes (L13)
- docs(docker): update What changes table — remove :full reference (Q3)
- docs(docker): add Image Signatures and SBOM section with cosign verify
  and sbom download commands (M8/docs)
- changelog: add Unreleased entries for rc soak, cosign signing, :full
  removal with migration guidance

* ci(docker): assert image-size budget per platform; add compose parity + breaking-change guard (M6/L9/L12)

- image-size.sh: add --platform flag; uses 'docker buildx imagetools
  inspect' to sum compressed layer sizes from registry manifest for
  linux/amd64 and linux/arm64 separately (M6)
- docker-release.yml smoke-test: runs size check for both platforms
- compose-parity.sh: diffs docker/compose.yaml vs
  docker/docker-compose.integrated.yml for image name, ports 3000/8317,
  volume mounts /root/.ccs and /var/log/ccs, ccs-net definition (L12)
- ci.yml: add compose-parity job wired to cliproxy runner (L12)
- breaking-change-guard.yml: fails PR if compose.yaml changes image name,
  network name, or container_name without a feat!/fix! commit (L9)

* chore(ci): fix cosign shell substitution — use tr instead of bash @L expansion (L6/nit)

* test(docker): fix compose-parity port regex for variable-interpolated host ports
2026-05-16 13:33:12 -04:00
Kai (Tam Nhu) TranandGitHub b50c2db3ce docs(docker): P3 — hoist two-command quickstart, restructure docker/README, add parity CI (#1260)
* docs: hoist Docker zero-install quickstart above npm install path

- Create docs/quickstart-snippet.md as canonical source for the
  two-command flow (curl + docker compose up -d), wrapped in
  <!-- quickstart-snippet-start/end --> markers
- Hoist the snippet into README.md immediately below the deprecation
  banner, above all other install paths
- Rename old npm-only "## Quick Start" to "## Install on Host (npm)"
  and move it below the Docker quickstart

* docs(docker): restructure README with zero-install first and migration section

- Reorder top-level sections: zero-install (canonical snippet with
  markers), choosing an image, power-user ccs docker, prebuilt image,
  connect your app to CLIProxy, migration, env vars, troubleshooting
- Add deprecation banner at the top pointing at the migration section
- Add ## Migration from ccs-dashboard:latest section with step-by-step
  instructions covering compose down, data preservation, named volume
  vs bind-mount path, and compose up with the new image
- Keep P1's Choosing an image table and P5's Connect Your App to
  CLIProxy section intact, just repositioned

* test(docs): parity check for quickstart snippet across README files

Assert README.md and docker/README.md both contain the canonical
quickstart block verbatim, anchored by marker comments. Exits non-zero
and prints a diff on any drift.

* ci(docs): wire quickstart-parity test on push and PR

Runs tests/docs/quickstart-parity.sh on self-hosted runner whenever
docs/quickstart-snippet.md, README.md, docker/README.md, or the
test/workflow files themselves change. Fails fast on snippet drift.
2026-05-16 12:56:09 -04:00
Kai (Tam Nhu) TranandGitHub 28f08cbb50 docs(docker): document ccs-net contract for sibling containers (#1259)
Add "Connect your app to CLIProxy" section to docker/README.md with:
- Public contract table (network=ccs-net, service DNS=ccs, ports 8317/3000)
- Pattern A: same compose file with external network reference
- Pattern B: docker run --network ccs-net
- Troubleshooting subsection (DNS, missing network, conflict, Podman, MTU)

Add one-line link in README.md pointing to the new section.
Add CHANGELOG entry under Unreleased noting the contract as SemVer-major stable.
Add CONTRIBUTING.md note that changing services.ccs or networks.ccs-net requires major bump.
2026-05-16 12:46:33 -04:00
Kai (Tam Nhu) TranandGitHub fbaac6a9ba feat(docker): canonical compose.yaml + smoke-test (P2 of #1251) (#1258)
* feat(docker): add canonical compose.yaml for zero-install flow (#1251)

* ci(docker): smoke-test ccs.kaitran.ca/docker-compose.yaml on release + nightly (#1251)
2026-05-16 12:39:50 -04:00
Kai (Tam Nhu) TranandGitHub d558cd2e36 feat(docker): publish ccs:latest + ccs:full integrated images (P1 of #1251) (#1257)
* feat(docker): parameterize Dockerfile.integrated with ARG FLAVOR=minimal|full

Add FLAVOR build arg that gates the AI CLI install layer (claude-code,
gemini-cli, grok-cli, opencode) so one Dockerfile produces both the
minimal (< 350 MB) and full (< 600 MB) integrated images.

Use BuildKit cache mount for /root/.npm to speed up repeated builds.
Part of #1251 (P1 — publish integrated images).

* feat(docker): emit startup deprecation warning in legacy ccs-dashboard entrypoint

Prepend a [WARN] line to stderr on every container start so operators
running ghcr.io/kaitranntt/ccs-dashboard:latest are notified to migrate
to ghcr.io/kaitranntt/ccs:latest. Sunset window: 2 releases. See #1251.

* test(docker): add image-size.sh budget assertion + unit test suite

image-size.sh: asserts docker image inspect .Size against a byte budget.
  Usage: image-size.sh <image:tag> <max-bytes>
  Budgets: minimal=350 MB (367001600), full=600 MB (629145600)

image-size-logic.test.sh: 6 mock-docker unit tests covering pass/fail
boundaries, bad arg count, and non-integer input. All 6 pass locally.
Part of #1251 (P1).

* ci(docker): extend docker-release.yml to publish ccs:latest and ccs:full

- Add publish-integrated job with matrix flavor: [minimal, full]
  - minimal -> ghcr.io/kaitranntt/ccs:<ver> + :latest (when promoted)
  - full    -> ghcr.io/kaitranntt/ccs:full-<ver> + :full (when promoted)
  - Multi-arch: linux/amd64 + linux/arm64 via buildx
  - Scoped GHA cache per flavor to avoid cross-contamination

- Add promote_to_latest workflow_dispatch input (default false)
  - rc.1 soak: first publish only pushes immutable version tag
  - Mutable :latest/:full promoted only on release event OR explicit opt-in

- Add smoke-test job (post-publish) for each flavor
  - Pulls the just-published version tag
  - Asserts image size via tests/docker/image-size.sh
  - Boots container, waits for healthcheck, probes :3000 and :8317

- Keep publish-dashboard job unchanged (legacy 2-release sunset)
  - Updated labels to note deprecation status

All jobs run on self-hosted cliproxy runners. Part of #1251 (P1).

* docs(docker): document new image tags, add ccs-dashboard deprecation notices

docker/README.md:
- Add "Choosing an image" table (ccs:latest / ccs:full / ccs-dashboard deprecated)
- Update Quick Start section to use ccs:latest as primary example
- Add legacy image note with sunset timeline

CHANGELOG.md:
- Add Unreleased > ### Deprecated entry for ccs-dashboard:latest
  pointing to migration path and #1251

README.md:
- Add one-line deprecation banner near top routing users to ccs:latest

Part of #1251 (P1).
2026-05-16 12:33:42 -04:00
Kai (Tam Nhu) TranandGitHub 2273757c83 fix(ci): harden self-hosted workflow trust boundaries
Keep active CCS workflows on self-hosted runners, gate self-hosted PR execution to trusted authors, and scope privileged release credentials to the exact git operations that need them.
2026-05-12 12:34:15 -04:00
Tam Nhu Tran 63a10e0e8d fix(ci): invoke semantic-release through bun x 2026-05-01 00:55:53 -04:00
Tam Nhu Tran 7bc1fbddb5 chore(pr): add force-UI evidence for integration PR #1152
Three light-theme captures of the merged integration branch (backend
#1141 + UI #1142 + final-touches #1151) at the dev-merge commit:

1. 01-overview-mock.png — full logs page with ?mock=logs:
   LOGS.STREAM marker, Live activity title, ENTRIES/TRACES/ERRORS
   stat strip, trace grouping with inline stage chips, populated
   detail panel showing all structured fields.
2. 02-real-backend-clean.png — real backend with default
   Hide-dashboard-internals filter active: clean empty state.
3. 03-mobile.png — 390x844 responsive layout.
2026-04-30 17:01:40 -04:00
Tam Nhu Tran 2d95e40ff9 chore(pr): pin force-UI evidence directory to PR #1151
Refs #1138
2026-04-30 14:33:50 -04:00
Tam Nhu Tran 2c0bbae943 chore(pr): add force-UI evidence for #1138 final-touches audit fixes
Light-theme captures from Playwright MCP against `bun run dev` with
`?mock=logs` deterministic fixture and real backend (no mock):

1. `01-after-overview-mock.png` — LOGS.STREAM marker, Live activity
   title, ENTRIES/TRACES/ERRORS stat strip, populated detail panel
2. `02-after-real-backend-clean.png` — real backend with default filter
   active: 0 entries because all are dashboard internals; no noise
3. `03-hide-internals-toggle.png` — advanced filters showing the new
   labelled toggle with red callout
4. `04-internals-shown-with-coalesce.png` — toggle off: dashboard
   internals visible but a 149-stage trace coalesces to 3 rows; detail
   panel shows full requestId UUID + module (C1 fix proven)
5. `05-after-mobile.png` — 390x844: header marker + title + stat strip
   wrap, expanded trace renders cleanly

Asset directory will be renamed from PLACEHOLDER_PR to actual PR number;
HEAD SHA placeholder pinned after final commit.

Refs #1138, #1141, #1142
2026-04-30 14:31:57 -04:00
Tam Nhu Tran fde0b16807 chore(pr): pin force-UI evidence to PR #1146 and HEAD SHA
Rename .github/pr-assets/PLACEHOLDER_PR -> .github/pr-assets/1146 and
substitute {HEAD_SHA_PLACEHOLDER} with the current HEAD so commit-pinned
raw GitHub URLs in the HTML report resolve correctly.

The pinned SHA in this commit is the parent commit; once this commit is
pushed, the PR body references will resolve via the htmlpreview.github.io
proxy against the parent SHA. Reviewers see frozen-in-time evidence.

Refs #1142, #1138
2026-04-30 13:07:04 -04:00
Tam Nhu Tran 1c814787a6 chore(pr): add force-UI evidence assets for #1142 logs UI redesign
Self-contained HTML report + 7 light-theme PNG captures with DOM-overlay
red callouts (#ef4444, 3px, 8px pad). Captured via Playwright MCP against
local 'bun run dev' instance with ?mock=logs deterministic 150-entry
fixture.

Surfaces:
1. Overview - default state, callouts on Live pill + ? button
2. Trace grouping - expanded vs collapsed states
3. Live-tail paused (Resume tail button)
4. Advanced filters expanded
5. Empty state - filtered-to-nothing + active filter chip
6. Keyboard shortcuts dialog
7. Mobile responsive (390x844)

Targeted-mode evidence; the 'before' UI is captured in the parent issue's
contributor screenshot. Asset directory will be renamed from PLACEHOLDER_PR
to actual PR number; HTML and asset URLs use {HEAD_SHA_PLACEHOLDER}
tokens that the maintainer pins after final commit.

Refs #1142, #1138
2026-04-30 13:05:02 -04:00
Tam Nhu Tran 4458774d5c fix(ci): align stable release runner 2026-04-28 23:30:33 -04:00
Tam Nhu Tran 0381fa9b52 fix(ci): align release sync with protected dev checks 2026-04-28 23:14:25 -04:00
Kai (Tam Nhu) TranandGitHub 8d4a7761b1 fix(ci): guard generated dev release commits 2026-04-28 22:33:14 -04:00
Kai (Tam Nhu) TranandGitHub fea7f18687 fix(ci): keep dev release commits visible to PR checks 2026-04-28 22:18:45 -04:00
Tam Nhu Tran 9a28ca55d8 fix(cliproxy): tolerate plain AI provider model rules 2026-04-28 14:19:21 -04:00
Tam Nhu Tran aa4a05e859 ci: preserve fast test budget summary on failure 2026-04-23 19:52:12 -04:00
Tam Nhu Tran 4c677507d0 ci: add fast test budget warning 2026-04-23 17:25:48 -04:00
Tam Nhu Tran ddfbcb63f4 docs(contrib): align local gate shortcuts 2026-04-22 15:12:24 -04:00
Tam Nhu Tran b314cf353e fix(ci): keep release workflows on full test coverage 2026-04-22 15:12:07 -04:00
Tam Nhu Tran c9eaae17c1 docs(contrib): clarify CI lanes and parity gates 2026-04-22 15:07:12 -04:00
Tam Nhu Tran 051f78aee7 chore(ci): split dev quality checks from release automation 2026-04-22 15:05:44 -04:00
Tam Nhu Tran e2ca197397 fix: preserve codex effort suffixes in dashboard profiles 2026-04-22 14:24:00 -04:00
Tam Nhu Tran 08edf1eef4 fix(ci): isolate bun cache per job 2026-04-21 12:34:43 -04:00
Tam Nhu Tran 06fffd3025 fix(ci): use PAT token for dev release pushes
- align dev-release workflow with protected-branch bypass used by release.yml

- add a workflow regression test so checkout and release auth do not drift back to github.token
2026-04-20 14:56:25 -04:00
Tam Nhu Tran 1b3ca82a84 fix(ci): stop caching node_modules in GitHub Actions
- cache only Bun's package cache so CI stops restoring partial hard-linked node_modules trees

- bump the cache key to avoid reusing corrupt node_modules archives on self-hosted runners
2026-04-20 13:23:42 -04:00
Tam Nhu Tran 69da284104 test(proxy): gate proxy e2e coverage in checks 2026-04-20 12:44:53 -04:00
Wooseong Kim 02747edb72 fix(ci): validate cached ui dependencies 2026-04-20 14:15:00 +09:00
Tam Nhu Tran ad54e179b6 ci: add concurrency group and split build/test with artifact sharing
- Add concurrency group keyed on github.ref with cancel-in-progress to stop superseded runs on rapid pushes
- Split validate matrix into: validate (typecheck/lint/format), build, test
- Build uploads dist/ artifact; test downloads instead of rebuilding (removes inline build:all duplication)
- Net: faster feedback, less runner time, DRY build step
2026-04-19 15:40:21 -04:00
Tam Nhu Tran b014b5cb51 chore(ci): parallelize CI matrix + cache, tighten AI-facing quality gate
- ci.yml: 5-job matrix (typecheck/lint/format/build/test), fail-fast off,
  actions/cache on ~/.bun/install/cache + node_modules + ui/node_modules.
  Target wall time ~60-90s vs prior ~3-4min.
- .husky/pre-push: add bun run build:all to feature-branch fast gate so
  UI tsc -b errors are caught pre-push (~18s warm).
- CLAUDE.md (symlinked as AGENTS.md): fix validate drift (maintainability
  is NOT part of validate), add CI-First Protocol mandating gh pr checks
  --watch after every push, replace pre-commit checklist with two-tier
  model (iterative push vs pre-merge).

Branch protection on main/dev must be updated post-merge to require the
new matrix status checks instead of the single legacy "validate" check.
2026-04-19 14:47:18 -04:00
Tam Nhu Tran 4bd16b89ce fix(ci): restore PR-Agent reviewer bot identity 2026-04-14 17:22:50 -04:00
Tam Nhu Tran 9a91901a9b fix(ci): allow PR-Agent dispatch bot reruns 2026-04-14 14:19:28 -04:00
Tam Nhu Tran 0d2ce7d1b3 fix(ci): restore trusted PR review dispatch 2026-04-14 13:47:55 -04:00
Tam Nhu Tran d6ec5eeebd fix(ci): preserve AI review variable contract 2026-04-14 13:28:29 -04:00
Tam Nhu Tran e1271e2dbf fix(ci): restrict self-hosted PR-Agent comment triggers 2026-04-14 12:43:25 -04:00
Tam Nhu Tran 25216eaf33 feat(ci): migrate AI review to self-hosted PR-Agent 2026-04-14 12:39:52 -04:00
Tam Nhu Tran 026ff6cedd refactor(ci): make ai-review model/endpoint configurable via repository variables
- replace hardcoded GLM endpoint with vars.AI_REVIEW_BASE_URL
- replace hardcoded model with vars.AI_REVIEW_MODEL
- use secrets.AI_REVIEW_API_KEY for auth token
- add configuration reference in header comment
2026-04-13 11:27:15 -04:00
Tam Nhu Tran f954f0f8ab hotfix: update ai-review workflow model budget
- switch the reviewer workflow from glm-5-turbo to glm-5.1

- increase workflow timeout to 20 minutes and max turns to 45

- lock the workflow test to the full reviewer model configuration

Closes #922
2026-04-07 16:43:30 -04:00
Tam Nhu Tran 58a0fc43e6 hotfix(ci): restore default ai review output 2026-04-02 14:36:12 -04:00
Tam Nhu Tran 0be4ef7a0d hotfix(ci): expand ai review comment layout 2026-04-02 03:14:49 -04:00
Tam Nhu Tran 25dddf4707 hotfix(ci): compact ai review comments and switch to glm-5-turbo 2026-04-02 01:07:03 -04:00
Tam Nhu Tran f6779a503a hotfix(ci): preserve literal snippet indentation in ai review comments 2026-04-02 00:48:14 -04:00
Tam Nhu Tran 07b4275543 hotfix(ci): render fenced evidence snippets in ai review comments 2026-04-02 00:44:44 -04:00
Tam Nhu Tran f47e7ae5a7 hotfix(ci): clean up ai review reruns and logging 2026-04-02 00:31:51 -04:00
Tam Nhu Tran 5b9427f8a0 hotfix(ci): restore reliable structured ai review comments 2026-04-02 00:24:29 -04:00
Tam Nhu Tran 4e05488c19 hotfix(ci): keep ai review feedback fast and deterministic 2026-04-01 22:20:55 -04:00