diff --git a/context/map/artifact-1-territory.md b/context/map/artifact-1-territory.md index 26be2f0..67fc262 100644 --- a/context/map/artifact-1-territory.md +++ b/context/map/artifact-1-territory.md @@ -2,7 +2,7 @@ -> Scan of `c823d6d` (2026-07-28) · window: last 12 months · engine: tools/repo-map +> Scan of `f67d3cc` (2026-07-28) · window: last 12 months · engine: tools/repo-map **Where the work actually happens.** Git-history activity over the last 12 months, noise filtered (lockfiles, generated files, assets). Activity is a signal of *risk of accidental regression*, not of quality. diff --git a/context/map/artifact-2-structure.md b/context/map/artifact-2-structure.md index 6c008ff..9c0f143 100644 --- a/context/map/artifact-2-structure.md +++ b/context/map/artifact-2-structure.md @@ -2,7 +2,7 @@ -> Scan of `c823d6d` (2026-07-28) · window: last 12 months · engine: tools/repo-map +> Scan of `f67d3cc` (2026-07-28) · window: last 12 months · engine: tools/repo-map **How it is built.** Static import graph of `src` plus a file inventory of every module. Folders show where the code is; the graph shows what actually depends on what. diff --git a/context/map/artifact-3-contributors.md b/context/map/artifact-3-contributors.md index 7cecdfc..70968ab 100644 --- a/context/map/artifact-3-contributors.md +++ b/context/map/artifact-3-contributors.md @@ -2,7 +2,7 @@ -> Scan of `c823d6d` (2026-07-28) · window: last 12 months · engine: tools/repo-map +> Scan of `f67d3cc` (2026-07-28) · window: last 12 months · engine: tools/repo-map **Who to ask before you change something.** Bots and AI agents (Claude, Codex, Copilot, dependabot…) are filtered out — this is about humans who hold undocumented context. Answers "who has context on this area", not "who changed this line" (that is `git blame`). @@ -12,7 +12,7 @@ | --- | --- | | “mkczarkowski” | 77 | | psmyrdek | 8 | -| Marcin Czarkowski | 4 | +| Marcin Czarkowski | 5 | | Przemek Smyrdek | 2 | ## Who to ask, per active area @@ -20,8 +20,8 @@ | Area | Ask (commits) | | --- | --- | | `src/lib` | “mkczarkowski” (29), psmyrdek (4) | -| `context/map` | Marcin Czarkowski (1) | +| `context/map` | Marcin Czarkowski (2) | | `context/changes` | “mkczarkowski” (28) | | `tests/e2e` | “mkczarkowski” (8), psmyrdek (3) | | `src/commands` | “mkczarkowski” (19), psmyrdek (4) | -| `package.json` | “mkczarkowski” (7), Przemek Smyrdek (1) | +| `package.json` | “mkczarkowski” (7), Marcin Czarkowski (1) | diff --git a/context/map/repo-map.md b/context/map/repo-map.md index a27d2bb..d1baf41 100644 --- a/context/map/repo-map.md +++ b/context/map/repo-map.md @@ -2,7 +2,7 @@ -> Scan of `c823d6d` (2026-07-28) · window: last 12 months · engine: tools/repo-map +> Scan of `f67d3cc` (2026-07-28) · window: last 12 months · engine: tools/repo-map > > Operational map of the territory, built from cheap deterministic signals (git history + import graph), synthesized for decisions — not an essay. A new developer should know in ~15 min where things live, what is dangerous, and where to start. Refresh with `npm run repo-map`. @@ -61,11 +61,11 @@ _No high-risk zones flagged this run._ | Area | Ask | | --- | --- | | `src/lib` | “mkczarkowski” (29), psmyrdek (4) | -| `context/map` | Marcin Czarkowski (1) | +| `context/map` | Marcin Czarkowski (2) | | `context/changes` | “mkczarkowski” (28) | | `tests/e2e` | “mkczarkowski” (8), psmyrdek (3) | | `src/commands` | “mkczarkowski” (19), psmyrdek (4) | -| `package.json` | “mkczarkowski” (7), Przemek Smyrdek (1) | +| `package.json` | “mkczarkowski” (7), Marcin Czarkowski (1) | ## 6. First day — read these first diff --git a/context/night-loop.md b/context/night-loop.md index 9723a58..58ab795 100644 --- a/context/night-loop.md +++ b/context/night-loop.md @@ -18,15 +18,17 @@ outer gate. after the turn; no repair or later iteration starts after the ceiling. - One base commit is pinned for the entire run. If the remote base advances, the loop stops instead of comparing against a stale baseline. -- Exact differential gate: lint cannot regress; passing tests cannot decrease; - the pinned set of 8 build-dependent failures must remain byte-for-byte the - same. Characterization work must increase the passing-test count. +- Exact differential gate: the root unit-test suite must stay green, lint + cannot regress, and the passing-test count cannot decrease. Build-dependent + smoke and E2E suites remain separate explicit gates outside this lint-only + loop. - At most two repair turns in the same Codex session. Persistent failure opens an `automation` issue and preserves the failed worktree for diagnosis. - Successful iterations open one small PR each. The runner never merges PRs. -- Agent and gate environments omit credential variables. Codex and outer gates - run without network; binary diffs and patches matching common secret formats - are rejected. +- Agent and gate environments omit credential variables. Codex has no network + access; outer gates deny external network while allowing loopback-only test + fixtures. Binary diffs and patches matching common secret formats are + rejected. The machine must remain awake and online. Saved Codex authentication and `gh` authentication must already work. diff --git a/scripts/night-loop.mjs b/scripts/night-loop.mjs index b239d97..51542a4 100644 --- a/scripts/night-loop.mjs +++ b/scripts/night-loop.mjs @@ -9,6 +9,7 @@ import { mkdirSync, openSync, readFileSync, + readdirSync, renameSync, rmSync, statSync, @@ -38,7 +39,14 @@ const MAX_PATCH_BYTES = 1024 * 1024; const DEFAULT_COMMAND_TIMEOUT_MS = 20 * 60 * 1000; const DEFAULT_BASE = "claude/context-loops-bootstrap"; const VALID_REF = /^[A-Za-z0-9._/-]+$/; -const NO_NETWORK_PROFILE = "(version 1)(allow default)(deny network*)"; +const NO_EXTERNAL_NETWORK_PROFILE = [ + "(version 1)", + "(allow default)", + "(deny network*)", + '(allow network-bind (local ip "localhost:*"))', + '(allow network-inbound (local ip "localhost:*"))', + '(allow network-outbound (remote ip "localhost:*"))', +].join(""); const activeChildren = new Set(); let lockPath; let fatalContext; @@ -62,6 +70,15 @@ function sanitizedEnvironment() { ); } +function unitTestArgs(worktree) { + const tests = readdirSync(join(worktree, "tests"), { withFileTypes: true }) + .filter((entry) => entry.isFile() && entry.name.endsWith(".test.ts")) + .map((entry) => join("tests", entry.name)) + .sort(); + if (tests.length === 0) throw new Error("No root unit tests found"); + return ["test", ...tests]; +} + function parseArgs(argv) { const options = { deadline: "07:30", @@ -466,7 +483,7 @@ async function captureBaseline({ repoRoot, base, deadlineMs, runRoot }) { ); const lint = await runCommand({ command: "sandbox-exec", - args: ["-p", NO_NETWORK_PROFILE, "bun", "run", "lint"], + args: ["-p", NO_EXTERNAL_NETWORK_PROFILE, "bun", "run", "lint"], cwd: baselineDir, deadlineMs, stdoutFile: join(logDir, "lint.log"), @@ -476,7 +493,7 @@ async function captureBaseline({ repoRoot, base, deadlineMs, runRoot }) { }); const tests = await runCommand({ command: "sandbox-exec", - args: ["-p", NO_NETWORK_PROFILE, "bun", "test"], + args: ["-p", NO_EXTERNAL_NETWORK_PROFILE, "bun", ...unitTestArgs(baselineDir)], cwd: baselineDir, deadlineMs, stdoutFile: join(logDir, "test.log"), @@ -492,8 +509,8 @@ async function captureBaseline({ repoRoot, base, deadlineMs, runRoot }) { if (testSummary.fail !== failedTests.length) { throw new Error("Baseline failing-test names do not match the failure count"); } - if (testSummary.fail !== 8) { - throw new Error(`Expected the pinned baseline of 8 failures, found ${testSummary.fail}`); + if (testSummary.fail !== 0) { + throw new Error(`Expected a green unit-test baseline, found ${testSummary.fail} failures`); } return { baseSha, lint: lintSummary, tests: testSummary, failedTests }; } finally { @@ -606,7 +623,7 @@ async function validateIteration({ ["diff", "git", ["diff", "--check"], true], ["typecheck", "bun", ["run", "typecheck"], true], ["lint", "bun", ["run", "lint"], true], - ["test", "bun", ["test"], false], + ["test", "bun", unitTestArgs(worktree), true], ["build", "bun", ["run", "build"], true], ["examples", "bun", ["run", "examples:catalog:check"], true], ["repo-map", "bun", ["run", "repo-map:check"], true], @@ -616,7 +633,7 @@ async function validateIteration({ for (const [name, command, args, expectZero] of commands) { const commandResult = await runCommand({ command: "sandbox-exec", - args: ["-p", NO_NETWORK_PROFILE, command, ...args], + args: ["-p", NO_EXTERNAL_NETWORK_PROFILE, command, ...args], cwd: worktree, deadlineMs, stdoutFile: join(logDir, `gate-${name}.log`),