Skip to content
Open
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
10 changes: 8 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ jobs:
# version in the user agent the probes carry (identity.ts), the
# anthropic-version every Messages probe sends
# (request-shape.ts), and the probes' own inputs, CI arming
# and the environment reader that arms a key (e2e-env.ts)
# included. Left out, and covered by the offline suite and the
# nightly `full` run: the generated catalog (catalog-data.ts)
# and the live model choice (models-catalog.ts), which the
Expand All @@ -287,7 +288,7 @@ jobs:
# it. grep reads a here-string, not a pipe: under
# pipefail, `grep -q` exiting on an early match could kill the
# writer with SIGPIPE on a long path list and read as a miss.
if grep -Eq '^(src/host/|src/utils/protocols\.ts$|src/utils/plane-model\.ts$|src/identity\.ts$|src/utils/request-shape\.ts$|test/e2e/keyed-probes\.e2e\.ts$|test/support/keyed-probes\.ts$|test/support/keyless-matrix\.ts$|\.github/workflows/ci\.yml$)' <<<"$changed"; then
if grep -Eq '^(src/host/|src/utils/protocols\.ts$|src/utils/plane-model\.ts$|src/identity\.ts$|src/utils/request-shape\.ts$|test/e2e/keyed-probes\.e2e\.ts$|test/support/keyed-probes\.ts$|test/support/keyless-matrix\.ts$|test/support/e2e-env\.ts$|\.github/workflows/ci\.yml$)' <<<"$changed"; then
tier=probes
else
tier=none
Expand Down Expand Up @@ -402,8 +403,13 @@ jobs:
# models-catalog.ts, config.ts, the generated catalog) is covered on
# each pull request by the in-process characterization of the same
# S0_TODAY rows, and here by the nightly run.
# The binary e2e's own inputs are in scope too: the file, the rows it
# asserts (support/host.ts), the versions it installs
# (support/host-versions.ts) and the reader its gate and every input
# come through (support/e2e-env.ts). test/keyed-probes.test.ts
# compiles this pattern and checks it.
# A here-string, not a pipe: see the e2e job's tier step.
if grep -Eq '^(src/host/|src/lifecycle\.ts$|src/identity\.ts$|src/cordis-context\.ts$|src/models-discovery\.ts$|src/fetch-patch\.ts$|src/stream-hook\.ts$|cordis\.patch\.yml$|test/e2e/host/|test/e2e/host-binary\.e2e\.ts$|test/support/host\.ts$|test/support/host-versions\.ts$|patches/|\.github/workflows/ci\.yml$|vitest\.e2e\.config\.ts$|vite\.config\.ts$)' <<<"$changed"; then
if grep -Eq '^(src/host/|src/lifecycle\.ts$|src/identity\.ts$|src/cordis-context\.ts$|src/models-discovery\.ts$|src/fetch-patch\.ts$|src/stream-hook\.ts$|cordis\.patch\.yml$|test/e2e/host/|test/e2e/host-binary\.e2e\.ts$|test/support/host\.ts$|test/support/host-versions\.ts$|test/support/e2e-env\.ts$|patches/|\.github/workflows/ci\.yml$|vitest\.e2e\.config\.ts$|vite\.config\.ts$)' <<<"$changed"; then
echo "run=true" >> "$GITHUB_OUTPUT"
else
echo "run=false" >> "$GITHUB_OUTPUT"
Expand Down
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ Host bundle (`lib/index.mjs`), a thin `apply` barrel over small modules:

- `src/index.ts` · `src/identity.ts` · `src/lifecycle.ts` — the public barrel, the identity constants, and the `apply()` composition (one installer per side effect, in load-bearing order). The barrel is the host's contract and nothing more: `apply`, `Config`, `inject`, `name` and the config types. Everything else is internal, and `test/public-api.test.ts` pins the list, so widening it is a reviewed change.
- `test/support/src.ts` — the one door tests use into `src`: each name re-exported from the module that DEFINES it, never through the barrel, so a module move edits one file. `test/architecture.test.ts` enforces both halves — every name is declared in its target, and no test outside the named exemptions (`vi.mock` targets, fresh re-imports after `vi.resetModules()`, the architecture and public-API tests, the jsdom client tests, the `reset-process-state.ts` setup file) imports a `src` path, and no jsdom test loads the door.
- `test/support/composition.ts` — the plugin mounted on a real cordis `Context` the way the Loader mounts it (`mountPlugin`: `ctx.plugin({ apply, Config, inject, name }, config)`, so the schema resolves the row config; a turn through `ctx.waterfall("llm/stream", …)`, teardown through `fiber.dispose()`), for a unit test that means "the plugin, applied". The mount passes the module's exports unchanged, so repeated activations on one root share one cordis runtime, as the Loader's do, and `activated()` is read off the fiber. `degradedHost` is the one stand-in for the plugin's COMPOSITION, for a composition a real `Context` cannot be, because a real one registers what it is asked to and always has `plugin`: (1) a `plugin` that registers nothing, with and without `get`, which drives the three unreachable-service diagnostics; (2) no `plugin`, or neither `plugin` nor `provide`. Its `effect` runs and keeps the disposer, never a no-op. Its two context-only helpers, `until` and `logLines`, come from `test/support/cordis-wait.ts` (which `host.ts` re-exports), so a bare-`Context` file loads no host package.
- `test/support/route-claim-stand-in.ts` — the route claim's own stand-in host, kept on purpose for the fiber states a real host cannot be put in on demand (a LOADING or FAILED pi-ai, a null uid, a refusing registry); it drives `test/route-claim*.test.ts`, and `test/host-planes*.test.ts` covers the claim on the real host. The discovery, Settings-graft and reload tests likewise keep the `llm` object their seam wraps. The client entry's tests (`test/settings-page*.test.tsx`, `test/client-bundle.test.ts`) keep a hand-built settings host of their own: the web runtime has no in-process tier, and the jsdom files must not load the door. The go-discovery and usage-service files serve their loader rows and credentials on `new Context()` (`test/support/loader-rows.ts`), so the reader takes the `ctx.get` arm a real host takes.
- `test/support/e2e-env.ts` — the live suites' environment, read in one place (`readE2eEnvironment`, and `e2e()` for the process's own, read on first call so loading the module reads nothing): whether the live suite runs, the key tier and the key it arms per plane, a mirror's bases, the record directory, the CI run and the host-binary cell. No e2e file reads `process.env` for these itself. `test/e2e-env.test.ts` drives it with environments of its own: the `live` gate, the key each plane arms per tier, blank bases, an unknown tier, the host-binary cell.
- `test/support/host-versions.ts` — the host the test install runs (pi, the `@deepseek-ai` cluster, cordis and its loader), named once; `test/host-versions.test.ts` checks the installed tree against it (with `pnpm-lock.yaml` as the record of what pnpm resolved, and the published peer ranges) and `test/harness-channel.test.ts` checks the declarations and the lockfile, including that `@deepseek-ai/schemastery` resolves to one copy inside the cluster's range.
- `test/support/host.ts` — the real host, in-process, in two tiers: **A** (`mountTierA`: a bare cordis `Context`, `LlmRuntime` and `dsh-llm-pi-ai` mounted with `{ providers }`) for the `internal/config` waterfall and pi-ai's credential paths, and **B** (`bootTierB`: dsh-app-boot's `boot()` with a temporary HOME and a test bundle) for the config editor, `settings.describe`, the volatile commit and the characterization of the plugin as it ships. Its fakes (`fakeCredentials`, `fakeLaunchEnvironment`, `recordingFetch`) keep every case offline and its credentials out of the environment (a boot blanks the two keys the plugin reads directly, should the shell export them, and restores them on dispose); `S0_PROFILE` is the contract fixture, `CHARACTERIZED_PROFILE` (S0 plus a declared catalog-unknown id) the characterization's, and `S0_TODAY` the measured per-model rows, tagged per facet, that the binary e2e shares (it reads `S0_TODAY.rows` directly; `todayRowsFor`, which only the characterization calls, answers the same rows for either apply order since the route claim). `logLines` captures every logger line at every level, because cordis's own buffer stops at `info` and tier B prints nothing to the console. Tier A has no loader, so the plugin's default host-module source cannot find pi-ai there; the harness swaps in `hostModulesForTests` through the seam in `src/cordis-context.ts`, and the route claim serves the planes in tier A as it does on the real host; `test/host-harness.test.ts` exercises the builder options no contract or characterization case passes, and tier B's two failure paths (an invalid first row, an exported key). Tier B's rows carry PACKAGE names through a stub module loader, never `cordis:` builtins, because the plugin's default host-module source finds pi-ai's entry by `options.name` (`piAiEntry` in `src/host/host-modules.ts`) and a builtin-named row would leave the route claim without pi-ai's module. It also fixes which of pi-ai and the plugin applies first (`order`), because the loader starts rows in parallel and the published binary applies the plugin first; the held row is released when the first one applies or its fiber settles, so an invalid first row fails instead of hanging the boot.
- `test/support/host.ts` — the real host, in-process, in two tiers: **A** (`mountTierA`: a bare cordis `Context`, `LlmRuntime` and `dsh-llm-pi-ai` mounted with `{ providers }`) for the `internal/config` waterfall and pi-ai's credential paths, and **B** (`bootTierB`: dsh-app-boot's `boot()` with a temporary HOME and a test bundle) for the config editor, `settings.describe`, the volatile commit and the characterization of the plugin as it ships. Its fakes (`fakeCredentials`, `fakeLaunchEnvironment`, `recordingFetch`) keep every case offline and its credentials out of the environment (a boot blanks the two keys the plugin reads directly, should the shell export them, and restores them on dispose); `S0_PROFILE` is the contract fixture, `CHARACTERIZED_PROFILE` (S0 plus a declared catalog-unknown id) the characterization's, and `S0_TODAY` the measured per-model rows, tagged per facet, that the binary e2e shares (it reads `S0_TODAY.rows` directly; `todayRowsFor`, which only the characterization calls, answers the same rows for either apply order since the route claim). `logLines` captures every logger line at every level, because cordis's own buffer stops at `info` and tier B prints nothing to the console. Tier A has no loader, so the plugin's default host-module source cannot find pi-ai there; the harness swaps in `hostModulesForTests` through the seam in `src/cordis-context.ts`, and the route claim serves the planes in tier A as it does on the real host. Tier B's `pluginRow` files the plugin's row under another id and package (the rename notice's case), and its two failure paths (an invalid first row, in each apply order) are `host-planes`' cases. Tier B's rows carry PACKAGE names through a stub module loader, never `cordis:` builtins, because the plugin's default host-module source finds pi-ai's entry by `options.name` (`piAiEntry` in `src/host/host-modules.ts`) and a builtin-named row would leave the route claim without pi-ai's module. It also fixes which of pi-ai and the plugin applies first (`order`), because the loader starts rows in parallel and the published binary applies the plugin first; the held row is released when the first one applies or its fiber settles, so an invalid first row fails instead of hanging the boot.
- `test/support/keyless-matrix.ts` — the keyless 401-vs-404 probe matrix's cell table and classifier over three URL shapes (legacy `/zen(/go)/v1`, the root `/inference(/go)/v1`, modern `/inference(/go)/{openai,anthropic,google}/…`), the keyless free-model probe's cells and classifier, and the cells each `endpoint` setting dispatches to (`TODAY_DISPATCH`, `LEGACY_DISPATCH`). Pure, though it lives in `test/support/` rather than `src/utils/`: a function of its arguments, with no code path that can set a credential header; `test/e2e/keyless-matrix.e2e.ts` sends the cells.
- `test/support/keyed-probes.ts` — the keyed cells (the keyless ones with the plane's key in the header its protocol reads, and a body of a few tokens), their classifier, and the `OPENCODE_E2E_KEYED` tier every keyed live suite reads its key through (`armedKey`). Pure: it takes the variable's value and the key as arguments; `test/e2e/keyed-probes.e2e.ts` sends the cells.
- `test/support/endpoint-record.ts` — the dated markdown table both live probe suites write, which CI publishes and `docs/redirect-planes.md` §13 holds; `test/endpoint-record.test.ts` keeps that section in the formatter's columns and row order.
Expand Down
Loading
Loading