Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions devlog/_plan/260907_platform_validation/000_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Platform verification follow-up

Baseline: dev `137d6a7270e7ecfb1c791993800a17c0e30022d9` (2026-09-07).

## Objective and authority

Satisfy the existing platform contracts for #3383, #3449, #3522 and #3573. The owner requested ordinary manual PRs, top-of-stack CI first, lower-layer CI only to diagnose a failed final run, no local test suites, push with --no-verify, admin merge after verification, and original contributor credit in commit trailers. No native GitHub stack registration. No publish, release, global settings changes, admission-limit increases, ACL relaxation, or speculative recovery policy.

The initial assigned checkout contains unrelated dirty work and is preserved. Work lives in an isolated worktree. No SessionStart FSM binding is available in the supplied context; this record documents the work without claiming automatic loop continuation is armed.

## Evidence and scope

Dockerfile, compose.yaml, docker/bootstrap-token.ts and the source-build guide already exist. Cross-platform CI has no real image build/start/recreate check. #3522 requires same-process Windows recovery evidence; #3573 requires actual rejected compact-byte evidence. Existing diagnostics must be checked before adding anything. PR #3383 is a mixed historical source: only Windows temp/teardown residuals are in scope, not picker controls.

Original Docker contributor: Buseong Kim <flight@skyline23.com>, verified from original #3421 commit metadata. Carry this identity in commit trailers.

## Dependency map

1. `010_oauth_teardown.md`: drain the asynchronous ACL fixture before deletion.
2. `020_container_smoke.md`: executable isolated container acceptance probe.
3. `030_container_ci.md`: CI consumes that probe and gates its result.
4. `035_body_diagnostics.md`: distinguish declared size, observed lower bound, and decoded size without changing admission.
5. `040_residual_evidence.md`: settle the Windows/spill/compact residuals; implement only a proven narrow gap through a plan amendment, otherwise preserve open status.

The manual review chain contains the independent OAuth fixture carry, bounded body diagnostics, the container probe, then its dependent CI integration. Independent code is prepared in disjoint files; the top CI validates their combined tree. Existing workflow triggers remain honest: final branch workflow_dispatch supplies the complete integration result; lower PR runs are not represented as passed if skipped/cancelled. Every implemented layer is reviewed, and final head is pinned before CI. After successful final CI, merge bottom-up using merge commits so reviewed commit ancestry survives. Revalidate the resulting integration and distinguish unrelated concurrent dev changes.

## Verification and completion

Local suites and typecheck are NOT RUN by owner instruction. Syntax and read-only diff checks are allowed. The real verifier is GitHub Cross-platform CI on the final branch, including the new Docker job. A failed final run is diagnosed on the smallest affected scope; do not repeatedly run passing gates. Independent Astra high review covers functionality and workflow/security boundaries. Security working notes remain in scratch, not this public unit.

Completion means verified deliverable PRs merged with commit attribution, plus explicit no-op/blocked disposition for unavailable field evidence. It does not mean every original issue is fixed. New product/security policy choices remain outside scope. Evidence and final outcome are appended to this unit; workflow run URLs and SHAs are preserved.
5 changes: 5 additions & 0 deletions devlog/_plan/260907_platform_validation/001_plan_audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Plan audit disposition

Independent Astra high reviewer: NEAR-PASS. OAuth teardown and bounded body diagnostics passed within scope. Three Docker/CI conditions were incorporated before implementation: explicit final lane=all executed-job inventory; isolated project/image/port and bounded cleanup; concrete readiness/admission/catalog/persistence checks before and after actual replacement.

Main judgment: pass with those amendments. Scope remains unchanged: existing Docker contract verification, test-fixture teardown, bounded diagnostics. Live spill recovery and exact historical compact-body proof remain deferred. No local suites or typecheck were run.
14 changes: 14 additions & 0 deletions devlog/_plan/260907_platform_validation/010_oauth_teardown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# OAuth fixture teardown carry

Original source: #3383 commit 51726d2c7c58146defdd6088aefa2b95a1e58553.
Original contributor: x3M3x <amroeid1999@gmail.com> (Git commit metadata).

## Concrete delta

MODIFY `tests/oauth/oauth-store-multi.test.ts` only: import flushConfigDirHardeningForTests and the async ICACLS test runner; stub synchronous and asynchronous runners consistently in setup. Change teardown to await the tracked hardening work before resetting runners/caches, restoring OPENCODEX_HOME, or removing the fixture. Preserve removeTreeWithRetry and all production semantics. Add a deterministic held-async-runner regression against the actual cleanup routine if the existing fixture seams allow it without a new production test API.

Production path proof: store reads call hardenConfigDir; config/paths tracks asynchronous directory hardening; resetHardenedStateForTests clears caches but does not drain those jobs. Deletion retries alone do not ensure ordering. The prior carry #3258 only replaced the removal function.

## Acceptance

No real asynchronous ICACLS escapes the fixture runner. Cleanup waits while a controlled ACL flight is unresolved and only deletes/restores environment after completion. The same OAuth test file passes in final Linux/macOS/Windows CI. Local tests/typecheck are NOT RUN by owner instruction. No numeric-open-flags change is included without current Bun reproduction. No new API/auth policy, credentials, or production runtime change.
25 changes: 25 additions & 0 deletions devlog/_plan/260907_platform_validation/020_container_smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Container smoke executable

## File delta

NEW `scripts/ci/docker-smoke.ts`: bounded Bun-native TypeScript probe for the existing source-build Compose contract. Reuse the canonical compatibility generator and docker/bootstrap-token.ts; do not add an alternative token writer or deployment configuration. The probe creates a unique temporary Compose project and image, builds the actual Dockerfile, bootstraps a freshly generated throwaway token through stdin, starts the hub, verifies health and data-plane admission, recreates the container on the same named volumes, and verifies persistent state again. Cleanup is limited to the unique test project and its generated artifacts. Never use an operator project, host home, provider credentials, global docker prune, or real upstream inference.

MODIFY owning documentation only as needed to explain the CI acceptance scope and its limits; no claim of upstream-provider validation.

## Acceptance

- Real image builds from the checkout with a generated compatibility manifest.
- Read-only/non-root Compose service becomes healthy; requests without a token are refused.
- A synthetic catalog in the separate Codex volume is served with the throwaway token, proving admission and persistence without provider access.
- /readyz succeeds separately from liveness, token reinitialization fails without replacement, and effective container restrictions are verified.
- Token/config/catalog persist across an actual container replacement (different container id, same volumes).
- Failures and cleanup are bounded; token/body contents never appear in logs.
- Existing Docker settings and defaults remain unchanged.

Run only in final remote CI. Locally perform source/static inspection, not the smoke or a test suite. Read the current lifecycle/API contracts before implementing assertions.

## Audit amendments

Use explicit unique project on every Compose command, unique image tag via a temporary override, controlled Compose environment, and loopback ephemeral host port. Preserve pre-existing generated files; cleanup must fail the probe if it cannot remove its own project resources. Bound every child, output capture and cleanup; terminate/reap timed-out children. Never print raw runtime logs or complete inspect output.

Before/after replacement: require readyz 200 with status ready; authenticated catalog 200 with exact synthetic fixture; missing/wrong token 401 for catalog, Responses and compact. Second bootstrap must fail and preserve the original token while rejecting the proposed replacement. Verify different container IDs, identical named-volume identities and persistent config/catalog evidence without reseeding; check effective non-root UID and read-only root.
21 changes: 21 additions & 0 deletions devlog/_plan/260907_platform_validation/030_container_ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Container CI integration

Depends on the committed probe from phase 1.

## File delta

MODIFY `.github/workflows/ci.yml`: include Dockerfile, compose.yaml, .dockerignore and docker/** in relevant scope detection; add an ubuntu-latest Docker smoke job using the existing pinned checkout and setup-project-bun action; invoke the script after installing required project dependencies if the generator needs them. Preserve read-only workflow permissions and persist-credentials false. Add the job to aggregate ci needs so failures cannot silently pass. No registry publishing, credentials, native stack integration or changes to existing suite retry/concurrency policy.

MODIFY `tests/ci-workflows/ci-workflows.test.ts`: extend the existing source-oracle checks for scope paths, direct aggregate dependency, pinned actions, and actual probe invocation. Keep existing domain/layout registration unchanged by using the owning test file.

MODIFY `docs-site/src/content/docs/guides/remote-hub.md`: describe image lifecycle validation and separate readiness/provider-auth limitations.

## Acceptance and verifier

Final-branch Cross-platform CI workflow_dispatch must run the smoke and the existing platform gates. The Docker job's failures must reach ci. Local suite/typecheck NOT RUN per owner. Independent review checks full workflow event, permission, input, credential, and cleanup boundaries before publishing. Existing source-oracle tests execute remotely in CI.

Publish branches with --no-verify; do not claim lower-layer CI if only the final tree was tested. Final failure permits narrower runs. User authorized admin merge of verified layers; original author names/emails come from source commit metadata and are included as Co-authored-by trailers.

## Final execution inventory

Dispatch existing Cross-platform CI with lane=all on the immutable final head. Record each expected job and actual conclusion: Docker, four Linux shards, storage-policy, api-usage, gates, two macOS shards, macos-control, six Windows shards, keyring jobs, any selected npm packaging jobs, and ci. Aggregate green alone does not prove Windows or Docker ran. Explain legitimate scope skips instead of counting them as tests.
17 changes: 17 additions & 0 deletions devlog/_plan/260907_platform_validation/035_body_diagnostics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Bounded inbound-body diagnostic semantics

Issue #3573 requests usable size evidence. The existing error stores a byte value but returns only the admission limit; the byte value currently mixes declared length, observed wire bytes, an artificial limit+1 lower bound, and exact decoded length.

## File delta

MODIFY `src/server/request-decompress.ts`: extend DecompressedBodyTooLargeError with a closed measurement category and retained limit, preserving existing constructor call compatibility. Annotate existing throw sites: declared_wire, observed_wire_lower_bound, decoded_exact, decoded_lower_bound. Append a bounded numeric/category suffix to the current message so existing core.ts error mapping carries it. No request body, path, headers, item counts, further inflate/read, admission-limit changes, or new retry semantics.

MODIFY `tests/usage/request-decompress.test.ts`: extend small-cap fixtures to verify identity/gzip/zstd/deflate and declared/fragmented input semantics. In particular, limit+1 remains a lower bound, never exact size. Verify HTTP 413 and existing error code/type through existing handler mapping. Preserve stream cancellation.

MODIFY `docs-site/src/content/docs/reference/proxy-formats.md`: explain wire declared length vs measured/lower-bound diagnostics, separately from compact-response limits. State that Bun listener rejection may happen before application diagnostics and that this does not measure the exact historical compact payload.

## Acceptance

Unchanged 256 MiB listener/decoder limit and rejection classification. No context-window wording that causes errors.ts to reclassify the failure. Message remains bounded, only fixed categories and finite numeric values. Negative tests run in final remote CI; no local test/typecheck. Keep #3573 open pending exact real compact evidence.

This is a new diagnostic refinement of an issue, not a carry of a new contributor PR. Credit reporter @nowhere1975 in commit prose without inventing name/email. Any borrowed existing PR patches must additionally retain their actual git author trailers.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Windows and request diagnostic residuals

## Read-only targets

- #3383: inspect current PR and merged descendants for Windows temp creation and OAuth teardown. Confirm current source behavior and test coverage before proposing a residual patch. No picker UI changes.
- #3522: inspect response spill telemetry and fresh-versus-memoized timeout handling. The acceptance is recovery within the same affected Windows process; generic synthetic success does not prove the reported process recovered.
- #3573: inspect decompression rejection diagnostics and exact latest issue measurements. Serialized journal size and normal requests after raising a cap do not prove the rejected compact payload size or compact success.

## Conditional delta

No production edit is pre-approved by this document without a source-grounded residual. If the existing code covers the measurement, record the missing field evidence and leave the issue open. If a specific content-free diagnostic is missing, amend with exact files, field flow and negative assertions before implementation. Never change admission caps, parse a rejected body to count items, relax ACLs, clear memo state, or choose a new recovery/retry policy.

## Completion

Record source/commit evidence, original contributor attribution where code is carried, and a separate status per candidate: already implemented, proven patch delivered, or blocked on field evidence. Do not close an original feature PR or issue merely because one residual probe passes.
86 changes: 73 additions & 13 deletions tests/oauth/oauth-store-multi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import * as atomicWrite from "../../src/config/atomic-write";
import * as oauthStore from "../../src/oauth/store";
import { flushConfigDirHardeningForTests } from "../../src/config/paths";
import {
resetHardenedStateForTests,
setAsyncIcaclsRunnerForTests,
setIcaclsRunnerForTests,
setPlatformForTests,
} from "../../src/lib/windows-secret-acl";
import { setSyntheticWindowsPrincipalForTests } from "../../src/lib/windows-user-principal";
import {
getAccountCredential,
getAccountSet,
Expand Down Expand Up @@ -35,6 +39,17 @@ import { removeTreeWithRetry } from "../helpers/remove-tree";

const TEST_DIR = join(import.meta.dir, ".tmp-oauth-store-multi-test");
let previousOpencodexHome: string | undefined;
const ICACLS_OK = { success: true, exitCode: 0, timedOut: false, stdout: "" };

async function cleanupOAuthStoreFixture(): Promise<void> {
await flushConfigDirHardeningForTests();
setIcaclsRunnerForTests(null);
setAsyncIcaclsRunnerForTests(null);
resetHardenedStateForTests();
if (previousOpencodexHome === undefined) delete process.env.OPENCODEX_HOME;
else process.env.OPENCODEX_HOME = previousOpencodexHome;
if (existsSync(TEST_DIR)) removeTreeWithRetry(TEST_DIR);
}

const cred = (over: Partial<OAuthCredentials> = {}): OAuthCredentials => ({
access: "access-1",
Expand All @@ -61,21 +76,66 @@ describe("multi-account auth store", () => {
mkdirSync(TEST_DIR, { recursive: true });
process.env.OPENCODEX_HOME = TEST_DIR;
resetHardenedStateForTests();
setIcaclsRunnerForTests(() => ({
success: true,
exitCode: 0,
timedOut: false,
stdout: "",
}));
setIcaclsRunnerForTests(() => ICACLS_OK);
setAsyncIcaclsRunnerForTests(async () => ICACLS_OK);
});

afterEach(() => {
setIcaclsRunnerForTests(null);
resetHardenedStateForTests();
if (previousOpencodexHome === undefined) delete process.env.OPENCODEX_HOME;
else process.env.OPENCODEX_HOME = previousOpencodexHome;
if (existsSync(TEST_DIR)) removeTreeWithRetry(TEST_DIR);
});
afterEach(cleanupOAuthStoreFixture);

test("fixture cleanup waits for a held config-directory ACL flight before restoring home or deleting files", async () => {
let release!: () => void;
const held = new Promise<void>(resolve => { release = resolve; });
let markStarted!: () => void;
const started = new Promise<void>(resolve => { markStarted = resolve; });
let deadlineTimer: ReturnType<typeof setTimeout> | undefined;
let cleaning: Promise<unknown> | undefined;
let cleanupSettled = false;
setPlatformForTests("win32");
// Keep SID discovery hermetic on Windows as well as on forced POSIX lanes.
setSyntheticWindowsPrincipalForTests("*S-1-5-21-1-2-3-1001");
setAsyncIcaclsRunnerForTests(async () => {
markStarted();
await held;
return ICACLS_OK;
});
try {
// A real store read starts the production-tracked directory hardening flight.
expect(getAccountSet("xai")).toBeNull();
await Promise.race([
started,
new Promise<never>((_, reject) => {
deadlineTimer = setTimeout(() => reject(new Error("ACL runner did not start")), INTERNAL_DEADLINE_MS);
}),
]);
clearTimeout(deadlineTimer);
cleaning = cleanupOAuthStoreFixture().then(
() => { cleanupSettled = true; return null; },
(error: unknown) => { cleanupSettled = true; return error; },
);
// An event-loop checkpoint lets an incorrectly unawaited cleanup finish; no sleep oracle.
await new Promise<void>(resolve => setImmediate(resolve));
expect(cleanupSettled).toBe(false);
expect(process.env.OPENCODEX_HOME).toBe(TEST_DIR);
expect(existsSync(TEST_DIR)).toBe(true);

release();
expect(await cleaning).toBeNull();
expect(cleanupSettled).toBe(true);
expect(process.env.OPENCODEX_HOME).toBe(previousOpencodexHome);
expect(existsSync(TEST_DIR)).toBe(false);
} finally {
if (deadlineTimer !== undefined) clearTimeout(deadlineTimer);
// Even a broken cleanup must not release the held flight into the real runner.
setAsyncIcaclsRunnerForTests(async () => ICACLS_OK);
release();
try {
await cleaning;
await flushConfigDirHardeningForTests();
} finally {
setPlatformForTests(null);
}
}
}, STORE_BUDGET_MS);

test("legacy single-credential auth.json normalizes and round-trips without losing login", async () => {
const authPath = join(TEST_DIR, "auth.json");
Expand Down
Loading