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
382 changes: 57 additions & 325 deletions AGENTS.md

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions data/repo-awareness-snapshot.json
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
{
"version": "repo-awareness-snapshot-v1",
"captured_revision": {
"sha": "d9a2e0619cd30539b5dc4a433da0d97cc8f618ed",
"committed_at": "2026-08-27T10:05:30+08:00"
"sha": "de2b9b35bb84c96ced4a6eb09e6d6c99bc2de0bc",
"committed_at": "2026-08-27T10:18:22+08:00"
},
"routes": {
"modes": [
Expand DownExpand Up@@ -3813,6 +3813,14 @@
"outcome": "FIXED",
"checks": "merge-main, review-replies, thread-resolve, snapshot-regen"
},
{
"date": "2026-08-27",
"ref": "codex/tooling-hooks-ci-invariants",
"head": "dba86f029eed2c6d8a4c6c7594840c60d5d004a2",
"scope": "PR #2397 babysit: merge main, conflict resolution, review thread fixes",
"outcome": "mergeable; snapshot regenerated; 5 premature inbox done requests removed; dev-drive-cache fails closed; Medication pin /medications",
"checks": "vitest session-start-hook+sidebar-production 18/18; check:dev-drive-cache; check:outstanding-issues-snapshot"
},
{
"date": "2026-08-26",
"ref": "claude/dev-hub-handoff-accuracy (PR #2382)",
Expand DownExpand Up@@ -24839,8 +24847,8 @@
}
],
"counts": {
"records": 2629,
"refs": 1606
"records": 2630,
"refs": 1607
}
}
}
16 changes: 12 additions & 4 deletions docs/agents-guide.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -102,7 +102,15 @@ To prevent dual competing responders from answering the same PR review comment (
1. **Authoritative responder**: The repository GitHub Action (`.github/workflows/codex-autofix-review-comments.yml`) is the primary automated resolver for Codex PR review comments. It includes explicit governance safeguards:
- Trusted-bot login gating (`chatgpt-codex-connector[bot]`).
- Per-PR deduplication marker (`<!-- codex-autoresolve-pr:<number> -->`).
- Three-cycle head-SHA cap per PR lifetime to prevent runaway repair loops.
- Respect for `skip-codex-review` labels.
2. **App-level watcher throttling**: Interactive desktop/client app watchers ("Autofix pull requests") must be disabled or stand down on pull requests where repository workflows run. Do not instruct an interactive agent session to concurrently fix a review comment that is already queued or being addressed by the repository workflow.
3. **Deduplication markers**: Automated fixers must inspect review threads for existing disposition markers (`<!-- codex-thread-disposition:resolved -->`) and active commit history before initiating new edits or pushing duplicate commits.
- Single automatic repair pass per PR lifetime to prevent runaway repair loops.
- Respect for `skip-codex-review` labels and explicit opt-ins via `codex-review`.
- Hard hold: clinical-decision surfaces (`data/**`, `src/data/**`, `src/lib/mha-act-sections.ts`, `src/lib/form-catalog.ts`, `src/lib/form-ranker.ts`, `src/components/forms/**`, `src/lib/rag/**`, and named ranking surfaces) are never automatically repaired.
2. **Bot ownership boundaries and watcher throttling**:
- **Repository Codex auto-fixer**: Owns unattended repair of actionable Codex review comments on open PRs passing risk routing.
- **App-level / Client watchers**: Interactive desktop or client app watchers ("Autofix pull requests") must stand down and not compete on repository pull requests. Do not instruct an interactive agent session to concurrently fix a review comment that is already queued or being addressed by the repository workflow.
- **CodeRabbit**: Advisory only (`commit_status: false`), intermittent/capped, skipped on draft PRs. Never generates fix commits or competes for PR mutation.
- **Interactive human / agent sessions**: When asked to fix comments or running a `Run PR` sweep, always check if an auto-fixer has already replied or pushed fixes (`<!-- codex-thread-disposition:resolved -->`). Never create competing commits on the same review finding.
3. **Review comment lifecycle and disposition markers**:
- For every fixed or fully dispositioned thread, start the thread reply with `<!-- codex-thread-disposition:resolved -->`.
- On the next line, include `<!-- codex-thread-result:fixed-head:<40-character commit SHA> -->` for code fixes or `<!-- codex-thread-result:no-change -->` for no-code dispositions.
- Threads requiring human judgment, architectural decisions, or touching clinical holds must be left open with an explanatory reply instead of using the resolved marker.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
| 2026-08-27 | codex/tooling-hooks-ci-invariants | dba86f029eed2c6d8a4c6c7594840c60d5d004a2 | PR #2397 babysit: merge main, conflict resolution, review thread fixes | mergeable; snapshot regenerated; 5 premature inbox done requests removed; dev-drive-cache fails closed; Medication pin /medications | vitest session-start-hook+sidebar-production 18/18; check:dev-drive-cache; check:outstanding-issues-snapshot |
2 changes: 1 addition & 1 deletion docs/scripts-index.md
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
# Scripts index

Curated map of `scripts/` (275 files) and the `package.json` script surface (279 entries),
Curated map of `scripts/` (276 files) and the `package.json` script surface (280 entries),
grouped by purpose. This is orientation, not an exhaustive per-file listing — the authoritative
command list is `package.json`, and `npm run docs:check-scripts` verifies every `npm run <x>`
referenced in docs resolves to a real script. `npm run docs:update` refreshes the exact counts above.
Expand Down
18 changes: 15 additions & 3 deletions docs/testing.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,21 +14,33 @@ Ordinary Vitest and Playwright runs remove OpenAI, Supabase, database, and E2E c

**Provider-backed boundary:** `test:live`, `eval:quality`, `eval:retrieval:quality`, `verify:release`, `check:supabase-project`, and other OpenAI/Supabase/hosted workflows need **explicit user approval** before agents run them (see root `AGENTS.md`). Prefer offline gates (`verify:cheap`, `verify:pr-local`, `eval:rag:offline`) unless that approval is in the task.

### Windows process-spawn diagnostic
### Windows process-spawn diagnostic (#VV83VA)

Before investigating a slow `git push`, `gh`, or pre-push guard on a Windows workstation, measure an unrelated local process spawn. In PowerShell:
Before investigating a slow `git push`, `gh`, or pre-push guard on a Windows workstation, measure an unrelated local process spawn.

In PowerShell:

```powershell
Measure-Command { node --version }
# or evaluating inline execution:
Measure-Command { node -e "console.log(process.version)" }
```

From `cmd.exe`, invoke the same measurement without relying on shell aliases:

```cmd
powershell -NoProfile -Command "Measure-Command { node --version }"
powershell -NoProfile -Command "Measure-Command { node -e 'console.log(process.version)' }"
```

In Bash / WSL:

```bash
time node --version
time node -e "console.log(process.version)"
```

Subsecond completion is healthy; if this simple command takes multiple seconds, treat it as host process-spawn starvation rather than a repository or GitHub CLI fault. Close stale Codex and terminal sessions, then retry; reboot the workstation if the condition persists. Do not change Windows Defender, add security exclusions, or otherwise alter Windows security settings as part of this diagnosis.
Subsecond completion (<0.2s) is healthy; if this simple command takes multiple seconds (measured up to 17s on process-starved hosts vs 0.08s after reboot), treat it as host process-spawn starvation rather than a repository or GitHub CLI fault. Close stale Codex, Node, and terminal sessions, then retry; reboot the workstation if the condition persists. Do not change Windows Defender, add security exclusions, or otherwise alter Windows security settings as part of this diagnosis.

## Risk-based selection

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -101,6 +101,7 @@
"check:gitleaks-pinned": "node scripts/run-gitleaks-pinned.mjs --self-test",
"check:ci-triage": "node scripts/ci-triage.mjs --self-test",
"check:gate-manifest": "node scripts/check-gate-manifest.mjs",
"check:dev-drive-cache": "node scripts/check-dev-drive-cache.mjs --self-test && node scripts/check-dev-drive-cache.mjs",
"check:branch-review-ledger": "node scripts/check-branch-review-ledger.mjs --self-test && node scripts/branch-review-ledger.mjs --self-test && node scripts/check-branch-review-ledger.mjs",
"check:outstanding-issues": "node scripts/check-outstanding-issues.mjs --self-test && node scripts/outstanding-issues.mjs --self-test && node scripts/ledger-inbox.mjs --self-test && node scripts/ledger-inbox.mjs check && node scripts/check-outstanding-issues.mjs && npm run check:outstanding-issues-snapshot",
"snapshot:issues": "node scripts/generate-outstanding-issues-snapshot.mjs",
Expand Down
154 changes: 154 additions & 0 deletions scripts/check-dev-drive-cache.mjs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
#!/usr/bin/env node
/**
* check-dev-drive-cache.mjs — Verify npm package cache registration in Windows Dev Drive trusted cache (#6SMMB4).
*
* On Windows workstations hosting worktrees on a Dev Drive (e.g. D:, ReFS),
* verifies whether `npm config get cache` resolves to a path on a Dev Drive volume
* and whether that cache directory is trusted by Microsoft Defender.
*/
import { execFileSync, spawnSync } from "node:child_process";
import path from "node:path";
import { pathToFileURL } from "node:url";

export function resolveNpmCache(exec = execFileSync) {
try {
const isWin = process.platform === "win32";
const stdout = isWin
? exec("cmd.exe", ["/c", "npm", "config", "get", "cache"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
})
: exec("npm", ["config", "get", "cache"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
});
return stdout.trim();
} catch {
return process.env.npm_config_cache || "";
}
}

export function inspectDevDriveTrust(cachePath, { platform = process.platform, exec = spawnSync } = {}) {
if (platform !== "win32") {
return {
status: "skipped",
reason: "Dev Drive trust verification is specific to Windows workstations.",
cachePath,
};
}

if (!cachePath) {
return {
status: "warning",
reason: "Could not resolve npm cache directory path.",
cachePath: "",
};
}

const drive = path.parse(path.resolve(cachePath)).root.replace(/[\/\\]$/, "");
const queryResult = exec("fsutil", ["devdrv", "query", drive], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});

const output = `${queryResult.stdout ?? ""}\n${queryResult.stderr ?? ""}`.trim();

if (queryResult.status !== 0) {
if (output.includes("Error 5") || output.includes("Access is denied")) {
return {
status: "info",
reason: `fsutil devdrv query requires elevation (Error 5). From an elevated administrator prompt run: fsutil devdrv trust "${cachePath}"`,
cachePath,
drive,
elevated: false,
};
}
return {
status: "warning",
reason: `Could not query Dev Drive status for volume ${drive}: ${output || "unknown error"}`,
cachePath,
drive,
};
}

const isTrusted = /trusted/i.test(output) && !/not trusted/i.test(output);
return {
status: isTrusted ? "ok" : "untrusted",
reason: isTrusted
? `Dev Drive on ${drive} is trusted.`
: `Dev Drive on ${drive} is not registered as trusted. Run elevated: fsutil devdrv trust "${cachePath}"`,
cachePath,
drive,
elevated: true,
rawOutput: output,
};
}

export function exitCodeForDevDriveResult(result) {
if (result.status === "ok") return 0;
if (result.status === "skipped" || result.status === "info") return 0;
return 1;
}

export function runCheck({ log = console.log, warn = console.warn, inspect = inspectDevDriveTrust } = {}) {
const cachePath = resolveNpmCache();
const result = inspect(cachePath);

if (result.status === "ok") {
log(`[devdrv-cache] OK: npm cache (${result.cachePath}) on ${result.drive} is registered in trusted Dev Drive.`);
return 0;
}
if (result.status === "skipped" || result.status === "info") {
log(`[devdrv-cache] ${result.reason}`);
return 0;
}
warn(`[devdrv-cache] ${result.status === "untrusted" ? "FAIL" : "WARN"}: ${result.reason}`);
return exitCodeForDevDriveResult(result);
}

function selfTest() {
const linuxResult = inspectDevDriveTrust("/home/user/.npm", { platform: "linux" });
if (linuxResult.status !== "skipped") throw new Error("Linux platform should be skipped");

const deniedExec = () => ({ status: 1, stdout: "", stderr: "Failed to open the volume. Error 5: Access is denied." });
const deniedResult = inspectDevDriveTrust("D:\\.npm-cache", { platform: "win32", exec: deniedExec });
if (deniedResult.status !== "info" || deniedResult.elevated !== false) {
throw new Error("Access denied should return info status with non-elevated flag");
}

const trustedExec = () => ({
status: 0,
stdout: "This is a Developer Volume (Dev Drive). Volume is trusted.",
stderr: "",
});
const trustedResult = inspectDevDriveTrust("D:\\.npm-cache", { platform: "win32", exec: trustedExec });
if (trustedResult.status !== "ok" || trustedResult.elevated !== true) {
throw new Error("Trusted output should return ok status");
}

const untrustedExec = () => ({
status: 0,
stdout: "This is a Developer Volume (Dev Drive). Volume is not trusted.",
stderr: "",
});
const untrustedResult = inspectDevDriveTrust("D:\\.npm-cache", { platform: "win32", exec: untrustedExec });
if (untrustedResult.status !== "untrusted") {
throw new Error("Untrusted output should return untrusted status");
}
if (exitCodeForDevDriveResult(untrustedResult) !== 1) {
throw new Error("Untrusted result should map to exit code 1");
}
if (exitCodeForDevDriveResult({ status: "warning", reason: "missing cache" }) !== 1) {
throw new Error("Warning result should map to exit code 1");
}

console.log("[devdrv-cache] self-test passed.");
return 0;
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
if (process.argv.includes("--self-test")) {
process.exit(selfTest());
}
process.exit(runCheck());
}
4 changes: 2 additions & 2 deletions tests/guard-push.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,7 +125,7 @@ describe("auto-merge verdict", () => {
});
});

describe("force-push detection", () => {
describe("force-push detection", { timeout: 60_000 }, () => {
it("does not flag a fast-forward push", () => {
const { root, git } = gitFixture();
writeFileSync(join(root, "one.md"), "one\n");
Expand DownExpand Up@@ -195,7 +195,7 @@ describe("drift verdict", () => {
});
});

describe("push-range parsing", () => {
describe("push-range parsing", { timeout: 60_000 }, () => {
it("parses a new-branch push (zero remote sha)", () => {
const ranges = parsePushRanges(`refs/heads/x abc123 refs/heads/x ${ZERO}\n`);
expect(ranges).toHaveLength(1);
Expand Down
2 changes: 1 addition & 1 deletion tests/hosted-migration-role-guard.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -71,7 +71,7 @@ function syntheticRepository({
};
}

describe("hosted migration-role guard", () => {
describe("hosted migration-role guard", { timeout: 60_000 }, () => {
it("accepts the current repository state", () => {
const result = inspectMigrationRoleRepository();

Expand Down
14 changes: 14 additions & 0 deletions tests/session-start-hook.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,20 @@ describe("precompact observability hook", () => {
);
}
});

it("survives malformed payloads and missing git directory without error", () => {
const emptyDir = mkdtempSync(join(tmpdir(), "precompact-empty-"));
scratchRoots.push(emptyDir);

const result = spawnSync(bashCommand, [sourcePrecompactHook.replace(/\\/g, "/")], {
cwd: emptyDir,
encoding: "utf8",
input: "not json at all { malformed [",
});
expect(result.status, `hook exited ${result.status}: ${result.stderr}`).toBe(0);
expect(result.stdout).toBe("");
expect(result.stderr).toBe("");
});
});

/**
Expand Down
Loading