ci: add one-shot memory final review repair #1
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: Repair memory final review blockers | ||
| on: | ||
| pull_request_target: | ||
| types: [opened, synchronize, reopened] | ||
| permissions: | ||
| contents: write | ||
| issues: write | ||
| jobs: | ||
| repair: | ||
| if: github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'feat/memory-guard-observability-dev' | ||
| runs-on: ubuntu-22.04 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event.pull_request.head.ref }} | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Check one-shot trigger | ||
| id: gate | ||
| shell: bash | ||
| run: | | ||
| if [ -f .github/trigger/memory-final-review ]; then | ||
| echo "run=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "run=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - uses: oven-sh/setup-bun@v2 | ||
| if: steps.gate.outputs.run == 'true' | ||
| with: | ||
| bun-version: '1.3' | ||
| - name: Add direct Windows accounting regressions | ||
| if: steps.gate.outputs.run == 'true' | ||
| shell: bash | ||
| run: | | ||
| python3 - <<'PY' | ||
| from pathlib import Path | ||
| path = Path('packages/coding-agent/test/tools/resource-gc.test.ts') | ||
| text = path.read_text() | ||
| memory_limit_import = 'import { resolveEffectiveMemoryLimit } from "../../src/runtime/memory-limit";\n' | ||
| settings_import = 'import { resetSettingsForTest, Settings } from "../../src/config/settings";\n' | ||
| if memory_limit_import not in text: | ||
| if settings_import not in text: | ||
| raise SystemExit('settings import anchor missing') | ||
| text = text.replace(settings_import, settings_import + memory_limit_import, 1) | ||
| seam = '\t__sampleWindowsJobMemoryForTest,\n' | ||
| import_anchor = '\t__sampleLinuxCgroupHierarchyForTest,\n' | ||
| if seam not in text: | ||
| if import_anchor not in text: | ||
| raise SystemExit('resource-gc import anchor missing') | ||
| text = text.replace(import_anchor, import_anchor + seam, 1) | ||
| marker = '});\ndescribe("resource GC controller", () => {' | ||
| regression = r''' }); | ||
| describe("Windows Job memory sampling", () => { | ||
| const GIB = 1024 ** 3; | ||
| const HOST_BYTES = 16 * GIB; | ||
| const PARENT_BYTES = 2 * GIB; | ||
| function uncappedCommitSnapshot() { | ||
| return __sampleWindowsJobMemoryForTest(HOST_BYTES, PARENT_BYTES, { | ||
| kind: "job_snapshot", | ||
| platform: "win32", | ||
| isInJob: true, | ||
| jobMemoryUsedBytes: String(20 * GIB), | ||
| peakJobMemoryUsedBytes: String(21 * GIB), | ||
| processPrivateUsageBytes: String(20 * GIB), | ||
| processWorkingSetBytes: String(PARENT_BYTES), | ||
| peakProcessWorkingSetBytes: String(3 * GIB), | ||
| }); | ||
| } | ||
| it("keeps uncapped Job commit charge separate from the physical-RAM domain", () => { | ||
| const snapshot = uncappedCommitSnapshot(); | ||
| expect(snapshot).not.toBeNull(); | ||
| expect(snapshot?.parentBytes).toBe(PARENT_BYTES); | ||
| const jobCommit = snapshot?.domains?.find(domain => domain.source === "windows_job"); | ||
| expect(jobCommit).toEqual({ | ||
| hardCapBytes: Number.MAX_SAFE_INTEGER, | ||
| totalUsageBytes: 20 * GIB, | ||
| source: "windows_job", | ||
| }); | ||
| const rss = snapshot?.domains?.find( | ||
| domain => domain.source === "windows_process_job_limit" && domain.hardCapBytes === HOST_BYTES, | ||
| ); | ||
| expect(rss).toEqual({ | ||
| hardCapBytes: HOST_BYTES, | ||
| totalUsageBytes: PARENT_BYTES, | ||
| source: "windows_process_job_limit", | ||
| }); | ||
| }); | ||
| it("does not clamp a commit-domain policy cap to physical RAM when commit exceeds RAM", () => { | ||
| const snapshot = uncappedCommitSnapshot(); | ||
| expect(snapshot).not.toBeNull(); | ||
| const policyLimitBytes = 24 * GIB; | ||
| const pressure = __selectMemoryPressureDomainForTest(snapshot!, policyLimitBytes); | ||
| const limit = resolveEffectiveMemoryLimit({ | ||
| hardCapBytes: pressure.hardCapBytes, | ||
| policyLimitBytes, | ||
| }); | ||
| expect(pressure.source).toBe("windows_job"); | ||
| expect(pressure.totalUsageBytes).toBe(20 * GIB); | ||
| expect(limit.effectiveBytes).toBe(policyLimitBytes); | ||
| expect(limit.effectiveBytes).toBeGreaterThan(HOST_BYTES); | ||
| const usageRatio = pressure.totalUsageBytes / limit.effectiveBytes!; | ||
| expect(usageRatio).toBeCloseTo(20 / 24, 8); | ||
| expect(usageRatio).toBeLessThan(1); | ||
| }); | ||
| }); | ||
| describe("resource GC controller", () => {''' | ||
| if 'describe("Windows Job memory sampling"' not in text: | ||
| if marker not in text: | ||
| raise SystemExit('Windows test insertion anchor missing') | ||
| text = text.replace(marker, regression, 1) | ||
| path.write_text(text) | ||
| PY | ||
| - name: Restore generated native declarations | ||
| if: steps.gate.outputs.run == 'true' | ||
| shell: bash | ||
| run: | | ||
| bun packages/natives/scripts/gen-enums.ts | ||
| cp packages/natives/native/index.d.ts /tmp/index.generated.d.ts | ||
| cp packages/natives/native/index.js /tmp/index.generated.js | ||
| bun packages/natives/scripts/gen-enums.ts | ||
| cmp /tmp/index.generated.d.ts packages/natives/native/index.d.ts | ||
| cmp /tmp/index.generated.js packages/natives/native/index.js | ||
| grep -F "macOS computer-use controller." packages/natives/native/index.d.ts | ||
| - name: Format changed test | ||
| if: steps.gate.outputs.run == 'true' | ||
| run: bunx biome check --write packages/coding-agent/test/tools/resource-gc.test.ts | ||
| - name: Install dependencies | ||
| if: steps.gate.outputs.run == 'true' | ||
| run: bun install --frozen-lockfile | ||
| - name: Run memory regression suites | ||
| if: steps.gate.outputs.run == 'true' | ||
| run: | | ||
| bun test --timeout 30000 \ | ||
| packages/coding-agent/test/runtime/memory-limit.test.ts \ | ||
| packages/coding-agent/test/runtime/memory-domain.test.ts \ | ||
| packages/coding-agent/test/runtime/memory-guard.test.ts \ | ||
| packages/coding-agent/test/tools/resource-gc.test.ts \ | ||
| packages/coding-agent/test/tools/resource-gc-redteam.test.ts \ | ||
| packages/coding-agent/test/gjc-runtime/linux-proc.test.ts \ | ||
| packages/coding-agent/test/cli-memory-guard-native-smoke.test.ts \ | ||
| packages/natives/test/memory-guard-native.test.ts \ | ||
| packages/natives/test/memory-guard-build-wiring.test.ts | ||
| - name: Run package checks | ||
| if: steps.gate.outputs.run == 'true' | ||
| run: | | ||
| bun --cwd=packages/coding-agent run check | ||
| bun --cwd=packages/natives run check | ||
| - name: Commit verified final fixes | ||
| if: steps.gate.outputs.run == 'true' | ||
| shell: bash | ||
| run: | | ||
| git config user.name "twoimo" | ||
| git config user.email "32544727+twoimo@users.noreply.github.com" | ||
| git rm .github/trigger/memory-final-review | ||
| git add \ | ||
| packages/coding-agent/test/tools/resource-gc.test.ts \ | ||
| packages/natives/native/index.d.ts \ | ||
| packages/natives/native/index.js | ||
| git commit -m "test(coding-agent): cover Windows memory accounting" | ||
| git push origin HEAD:${{ github.event.pull_request.head.ref }} | ||
| - name: Report success | ||
| if: steps.gate.outputs.run == 'true' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: 'MEMORY_FINAL_REVIEW_OK', | ||
| }); | ||