Merge feature/omlx-presets into dev: apply oMLX reasoning settings + … #8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Dev CI | |
| on: | |
| push: | |
| branches: [dev] | |
| pull_request: | |
| branches: [dev] | |
| types: [opened, edited, synchronize, reopened, ready_for_review] | |
| workflow_dispatch: | |
| inputs: | |
| base_ref: | |
| description: Base branch ref whose tip must equal base_sha. | |
| required: true | |
| type: string | |
| base_sha: | |
| description: Exact 40-hex base commit SHA to compare against base_ref. | |
| required: true | |
| type: string | |
| base_repository: | |
| description: Base owner/repository containing base_ref (must be this repository). | |
| required: true | |
| type: string | |
| head_sha: | |
| description: Exact 40-hex PR head commit SHA to validate against the latest terminal-green dev state. | |
| required: false | |
| type: string | |
| base_sha_override: | |
| description: Optional exact terminal-green dev SHA; validation rejects a mismatch. | |
| required: false | |
| type: string | |
| # Least privilege by default: no dev-ci job needs a write-scoped GITHUB_TOKEN. | |
| # Every job only checks out, installs, tests, and exchanges artifacts through the | |
| # Actions artifact API, so a read-scoped token is sufficient. | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.event_name == 'workflow_dispatch' && inputs.head_sha != '' && 'dev-ci-virtual-integration' || format('{0}-{1}', github.workflow, github.ref) }} | |
| cancel-in-progress: ${{ !(github.event_name == 'workflow_dispatch' && inputs.head_sha != '') }} | |
| jobs: | |
| pr-contract-bootstrap: | |
| name: PR contract bootstrap | |
| if: ${{ github.event_name == 'pull_request' }} | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| env: | |
| PR_BODY: ${{ github.event.pull_request.body }} | |
| PR_AUTHOR: ${{ github.event.pull_request.user.login }} | |
| PR_BASE_REF: ${{ github.event.pull_request.base.ref }} | |
| PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| persist-credentials: false | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Validate bootstrap PR contract | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| unset BUN_OPTIONS | |
| empty_bunfig="$RUNNER_TEMP/gjc-pr-contract-empty-bunfig.toml" | |
| printf '# trusted empty Bun configuration\n' > "$empty_bunfig" | |
| bun --no-env-file --config="$empty_bunfig" -e ' | |
| const pattern = /^gajae\.pr-review-verdict\.v1 (merge-approved|merge-blocked|needs-human) sha256:([0-9a-f]{64}) reviewer:(architect|critic|human) reviewer-id:([^\s]+) evidence:(.+)$/; | |
| const lines = (Bun.env.PR_BODY ?? "").split(/\r?\n/).map(line => line.trim()).filter(line => line.startsWith("gajae.pr-review-verdict.v1")); | |
| if (lines.length !== 1) throw new Error(`Expected exactly one verdict line; found ${lines.length}.`); | |
| const match = pattern.exec(lines[0]); | |
| if (!match) throw new Error("Malformed gajae.pr-review-verdict.v1 line."); | |
| if (Bun.env.PR_BASE_REF !== "dev") throw new Error(`PR base must be dev, not ${Bun.env.PR_BASE_REF}.`); | |
| const [verdict, declaredDigest, , reviewerId] = match.slice(1); | |
| if (verdict !== "merge-approved") throw new Error(`Verdict ${verdict} intentionally blocks merge.`); | |
| if (reviewerId.toLowerCase() === (Bun.env.PR_AUTHOR ?? "").toLowerCase()) throw new Error("merge-approved cannot be self-approved."); | |
| const run = async argv => { const child = Bun.spawn(argv, { stdout: "pipe", stderr: "pipe" }); const [stdout, stderr, exitCode] = await Promise.all([new Response(child.stdout).bytes(), new Response(child.stderr).text(), child.exited]); if (exitCode !== 0) throw new Error(`${argv.join(" ")} failed: ${stderr}`); return stdout; }; | |
| const head = new TextDecoder().decode(await run(["git", "rev-parse", "HEAD"])).trim(); | |
| if (head !== Bun.env.PR_HEAD_SHA) throw new Error(`Checked-out head ${head} != event head ${Bun.env.PR_HEAD_SHA}.`); | |
| await run(["git", "fetch", "--no-tags", "origin", Bun.env.PR_BASE_SHA]); | |
| await run(["git", "merge-base", "--is-ancestor", Bun.env.PR_BASE_SHA, head]); | |
| const diff = await run(["git", "diff", "--binary", "--full-index", "--no-ext-diff", `${Bun.env.PR_BASE_SHA}...${head}`]); | |
| const digest = new Bun.CryptoHasher("sha256").update(diff).digest("hex"); | |
| if (digest !== declaredDigest) throw new Error(`Stale verdict digest ${declaredDigest}; exact digest is ${digest}.`); | |
| const reviews = []; | |
| for (let page = 1; ; page++) { const response = await fetch(`https://api.github.com/repos/${Bun.env.GITHUB_REPOSITORY}/pulls/${Bun.env.PR_NUMBER}/reviews?per_page=100&page=${page}`, { headers: { Accept: "application/vnd.github+json", Authorization: `Bearer ${Bun.env.GITHUB_TOKEN}`, "X-GitHub-Api-Version": "2022-11-28" } }); if (!response.ok) throw new Error(`Reviews API failed: ${response.status}`); const batch = await response.json(); reviews.push(...batch); if (batch.length < 100) break; } | |
| const latest = reviews.filter(review => review.user?.login?.toLowerCase() === reviewerId.toLowerCase() && review.state !== "COMMENTED" && review.commit_id === head).at(-1); | |
| if (latest?.state !== "APPROVED" || latest.commit_id !== head) throw new Error(`Reviewer ${reviewerId} lacks an effective exact-head approval.`); | |
| const permissionResponse = await fetch(`https://api.github.com/repos/${Bun.env.GITHUB_REPOSITORY}/collaborators/${encodeURIComponent(reviewerId)}/permission`, { headers: { Accept: "application/vnd.github+json", Authorization: `Bearer ${Bun.env.GITHUB_TOKEN}`, "X-GitHub-Api-Version": "2022-11-28" } }); | |
| if (!permissionResponse.ok) throw new Error(`Reviewer permission lookup failed: ${permissionResponse.status}`); | |
| const permission = (await permissionResponse.json()).permission; | |
| if (!["admin", "maintain", "write"].includes(permission)) throw new Error(`Reviewer ${reviewerId} lacks repository review authority.`); | |
| console.log(`Bootstrap PR contract valid: ${digest}`); | |
| ' | |
| - name: Run bootstrap G1 writer gate | |
| run: bun scripts/verify-gjc-state-writers.ts --fail --root . | |
| # Planner: resolve changed-path relevance and the affected task plan once, then | |
| # fan the plan out into per-task shards. Emits the matrix, has_tasks/has_native | |
| # flags, and the resolved changed paths so every downstream job reuses this | |
| # exact diff via CI_DEV_CHANGED_PATHS instead of re-resolving the base ref. | |
| affected-plan: | |
| name: Affected path validation / plan | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.head_sha != '') }} | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 10 | |
| outputs: | |
| relevant: ${{ steps.relevance.outputs.relevant }} | |
| matrix: ${{ steps.plan.outputs.matrix }} | |
| has_tasks: ${{ steps.plan.outputs.has_tasks }} | |
| has_native: ${{ steps.plan.outputs.has_native }} | |
| has_python: ${{ steps.plan.outputs.has_python }} | |
| has_risk_canaries: ${{ steps.plan.outputs.has_risk_canaries }} | |
| has_darwin_arm64_tab_worker_smoke: ${{ steps.plan.outputs.has_darwin_arm64_tab_worker_smoke }} | |
| has_windows_session_path: ${{ steps.plan.outputs.has_windows_session_path }} | |
| changed_paths: ${{ steps.plan.outputs.changed_paths }} | |
| plan_mode: ${{ steps.plan.outputs.plan_mode }} | |
| plan_digest: ${{ steps.plan.outputs.plan_digest }} | |
| plan_source_sha: ${{ steps.plan.outputs.plan_source_sha }} | |
| env: | |
| GITHUB_EVENT_BEFORE: ${{ github.event.before }} | |
| GITHUB_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'workflow_dispatch' && inputs.base_sha || github.event.before }} | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Verify checked-out source head | |
| shell: bash | |
| run: | | |
| head="$(git rev-parse HEAD)" | |
| test "$head" = "$CI_DEV_SOURCE_SHA" || { echo "Checked-out SHA $head does not match $CI_DEV_SOURCE_SHA"; exit 1; } | |
| - name: Verify PR head contains exact base | |
| if: ${{ github.event_name == 'pull_request' }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if ! git fetch --no-tags origin "${GITHUB_BASE_SHA}"; then | |
| echo "::error::Could not fetch immutable event base ${GITHUB_BASE_SHA} from ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}; rerun the PR event or rebase onto current ${GITHUB_BASE_REF}." | |
| exit 1 | |
| fi | |
| if git merge-base --is-ancestor "${GITHUB_BASE_SHA}" HEAD; then | |
| : | |
| else | |
| status=$? | |
| if [ "$status" -eq 1 ]; then | |
| echo "::error::Exact-head CI requires this PR head to contain base ${GITHUB_BASE_SHA}; rebase onto current ${GITHUB_BASE_REF}." | |
| else | |
| echo "::error::Could not compare exact PR head ${CI_DEV_SOURCE_SHA} with immutable event base ${GITHUB_BASE_SHA} (merge-base exit ${status})." | |
| fi | |
| exit 1 | |
| fi | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| with: | |
| toolchain: nightly-2026-04-29 | |
| # Released CHANGELOG sections are append-only. These files have no | |
| # `merge=union` driver, so a rebase conflicts here for real and a bad | |
| # resolution can silently drop the whole file — which is exactly what | |
| # happened to ten open PRs across six authors within ten minutes of the | |
| # driver being removed. Runs in affected-plan because it already has | |
| # full history and the immutable event base sha. | |
| - name: Guard released CHANGELOG history | |
| if: ${{ github.event_name == 'pull_request' }} | |
| run: bun scripts/changelog-history-guard.ts | |
| - name: Compute changed-path relevance | |
| id: relevance | |
| run: bun scripts/ci-job-relevance.ts | |
| - name: Compute affected task matrix | |
| id: plan | |
| run: bun scripts/ci-dev-affected.ts --matrix-json | |
| - name: Upload canonical affected plan | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: dev-affected-plan-${{ github.run_id }} | |
| path: .ci-dev-affected-plan.json | |
| include-hidden-files: true | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true | |
| telegram-daemon-generation: | |
| name: Telegram daemon generation guard | |
| needs: [affected-plan] | |
| if: ${{ needs.affected-plan.outputs.relevant == 'true' && (contains(needs.affected-plan.outputs.changed_paths, 'telegram-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/broker/process-incarnation.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/host/host.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/client/discovery.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/telegram-daemon-generation-guard.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/telegram-daemon-generation-manifest.json')) }} | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 15 | |
| env: | |
| GITHUB_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'workflow_dispatch' && inputs.base_sha || github.event.before }} | |
| GITHUB_HEAD_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} | |
| BASE_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || github.event_name == 'workflow_dispatch' && inputs.base_ref || github.ref_name }} | |
| HEAD_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref_name }} | |
| BASE_REPOSITORY: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.repo.full_name || github.event_name == 'workflow_dispatch' && inputs.base_repository || github.repository }} | |
| HEAD_REPOSITORY: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }} | |
| GUARD_EVENT_NAME: ${{ github.event_name }} | |
| GUARD_REPOSITORY: ${{ github.repository }} | |
| steps: | |
| - name: Validate exact guard inputs | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| sha='^[0-9a-fA-F]{40}$' | |
| repo='^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$' | |
| [[ "${GITHUB_BASE_SHA}" =~ ${sha} ]] || { echo "Guard base SHA must be an exact 40-hex commit"; exit 1; } | |
| [[ "${GITHUB_HEAD_SHA}" =~ ${sha} ]] || { echo "Guard head SHA must be an exact 40-hex commit"; exit 1; } | |
| [[ "${BASE_REPOSITORY}" =~ ${repo} ]] || { echo "Guard base repository is invalid"; exit 1; } | |
| [[ "${HEAD_REPOSITORY}" =~ ${repo} ]] || { echo "Guard head repository is invalid"; exit 1; } | |
| [[ "${GUARD_REPOSITORY}" =~ ${repo} ]] || { echo "Guard repository is invalid"; exit 1; } | |
| git check-ref-format --branch "${BASE_REF}" >/dev/null || { echo "Guard base ref is not a valid branch ref"; exit 1; } | |
| git check-ref-format --branch "${HEAD_REF}" >/dev/null || { echo "Guard head ref is not a valid branch ref"; exit 1; } | |
| case "${GUARD_EVENT_NAME}" in | |
| pull_request) [[ "${BASE_REPOSITORY}" == "${GUARD_REPOSITORY}" ]] || { echo "PR base repository must be this repository"; exit 1; } ;; | |
| push|workflow_dispatch) [[ "${BASE_REPOSITORY}" == "${GUARD_REPOSITORY}" && "${HEAD_REPOSITORY}" == "${GUARD_REPOSITORY}" ]] || { echo "Push and dispatch repositories must be this repository"; exit 1; } ;; | |
| *) echo "Unsupported guard event"; exit 1 ;; | |
| esac | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| repository: ${{ env.HEAD_REPOSITORY }} | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| fetch-depth: 0 | |
| - name: Verify checked-out source head | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| head="$(git rev-parse HEAD)" | |
| [[ "${head}" == "${GITHUB_HEAD_SHA}" ]] || { echo "Checked-out SHA ${head} does not match ${GITHUB_HEAD_SHA}"; exit 1; } | |
| - name: Fetch and prove authoritative guard revisions | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git remote add guard-head "https://github.com/${HEAD_REPOSITORY}.git" | |
| git remote add guard-base "https://github.com/${BASE_REPOSITORY}.git" | |
| git fetch --no-tags guard-head "refs/heads/${HEAD_REF}:refs/remotes/guard-head/${HEAD_REF}" | |
| base_ref_sha='' | |
| case "${GUARD_EVENT_NAME}" in | |
| pull_request) | |
| # The immutable event base object remains authoritative while a queued | |
| # pull request's live base branch advances. | |
| git fetch --no-tags guard-base "${GITHUB_BASE_SHA}" | |
| ;; | |
| workflow_dispatch) | |
| git fetch --no-tags guard-base "refs/heads/${BASE_REF}:refs/remotes/guard-base/${BASE_REF}" | |
| base_ref_sha="$(git rev-parse --verify "refs/remotes/guard-base/${BASE_REF}^{commit}")" | |
| [[ "${base_ref_sha}" == "${GITHUB_BASE_SHA}" ]] || { echo "Dispatch base ref ${BASE_REF} resolves to ${base_ref_sha}, not ${GITHUB_BASE_SHA}"; exit 1; } | |
| ;; | |
| push) | |
| git fetch --no-tags guard-base "${GITHUB_BASE_SHA}" | |
| ;; | |
| esac | |
| { | |
| echo "GUARD_CHECKED_OUT_HEAD=$(git rev-parse --verify HEAD^{commit})" | |
| echo "GUARD_HEAD_REF_SHA=$(git rev-parse --verify "refs/remotes/guard-head/${HEAD_REF}^{commit}")" | |
| echo "GUARD_BASE_OBJECT_SHA=$(git rev-parse --verify "${GITHUB_BASE_SHA}^{commit}")" | |
| echo "GUARD_BASE_REF_SHA=${base_ref_sha}" | |
| } >> "${GITHUB_ENV}" | |
| printf 'guard evidence: base %s@%s; head %s@%s\n' "${BASE_REPOSITORY}" "${GITHUB_BASE_SHA}" "${HEAD_REPOSITORY}" "${HEAD_REF}" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Cache bun dependencies | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-1.3.14-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} | |
| # The guard's AST canonicalization is parser-version sensitive: install the | |
| # pinned @babel/parser from the lockfile so the current-tree digest check is | |
| # deterministic and matches the committed attestations (no auto-install drift). | |
| - run: bun install --frozen-lockfile | |
| - run: bun scripts/telegram-daemon-generation-guard.ts --check-authority | |
| - run: bun scripts/telegram-daemon-generation-guard.ts | |
| windows-dev-doctor: | |
| name: Windows dev:doctor + session-path regression | |
| needs: [affected-plan] | |
| if: ${{ needs.affected-plan.outputs.relevant == 'true' && (contains(needs.affected-plan.outputs.changed_paths, 'scripts/dev-link') || needs.affected-plan.outputs.has_windows_session_path == 'true' || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/session/blob-store.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/session/session-manager.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/test/session/resident-cache-win32-gate.windows.test.ts')) }} | |
| runs-on: windows-latest | |
| timeout-minutes: 60 | |
| env: | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Verify checked-out source head | |
| shell: pwsh | |
| run: | | |
| $head = (git rev-parse HEAD).Trim() | |
| if ($head -ne $env:CI_DEV_SOURCE_SHA) { throw "Checked-out SHA $head does not match $env:CI_DEV_SOURCE_SHA" } | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| with: | |
| toolchain: nightly-2026-04-29 | |
| - name: Prepend rustup toolchain bin to PATH | |
| shell: bash | |
| run: | | |
| toolchain_bin="$(dirname "$(rustup which cargo)")" | |
| echo "$toolchain_bin" >> "$GITHUB_PATH" | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| shared-key: windows-dev-doctor-win32-x64 | |
| cache-on-failure: true | |
| save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' }} | |
| cache-workspace-crates: true | |
| - name: Cache bun dependencies | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-1.3.14-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} | |
| - run: bun install --frozen-lockfile | |
| - name: Build native addon (win32-x64 baseline) | |
| env: | |
| TARGET_PLATFORM: win32 | |
| TARGET_ARCH: x64 | |
| TARGET_VARIANTS: baseline | |
| run: bun run ci:build:native | |
| - name: Verify Windows workspace shim and doctor | |
| shell: pwsh | |
| run: | | |
| bun test ./scripts/dev-link.test.ts | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| bun run dev:doctor | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| - name: Windows session-path canonicalization regression | |
| shell: pwsh | |
| run: | | |
| bun test ./packages/coding-agent/test/session-manager/windows-canonical-path.test.ts | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| bun test ./packages/coding-agent/test/session/resident-cache-win32-gate.windows.test.ts | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| bun test ./packages/coding-agent/test/session/managed-lock-lease.windows.test.ts | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| bun test ./packages/coding-agent/test/sdk-session-directory.windows.test.ts | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| bun test ./packages/coding-agent/test/sdk-session-index-fsync.windows.test.ts | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| windows-native-build-toolchain: | |
| name: Windows native build toolchain path | |
| needs: [affected-plan] | |
| if: ${{ needs.affected-plan.outputs.relevant == 'true' && (contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/scripts/build-native.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/scripts/rust-toolchain-path.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/test/build-native-profile.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/ci-build-native.ts')) }} | |
| runs-on: windows-latest | |
| timeout-minutes: 60 | |
| env: | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Verify checked-out source head | |
| shell: pwsh | |
| run: | | |
| $head = (git rev-parse HEAD).Trim() | |
| if ($head -ne $env:CI_DEV_SOURCE_SHA) { throw "Checked-out SHA $head does not match $env:CI_DEV_SOURCE_SHA" } | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| with: | |
| toolchain: nightly-2026-04-29 | |
| - name: Cache bun dependencies | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-1.3.14-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} | |
| - run: bun install --frozen-lockfile | |
| - name: Run native build toolchain tests | |
| run: bun test packages/natives/test/build-native-profile.test.ts | |
| - name: Build native addon (win32-x64 baseline) | |
| env: | |
| TARGET_PLATFORM: win32 | |
| TARGET_ARCH: x64 | |
| TARGET_VARIANTS: baseline | |
| run: bun run ci:build:native | |
| windows-telegram-daemon-safety: | |
| name: Windows Telegram daemon safety | |
| needs: [affected-plan] | |
| if: ${{ needs.affected-plan.outputs.relevant == 'true' && (contains(needs.affected-plan.outputs.changed_paths, 'telegram-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon-control.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/broker/process-incarnation.ts') || contains(needs.affected-plan.outputs.changed_paths, 'daemon-control.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'notifications-telegram-daemon.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-natives/src/path_identity.rs') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-natives/src/ps.rs') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-shell/src/process.rs') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/native/index.d.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/native/index.js') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/test/path-identity-windows.test.ts')) }} | |
| runs-on: windows-latest | |
| timeout-minutes: 60 | |
| env: | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Verify checked-out source head | |
| shell: pwsh | |
| run: | | |
| $head = (git rev-parse HEAD).Trim() | |
| if ($head -ne $env:CI_DEV_SOURCE_SHA) { throw "Checked-out SHA $head does not match $env:CI_DEV_SOURCE_SHA" } | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| with: | |
| toolchain: nightly-2026-04-29 | |
| - name: Prepend rustup toolchain bin to PATH | |
| shell: bash | |
| run: | | |
| toolchain_bin="$(dirname "$(rustup which cargo)")" | |
| echo "$toolchain_bin" >> "$GITHUB_PATH" | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| shared-key: windows-telegram-daemon-safety-win32-x64 | |
| cache-on-failure: true | |
| cache-workspace-crates: true | |
| - name: Cache bun dependencies | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-1.3.14-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} | |
| - run: bun install --frozen-lockfile | |
| - name: Build native addon (win32-x64 baseline) | |
| env: | |
| TARGET_PLATFORM: win32 | |
| TARGET_ARCH: x64 | |
| TARGET_VARIANTS: baseline | |
| run: bun run ci:build:native | |
| - name: Run Windows daemon provenance safety contract | |
| shell: pwsh | |
| run: | | |
| bun test ./packages/coding-agent/test/daemon-control.test.ts --test-name-pattern 'incarnation|captured-owner|owner-lock|poll overlap|configured chat providers|hard Windows authority' | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| bun test ./packages/coding-agent/test/notifications-telegram-daemon.test.ts --test-name-pattern 'provider owner state contains transport authority|constructing or restarting provider transport cannot mutate session lifecycle' | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| bun test ./packages/natives/test/native.test.ts --test-name-pattern 'signals only the pinned root process' | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| bun test ./packages/natives/test/path-identity-windows.test.ts | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| # Native addon build runs at most once per run and publishes the built `.node` | |
| # files as an artifact the runtime-dependent shards download. A content-hash | |
| # cache of the native sources lets PRs that don't touch crates/natives restore | |
| # the prebuilt addon and skip the Rust/native source build entirely; only a | |
| # cache miss (native source actually changed) recompiles. Skipped when the plan | |
| # needs no native build at all. | |
| affected-native: | |
| name: Affected path validation / native-build | |
| needs: [affected-plan] | |
| if: ${{ needs.affected-plan.outputs.has_native == 'true' }} | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 30 | |
| env: | |
| CI_DEV_CHANGED_PATHS: ${{ needs.affected-plan.outputs.changed_paths }} | |
| CI_DEV_PLAN_MODE: ${{ needs.affected-plan.outputs.plan_mode }} | |
| CI_DEV_AFFECTED_PLAN: .ci-dev-affected-plan.json | |
| CI_DEV_PLAN_DIGEST: ${{ needs.affected-plan.outputs.plan_digest }} | |
| CI_DEV_PLAN_SOURCE_SHA: ${{ needs.affected-plan.outputs.plan_source_sha }} | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Verify checked-out source head | |
| shell: bash | |
| run: | | |
| head="$(git rev-parse HEAD)" | |
| test "$head" = "$CI_DEV_SOURCE_SHA" || { echo "Checked-out SHA $head does not match $CI_DEV_SOURCE_SHA"; exit 1; } | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Download and validate canonical affected plan | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: dev-affected-plan-${{ github.run_id }} | |
| path: . | |
| - run: bun scripts/ci-dev-affected.ts --validate-plan | |
| - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| with: | |
| toolchain: nightly-2026-04-29 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| shared-key: dev-affected-native-linux-x64 | |
| cache-on-failure: true | |
| save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' }} | |
| cache-workspace-crates: true | |
| - name: Cache bun dependencies | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-1.3.14-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} | |
| - name: Install system deps | |
| run: bash scripts/ci-install-system-deps.sh | |
| - run: bun install --frozen-lockfile | |
| - name: Build affected native addon(s) | |
| run: bun scripts/ci-dev-affected.ts --native-build | |
| - name: Verify required native addon variants | |
| run: | | |
| test -f packages/natives/native/pi_natives.linux-x64-baseline.node | |
| test -f packages/natives/native/pi_natives.linux-x64-modern.node | |
| - name: Upload native addon(s) | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: dev-affected-native-${{ github.run_id }} | |
| path: | | |
| packages/natives/native/pi_natives.linux-x64-baseline.node | |
| packages/natives/native/pi_natives.linux-x64-modern.node | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true | |
| # Darwin arm64 builds the checked-out PR head end-to-end for every compiled | |
| # tab-worker smoke-graph path. The planner emits this canonical relevance flag. | |
| affected-darwin-arm64-tab-worker-smoke: | |
| name: Affected path validation / darwin-arm64 tab-worker smoke | |
| needs: [affected-plan] | |
| if: ${{ needs.affected-plan.outputs.has_darwin_arm64_tab_worker_smoke == 'true' }} | |
| runs-on: macos-14 | |
| timeout-minutes: 45 | |
| env: | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Verify checked-out source head | |
| shell: bash | |
| run: | | |
| head="$(git rev-parse HEAD)" | |
| test "$head" = "$CI_DEV_SOURCE_SHA" || { echo "Checked-out SHA $head does not match $CI_DEV_SOURCE_SHA"; exit 1; } | |
| node -e 'if (process.platform !== "darwin" || process.arch !== "arm64") { throw new Error(`Expected darwin/arm64, got ${process.platform}/${process.arch}`) }' | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| with: | |
| toolchain: nightly-2026-04-29 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| shared-key: dev-affected-darwin-arm64-tab-worker | |
| cache-on-failure: true | |
| save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' }} | |
| cache-workspace-crates: true | |
| - name: Cache bun dependencies | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-1.3.14-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} | |
| - run: bun install --frozen-lockfile | |
| - name: Build native addon (darwin-arm64) | |
| env: | |
| TARGET_PLATFORM: darwin | |
| TARGET_ARCH: arm64 | |
| run: bun run ci:build:native | |
| - name: Build darwin-arm64 coding-agent binary | |
| run: bun --cwd=packages/coding-agent run build | |
| - name: Smoke compiled tab worker with fresh owner directories | |
| shell: bash | |
| run: | | |
| runtime_dir="$(mktemp -d)" | |
| mkdir -p "$runtime_dir/home" "$runtime_dir/xdg" | |
| HOME="$runtime_dir/home" XDG_CONFIG_HOME="$runtime_dir/xdg/config" XDG_DATA_HOME="$runtime_dir/xdg/data" XDG_CACHE_HOME="$runtime_dir/xdg/cache" packages/coding-agent/dist/gjc --smoke-test | |
| printf 'CI_DEV_DARWIN_SMOKE_HOME=%s\n' "$runtime_dir/home" >> "$GITHUB_ENV" | |
| printf 'CI_DEV_DARWIN_SMOKE_XDG_CONFIG_HOME=%s\n' "$runtime_dir/xdg/config" >> "$GITHUB_ENV" | |
| printf 'CI_DEV_DARWIN_SMOKE_XDG_DATA_HOME=%s\n' "$runtime_dir/xdg/data" >> "$GITHUB_ENV" | |
| printf 'CI_DEV_DARWIN_SMOKE_XDG_CACHE_HOME=%s\n' "$runtime_dir/xdg/cache" >> "$GITHUB_ENV" | |
| - name: Write immutable Darwin smoke receipt | |
| env: | |
| CI_DEV_DARWIN_BINARY: packages/coding-agent/dist/gjc | |
| CI_DEV_DARWIN_NATIVE_ADDON: packages/natives/native/pi_natives.darwin-arm64.node | |
| run: bun scripts/ci-validate-darwin-receipt.ts --write | |
| - name: Upload Darwin smoke receipt | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: dev-affected-darwin-receipt-${{ github.run_id }} | |
| path: .ci-dev-darwin-arm64-receipt.json | |
| include-hidden-files: true | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true | |
| # One shard per planned task on the broad runner. Native build tasks are | |
| # excluded (they run in affected-native); shards that load the native addon at | |
| # runtime download the prebuilt artifact instead of rebuilding it. | |
| affected-shards: | |
| name: Affected path validation / ${{ matrix.key }} | |
| needs: [affected-plan, affected-native] | |
| if: ${{ always() && needs.affected-plan.outputs.has_tasks == 'true' && needs.affected-native.result != 'failure' && needs.affected-native.result != 'cancelled' }} | |
| runs-on: ubuntu-22.04 | |
| # Broad push-mode coding-agent/root test shards can need up to 90 minutes, but | |
| # the bounded root-check must retain the same 30-minute fail-fast contract as | |
| # Main CI. SDK closure remains outside that CI command. | |
| timeout-minutes: ${{ matrix.key == 'root-check' && 30 || 90 }} | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 8 | |
| matrix: ${{ fromJSON(needs.affected-plan.outputs.matrix) }} | |
| env: | |
| CI_DEV_CHANGED_PATHS: ${{ needs.affected-plan.outputs.changed_paths }} | |
| CI_DEV_PLAN_MODE: ${{ needs.affected-plan.outputs.plan_mode }} | |
| CI_DEV_AFFECTED_PLAN: .ci-dev-affected-plan.json | |
| CI_DEV_PLAN_DIGEST: ${{ needs.affected-plan.outputs.plan_digest }} | |
| CI_DEV_PLAN_SOURCE_SHA: ${{ needs.affected-plan.outputs.plan_source_sha }} | |
| CI_DEV_MATRIX_KEY: ${{ matrix.key }} | |
| CI_DEV_MATRIX_IDENTITY: ${{ matrix.identity }} | |
| CI_DEV_SHARD_INDEX: ${{ strategy.job-index }} | |
| CI_DEV_MATRIX_RUST: ${{ matrix.rust }} | |
| CI_DEV_MATRIX_NEXTEST: ${{ matrix.nextest }} | |
| CI_DEV_MATRIX_NATIVE: ${{ matrix.native }} | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| fetch-depth: 0 | |
| - name: Verify checked-out source head | |
| shell: bash | |
| run: | | |
| head="$(git rev-parse HEAD)" | |
| test "$head" = "$CI_DEV_SOURCE_SHA" || { echo "Checked-out SHA $head does not match $CI_DEV_SOURCE_SHA"; exit 1; } | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Download and validate canonical affected plan | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: dev-affected-plan-${{ github.run_id }} | |
| path: . | |
| - run: bun scripts/ci-dev-affected.ts --validate-plan | |
| - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| if: ${{ matrix.rust }} | |
| with: | |
| toolchain: nightly-2026-04-29 | |
| - uses: taiki-e/install-action@56545b37b57562edd73171cb6c62cc509db4c34e # v2 | |
| if: ${{ matrix.nextest }} | |
| with: | |
| tool: nextest@0.9.137 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| if: ${{ matrix.rust }} | |
| with: | |
| shared-key: dev-affected-rust-linux-x64 | |
| cache-on-failure: true | |
| save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' }} | |
| cache-workspace-crates: true | |
| - name: Cache bun dependencies | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-1.3.14-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} | |
| - name: Install system deps | |
| run: bash scripts/ci-install-system-deps.sh | |
| - run: bun install --frozen-lockfile | |
| - name: Download native addon(s) | |
| if: ${{ matrix.native }} | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: dev-affected-native-${{ github.run_id }} | |
| path: packages/natives/native | |
| - name: Run affected task shard | |
| env: | |
| AFFECTED_TASK_KEY: ${{ matrix.key }} | |
| GITHUB_EVENT_BEFORE: ${{ github.event.before }} | |
| GITHUB_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'workflow_dispatch' && inputs.base_sha || github.event.before }} | |
| run: bun scripts/ci-dev-affected.ts --task="$AFFECTED_TASK_KEY" | |
| - name: Write shard completion receipt | |
| run: | | |
| bun -e 'await Bun.write(`.ci-dev-shard-receipts/${process.env.CI_DEV_SHARD_INDEX}.json`, JSON.stringify({ key: process.env.AFFECTED_TASK_KEY, identity: process.env.CI_DEV_MATRIX_IDENTITY }))' | |
| env: | |
| AFFECTED_TASK_KEY: ${{ matrix.key }} | |
| - name: Upload shard completion receipt | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: dev-affected-shard-${{ github.run_id }}-${{ strategy.job-index }} | |
| path: .ci-dev-shard-receipts/${{ strategy.job-index }}.json | |
| include-hidden-files: true | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true | |
| # This producer is deliberately not the protected status: the downstream job | |
| # validates the finalized bundle downloaded by its immutable artifact ID. | |
| affected-evidence-producer: | |
| name: Affected path validation / evidence producer | |
| if: ${{ always() && !(github.event_name == 'workflow_dispatch' && inputs.head_sha != '') }} | |
| needs: [affected-plan, affected-native, affected-shards, telegram-daemon-generation, windows-dev-doctor, windows-native-build-toolchain, windows-telegram-daemon-safety, affected-darwin-arm64-tab-worker-smoke] | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 5 | |
| outputs: | |
| artifact_id: ${{ steps.upload-evidence.outputs.artifact-id }} | |
| artifact_digest: ${{ steps.upload-evidence.outputs.artifact-digest }} | |
| env: | |
| CI_DEV_AFFECTED_PLAN: .ci-dev-affected-plan.json | |
| CI_DEV_PLAN_DIGEST: ${{ needs.affected-plan.outputs.plan_digest }} | |
| CI_DEV_PLAN_SOURCE_SHA: ${{ needs.affected-plan.outputs.plan_source_sha }} | |
| CI_DEV_PLAN_MODE: ${{ needs.affected-plan.outputs.plan_mode }} | |
| CI_DEV_SHARD_RECEIPTS: .ci-dev-shard-receipts | |
| CI_DEV_EVIDENCE_ROOT: . | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Verify checked-out source head | |
| shell: bash | |
| run: | | |
| head="$(git rev-parse HEAD)" | |
| test "$head" = "$CI_DEV_SOURCE_SHA" || { echo "Checked-out SHA $head does not match $CI_DEV_SOURCE_SHA"; exit 1; } | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Download canonical affected plan | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: dev-affected-plan-${{ github.run_id }} | |
| path: . | |
| - name: Download shard completion receipts | |
| if: ${{ needs.affected-plan.result == 'success' && needs.affected-plan.outputs.has_tasks == 'true' && needs.affected-shards.result == 'success' }} | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| pattern: dev-affected-shard-${{ github.run_id }}-* | |
| path: .ci-dev-shard-receipts | |
| merge-multiple: true | |
| - name: Validate canonical shard completion | |
| if: ${{ needs.affected-plan.result == 'success' && needs.affected-plan.outputs.has_tasks == 'true' && needs.affected-shards.result == 'success' }} | |
| run: bun scripts/ci-dev-affected.ts --validate-shard-receipts | |
| - name: Download Darwin smoke receipt | |
| if: ${{ needs.affected-plan.result == 'success' && needs.affected-plan.outputs.has_darwin_arm64_tab_worker_smoke == 'true' && needs.affected-darwin-arm64-tab-worker-smoke.result == 'success' }} | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: dev-affected-darwin-receipt-${{ github.run_id }} | |
| path: . | |
| - name: Validate Darwin smoke receipt | |
| if: ${{ needs.affected-plan.result == 'success' && needs.affected-plan.outputs.has_darwin_arm64_tab_worker_smoke == 'true' && needs.affected-darwin-arm64-tab-worker-smoke.result == 'success' }} | |
| run: bun scripts/ci-validate-darwin-receipt.ts | |
| - name: Produce affected evidence | |
| env: | |
| CI_DEV_PLAN_RESULT: ${{ needs.affected-plan.result }} | |
| CI_DEV_NATIVE_RESULT: ${{ needs.affected-native.result }} | |
| CI_DEV_SHARDS_RESULT: ${{ needs.affected-shards.result }} | |
| CI_DEV_HAS_NATIVE: ${{ needs.affected-plan.outputs.has_native }} | |
| CI_DEV_HAS_TASKS: ${{ needs.affected-plan.outputs.has_tasks }} | |
| CI_DEV_HAS_PYTHON: ${{ needs.affected-plan.outputs.has_python }} | |
| CI_DEV_PYTHON_RESULT: skipped | |
| CI_DEV_WINDOWS_DOCTOR_RESULT: ${{ needs.windows-dev-doctor.result }} | |
| CI_DEV_WINDOWS_DOCTOR_REQUIRED: ${{ contains(needs.affected-plan.outputs.changed_paths, 'scripts/dev-link') || needs.affected-plan.outputs.has_windows_session_path == 'true' || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/session/blob-store.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/session/session-manager.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/test/session/resident-cache-win32-gate.windows.test.ts') }} | |
| CI_DEV_WINDOWS_NATIVE_TOOLCHAIN_RESULT: ${{ needs.windows-native-build-toolchain.result }} | |
| CI_DEV_WINDOWS_NATIVE_TOOLCHAIN_REQUIRED: ${{ contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/scripts/build-native.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/scripts/rust-toolchain-path.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/test/build-native-profile.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/ci-build-native.ts') }} | |
| CI_DEV_TELEGRAM_GUARD_RESULT: ${{ needs.telegram-daemon-generation.result }} | |
| CI_DEV_TELEGRAM_GUARD_REQUIRED: ${{ contains(needs.affected-plan.outputs.changed_paths, 'telegram-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/broker/process-incarnation.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/host/host.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/client/discovery.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/telegram-daemon-generation-guard.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/telegram-daemon-generation-manifest.json') }} | |
| CI_DEV_TELEGRAM_WINDOWS_RESULT: ${{ needs.windows-telegram-daemon-safety.result }} | |
| CI_DEV_TELEGRAM_WINDOWS_REQUIRED: ${{ contains(needs.affected-plan.outputs.changed_paths, 'telegram-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon-control.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/broker/process-incarnation.ts') || contains(needs.affected-plan.outputs.changed_paths, 'daemon-control.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'notifications-telegram-daemon.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-natives/src/path_identity.rs') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-natives/src/ps.rs') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-shell/src/process.rs') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/native/index.d.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/native/index.js') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/test/path-identity-windows.test.ts') }} | |
| CI_DEV_DARWIN_ARM64_TAB_WORKER_SMOKE_RESULT: ${{ needs.affected-darwin-arm64-tab-worker-smoke.result }} | |
| CI_DEV_DARWIN_ARM64_TAB_WORKER_SMOKE_REQUIRED: ${{ needs.affected-plan.outputs.has_darwin_arm64_tab_worker_smoke }} | |
| run: bun scripts/ci-dev-affected.ts --write-affected-evidence | |
| - name: Upload affected evidence | |
| id: upload-evidence | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: dev-affected-evidence-${{ github.run_id }} | |
| path: | | |
| .ci-dev-affected-evidence.json | |
| .ci-dev-affected-evidence.receipt.json | |
| .ci-dev-affected-plan.json | |
| .ci-dev-shard-receipts | |
| .ci-dev-darwin-arm64-receipt.json | |
| include-hidden-files: true | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true | |
| affected: | |
| name: Affected path validation | |
| if: ${{ always() && !(github.event_name == 'workflow_dispatch' && inputs.head_sha != '') }} | |
| needs: [affected-evidence-producer, affected-plan, affected-native, affected-shards, telegram-daemon-generation, windows-dev-doctor, windows-native-build-toolchain, windows-telegram-daemon-safety, affected-darwin-arm64-tab-worker-smoke] | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 5 | |
| env: | |
| CI_DEV_PLAN_DIGEST: ${{ needs.affected-plan.outputs.plan_digest }} | |
| CI_DEV_PLAN_SOURCE_SHA: ${{ needs.affected-plan.outputs.plan_source_sha }} | |
| CI_DEV_PLAN_MODE: ${{ needs.affected-plan.outputs.plan_mode }} | |
| CI_DEV_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| CI_DEV_PLAN_RESULT: ${{ needs.affected-plan.result }} | |
| CI_DEV_NATIVE_RESULT: ${{ needs.affected-native.result }} | |
| CI_DEV_SHARDS_RESULT: ${{ needs.affected-shards.result }} | |
| CI_DEV_HAS_NATIVE: ${{ needs.affected-plan.outputs.has_native }} | |
| CI_DEV_HAS_TASKS: ${{ needs.affected-plan.outputs.has_tasks }} | |
| CI_DEV_HAS_PYTHON: ${{ needs.affected-plan.outputs.has_python }} | |
| CI_DEV_PYTHON_RESULT: skipped | |
| CI_DEV_WINDOWS_DOCTOR_RESULT: ${{ needs.windows-dev-doctor.result }} | |
| CI_DEV_WINDOWS_DOCTOR_REQUIRED: ${{ contains(needs.affected-plan.outputs.changed_paths, 'scripts/dev-link') || needs.affected-plan.outputs.has_windows_session_path == 'true' || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/session/blob-store.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/session/session-manager.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/test/session/resident-cache-win32-gate.windows.test.ts') }} | |
| CI_DEV_WINDOWS_NATIVE_TOOLCHAIN_RESULT: ${{ needs.windows-native-build-toolchain.result }} | |
| CI_DEV_WINDOWS_NATIVE_TOOLCHAIN_REQUIRED: ${{ contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/scripts/build-native.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/scripts/rust-toolchain-path.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/test/build-native-profile.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/ci-build-native.ts') }} | |
| CI_DEV_DARWIN_ARM64_TAB_WORKER_SMOKE_RESULT: ${{ needs.affected-plan.outputs.has_darwin_arm64_tab_worker_smoke == 'true' && 'success' || 'skipped' }} | |
| CI_DEV_DARWIN_ARM64_TAB_WORKER_SMOKE_REQUIRED: ${{ needs.affected-plan.outputs.has_darwin_arm64_tab_worker_smoke }} | |
| CI_DEV_TELEGRAM_GUARD_RESULT: ${{ needs.telegram-daemon-generation.result }} | |
| CI_DEV_TELEGRAM_GUARD_REQUIRED: ${{ contains(needs.affected-plan.outputs.changed_paths, 'telegram-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/broker/process-incarnation.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/host/host.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/client/discovery.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/telegram-daemon-generation-guard.ts') || contains(needs.affected-plan.outputs.changed_paths, 'scripts/telegram-daemon-generation-manifest.json') }} | |
| CI_DEV_TELEGRAM_WINDOWS_RESULT: ${{ needs.windows-telegram-daemon-safety.result }} | |
| CI_DEV_TELEGRAM_WINDOWS_REQUIRED: ${{ contains(needs.affected-plan.outputs.changed_paths, 'telegram-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon-control.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/coding-agent/src/sdk/broker/process-incarnation.ts') || contains(needs.affected-plan.outputs.changed_paths, 'daemon-control.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'notifications-telegram-daemon.test.ts') || contains(needs.affected-plan.outputs.changed_paths, 'chat-daemon') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-natives/src/path_identity.rs') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-natives/src/ps.rs') || contains(needs.affected-plan.outputs.changed_paths, 'crates/pi-shell/src/process.rs') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/native/index.d.ts') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/native/index.js') || contains(needs.affected-plan.outputs.changed_paths, 'packages/natives/test/path-identity-windows.test.ts') }} | |
| steps: | |
| - name: Fail closed on producer and live dependency results | |
| env: | |
| CI_DEV_EVIDENCE_ROOT: ${{ runner.temp }}/ci-dev-affected-evidence | |
| shell: bash | |
| run: | | |
| test '${{ needs.affected-evidence-producer.result }}' = success | |
| test '${{ needs.affected-plan.result }}' = success | |
| test '${{ needs.affected-evidence-producer.outputs.artifact_id }}' != '' | |
| test '${{ needs.affected-evidence-producer.outputs.artifact_digest }}' != '' | |
| test "$CI_DEV_EVIDENCE_ROOT" != "$GITHUB_WORKSPACE" | |
| case "$CI_DEV_EVIDENCE_ROOT" in "$GITHUB_WORKSPACE"/*) exit 1;; esac | |
| rm -rf "$CI_DEV_EVIDENCE_ROOT" | |
| mkdir -p "$CI_DEV_EVIDENCE_ROOT" | |
| - name: Download finalized affected evidence | |
| # download-artifact selects the immutable upload by artifact ID; the pinned | |
| # action exposes no downloaded digest output to compare, so artifact_digest | |
| # remains a required producer audit binding rather than a path selector. | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| artifact-ids: ${{ needs.affected-evidence-producer.outputs.artifact_id }} | |
| path: ${{ runner.temp }}/ci-dev-affected-evidence | |
| merge-multiple: true | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - name: Verify checked-out source head | |
| shell: bash | |
| run: | | |
| head="$(git rev-parse HEAD)" | |
| test "$head" = "$CI_DEV_SOURCE_SHA" || { echo "Checked-out SHA $head does not match $CI_DEV_SOURCE_SHA"; exit 1; } | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Validate finalized Darwin smoke receipt | |
| if: ${{ needs.affected-plan.outputs.has_darwin_arm64_tab_worker_smoke == 'true' }} | |
| env: | |
| CI_DEV_DARWIN_RECEIPT: ${{ runner.temp }}/ci-dev-affected-evidence/.ci-dev-darwin-arm64-receipt.json | |
| run: bun scripts/ci-validate-darwin-receipt.ts | |
| - name: Validate finalized affected evidence | |
| env: | |
| CI_DEV_EVIDENCE_ROOT: ${{ runner.temp }}/ci-dev-affected-evidence | |
| run: bun scripts/ci-dev-affected.ts --validate-affected-evidence | |
| - name: Validate live affected aggregate | |
| env: | |
| CI_DEV_AFFECTED_PLAN: ${{ runner.temp }}/ci-dev-affected-evidence/.ci-dev-affected-plan.json | |
| run: bun scripts/ci-dev-affected.ts --validate-aggregate | |
| gjc-state-gates-matrix: | |
| name: gjc-state-gates / ${{ matrix.group }} | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.head_sha != '') }} | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 10 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| group: [static, runtime, integrity, read] | |
| env: | |
| GITHUB_EVENT_BEFORE: ${{ github.event.before }} | |
| GITHUB_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'workflow_dispatch' && inputs.base_sha || github.event.before }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Restore bun dependency cache | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-1.3.14-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} | |
| restore-keys: | | |
| bun-1.3.14-${{ runner.os }}- | |
| - run: bun install --frozen-lockfile | |
| - name: Run GJC state gate shard | |
| run: bun scripts/ci-gjc-state-gates.ts --group=${{ matrix.group }} | |
| # Branch protection must keep requiring this stable aggregate status. | |
| gjc-state-gates: | |
| name: gjc-state-gates | |
| if: ${{ always() && !(github.event_name == 'workflow_dispatch' && inputs.head_sha != '') }} | |
| needs: [gjc-state-gates-matrix] | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Aggregate GJC state gate shards | |
| run: | | |
| result='${{ needs.gjc-state-gates-matrix.result }}' | |
| echo "gjc-state-gates shard result: $result" | |
| test "$result" = success | |
| # Stable pre-merge status: this runs for every PR after the protected affected | |
| # aggregate, then only materializes a virtual merge when the canonical planner | |
| # selected risk canaries. A dispatch remains available for exact-head bootstrap | |
| # evidence. Every merge candidate shares one non-cancelling lane: a candidate | |
| # selects and materializes against dev, so overlapping candidates could otherwise | |
| # validate against incompatible integration state. Keep up to 100 candidates | |
| # pending instead of replacing an older pending validation. | |
| virtual-integration: | |
| name: Virtual integration validation | |
| needs: [affected-plan, affected] | |
| if: ${{ always() && ((github.event_name == 'pull_request' && needs.affected.result == 'success') || (github.event_name == 'workflow_dispatch' && inputs.head_sha != '')) }} | |
| concurrency: | |
| group: dev-ci-virtual-integration | |
| cancel-in-progress: false | |
| queue: max | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| actions: read | |
| env: | |
| CI_VI_HEAD_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.head_sha }} | |
| # CI_VI_BASE_SHA is set per-step from the authoritative terminal-green | |
| # dev base selected by --select-base, never from the stale PR event base. | |
| CI_VI_REQUIRED: ${{ github.event_name == 'workflow_dispatch' && 'true' || needs.affected-plan.outputs.has_risk_canaries }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.head_sha }} | |
| - name: Verify checked-out source head | |
| shell: bash | |
| run: | | |
| head="$(git rev-parse HEAD)" | |
| test "$head" = "$CI_VI_HEAD_SHA" || { echo "Checked-out SHA $head does not match $CI_VI_HEAD_SHA"; exit 1; } | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Select authoritative terminal-green dev base | |
| id: green-dev | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| CI_VI_HEAD_SHA: ${{ env.CI_VI_HEAD_SHA }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bun scripts/ci-virtual-integration.ts --select-base | | |
| { | |
| base_sha="" | |
| base_run_id="" | |
| base_conclusion="" | |
| while IFS='=' read -r key value; do | |
| case "$key" in | |
| authority_base_sha) base_sha="$value" ;; | |
| authority_base_run_id) base_run_id="$value" ;; | |
| authority_base_conclusion) base_conclusion="$value" ;; | |
| esac | |
| done | |
| test -n "$base_sha" || { echo "::error::No authoritative terminal-green dev base selected"; exit 1; } | |
| echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT" | |
| echo "base_run_id=$base_run_id" >> "$GITHUB_OUTPUT" | |
| echo "base_conclusion=$base_conclusion" >> "$GITHUB_OUTPUT" | |
| } | |
| - name: Fetch and verify authoritative base | |
| env: | |
| CI_VI_AUTHORITY_BASE_SHA: ${{ steps.green-dev.outputs.base_sha }} | |
| run: | | |
| git fetch --no-tags origin "${CI_VI_AUTHORITY_BASE_SHA}:refs/remotes/origin/ci-virtual-base" | |
| fetched="$(git rev-parse origin/ci-virtual-base)" | |
| test "$fetched" = "$CI_VI_AUTHORITY_BASE_SHA" || { echo "::error::Fetched base $fetched != $CI_VI_AUTHORITY_BASE_SHA"; exit 1; } | |
| - name: Validate virtual integration evidence | |
| if: ${{ env.CI_VI_REQUIRED == 'true' }} | |
| env: | |
| CI_VI_BASE_SHA: ${{ steps.green-dev.outputs.base_sha }} | |
| CI_VI_BASE_SHA_OVERRIDE: ${{ inputs.base_sha_override }} | |
| CI_VI_BASE_RUN_ID: ${{ steps.green-dev.outputs.base_run_id }} | |
| CI_VI_BASE_CONCLUSION: ${{ steps.green-dev.outputs.base_conclusion }} | |
| run: bun scripts/ci-virtual-integration.ts --validate | |
| - name: Provision pinned Rust toolchain for canary native build | |
| if: ${{ env.CI_VI_REQUIRED == 'true' }} | |
| uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| with: | |
| toolchain: nightly-2026-04-29 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| if: ${{ env.CI_VI_REQUIRED == 'true' }} | |
| with: | |
| shared-key: dev-virtual-integration-linux-x64 | |
| - name: Run risk-selected canaries in the materialized merge | |
| if: ${{ env.CI_VI_REQUIRED == 'true' }} | |
| env: | |
| CI_VI_BASE_SHA: ${{ steps.green-dev.outputs.base_sha }} | |
| CI_VI_BASE_RUN_ID: ${{ steps.green-dev.outputs.base_run_id }} | |
| run: bun scripts/ci-virtual-integration.ts --run-canaries | |
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: ${{ env.CI_VI_REQUIRED == 'true' }} | |
| with: | |
| name: dev-virtual-integration-${{ github.run_id }} | |
| path: .ci-virtual-integration.json | |
| include-hidden-files: true | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true |