Repository navigation
Conversation
…acy schemas
Databases created from a pre-rewrite baseline cannot complete the migration
chain. 0006 rebuilds `share_links_kind_check` over rows that still hold the
retired `monitor`/`setup` kinds, so the check fails, the transaction rolls back,
and every later migration is left unapplied (35 of them as of 0040).
Rename those rows first (`monitor`→`enroll`, `setup`→`handoff`) and retire them,
matching the rule the backup importer already applies to the same legacy kinds
("import them as inactive enrollment links so their old tokens never regain a
usable public endpoint", `src/console/backup/validate.ts`): the rows were written
for the old page contract, and an old token must not start answering the new one.
Objects introduced by editing `0000_baseline.sql` itself reach no existing
database — the snapshot is already in the ledger, and no later file adds them.
A pre-rewrite database therefore finished the chain while still missing
`telemetry_usage_totals` and the share-key columns on `api_keys`
(`key_mode`, `parent_key_id`, `issued_client_ip`, `issued_client_ip_key`), then
failed its first key lookup with `column "key_mode" does not exist`.
`0041_reconcile_legacy_schema.sql` closes that gap idempotently.
Unused legacy objects (`backup_status`, `console_users.last_login_at`,
`telemetry_payloads.redaction_applied`, four superseded indexes) are left in
place: dropping them is destructive and not required to unblock the upgrade.
Verified against a production dump: chain completes (42 migrations), the
resulting schema matches a fresh database column-for-column, a live completion
succeeds, and data is preserved.
`homeDir` re-exported `node:os`'s `homedir()`, which Bun resolves once and freezes for the process lifetime. The injector tests redirect `HOME` to a temp directory — the seam this module's own header documents — so every injector kept reading and writing the operator's real home directory instead. The codex lifecycle suite failed on a stale real `~/.codex`, and its `apply` wrote the fixture config into that directory. Resolve `HOME`/`USERPROFILE` on each call and keep `homedir()` as the fallback for a process started without either variable.
Three defects kept `bun run test` red on a clean checkout, and two of them hid each other. `requireDatabase()` registered a `beforeAll` that called `getTestPool()` unconditionally, so a machine without `CARTETHYIA_TEST_DATABASE_URL` failed two suites instead of skipping them — the opposite of this module's promise that an unconfigured service skips with a stated reason. The suites that call it were plain `describe`, so they were not skipped either; they now use the `dbDescribe` primitive that already existed for exactly this. `.env.test` and `.env.test.example` pointed at `postgres://postgres@localhost:5432/...`. `pg_hba` requires scram over TCP, so the passwordless URL failed all twelve database suites at connect time. Both now carry a dedicated `cartethyia_test` role, and `.env.test.example` uses the variable name the suites and `test-db:check` actually read — it held the retired `TEST_DATABASE_URL`, the exact mismatch `test-db-check.ts` documents. Verified on a local PostgreSQL 18: 1992 backend + 375 dashboard tests pass with the database reachable, and the same run is green with 142 stated skips when it is not.
…ktop path `dropCorruptAttachments`'s two capture-based tests read `C:\Users\Aria\Desktop\awok..txt`, an absolute path to the author's desktop that never entered the repository. They cannot pass anywhere else, so the suite was red on every other machine. The defect they pinned — an IDAT chunk whose stored CRC disagrees with its bytes — is already reproduced from this file's PNG primitives by the `png_crc_mismatch` test, and the well-formed counterpart by the valid 1x1 fixture. The rationale moves onto that test so the regression's origin is not lost with the file path.
Twenty `agent.minimax.io` accounts sat in the `minimax` provider with nothing
behind them: every account verified active, the console listed no models, and
`/v1/models` advertised no MiniMax route at all. The provider needed a
dispatch adapter before any of that capacity could be reached.
`integrations/minimax.ts` drives the upstream's three-call agent handshake —
agent list, session create, then message SSE on the separate `agent-stream`
host — carrying the signature the shipped web client sends
(`md5(timestamp + salt + body)`) and the query envelope the upstream expects.
Every dispatch opens a fresh session, so concurrent requests never share the
agent's mutable server-side history, and the catalog is taken from the
upstream's own `context_limit` values rather than base-catalog guesses.
The provider row is marked `bespokeWire`, because the adapter frames its own
protocol: there is no canonical codec behind it to drop a part it cannot
encode or to translate a generation control it does not read. It builds its
own User-Agent for the same reason, so the dashboard offers no route-level
override.
The credential is the object the `minimax-agent-automation` harvester writes,
and one field in it is load-bearing rather than decorative. The upstream keys
every query envelope on the account id, and that id is the harvester's
`realUserID` — NOT the JWT's `user.id`. The two are different numbers for every
harvested account (562139107990159360 against 562139107906273285 for the same
row), and the upstream answers 401 for the token's value and for an absent
`user_id` alike, so it cannot be recovered from the token. A credential without
it is refused at parse time instead of dispatched into a bare 401, and the
harvested JSON therefore carries `{email, password, cookie, jwt, realUserID}`
as one unit: the bearer JWT, the cookie jar the WAF expects, and the id the
envelope is keyed on.
The reasoning block had to name no default. The upstream accepts only `low`,
`medium`, `high`, `xhigh`, and `max`, and answers anything else with
`invalid params, invalid reasoning_effort: "default"` — a rejection the agent
delivers as an ordinary assistant message, so the first version returned HTTP
200 whose completion was the error text, with zero usage and `stop` as the
finish reason. The block is now omitted unless the request names one of the
five, which is what the upstream does on its own.
Verified end to end against the pooled accounts: all four catalogued models
answered `SMOKE_OK` through the adapter directly, and again through the real
`/v1/chat/completions` surface, streaming and non-streaming. The suite covers
the model block's effort handling, the credential shapes including the missing
account id, the envelope's `user_id`, the chunk/final-envelope de-duplication,
the handshake call order, and an upstream rejection surfacing as a gateway
error.
`bun run test` failed intermittently with `40P01 deadlock detected`. The server log names both sides: a migration suite's `ALTER TABLE ... ADD COLUMN` holds `provider_routing_settings` and waits for `provider_accounts`, while a tenant teardown's `on delete cascade` holds `provider_accounts` and waits for `provider_routing_settings`. Neither side is wrong and no lock order fixes it. The DDL transaction must change two tables, so it can only ever hold one of the two locks while it waits for the other; the cascade's order across the eleven tenant-scoped tables is the server's choice. Measured on a pristine `origin/dev`, 3 runs in 14 failed, each one a different suite — whichever transaction the server picked as the victim. The runner now executes `test/persistence/migration-*.test.ts` first, with `--parallel` off, and excludes them from the parallel pass. With no other connection open, no other transaction can hold the lock the DDL waits for, so the inversion cannot form. The two passes together still run every backend file once. `SCHEMA_DDL_DIR`/`SCHEMA_DDL_FILE` pick the files for the serial pass and `SCHEMA_DDL_IGNORE` is the same predicate for `--path-ignore-patterns`; the glob must stay rooted with `**` because the flag matches paths as `bun test` discovered them, relative to the working directory. An absolute pattern matches nothing, which runs those files twice and restores the deadlock.
With `CARTETHYIA_TEST_DATABASE_URL` unset the run was supposed to be green with stated skips, and it was not: four cases in `remote-debug-access`, one error per file in `static-token-detection`, `runtime-settings-legacy` and `share-link-refusal`. Three distinct causes, one shape. `getDb()` throws when no connection string is configured, and a skipped suite still evaluates its body to collect the cases — so a handle built at collection time fails the file instead of skipping it. `remote-debug-access` was worse: it was a plain `describe` over `createWorld`, which needs the database, so it ran and failed four assertions rather than skipping. Each site now resolves its handle where the work happens: inside `beforeAll` for the two suites that keep it for the whole file, and per call for `share-link-refusal`, whose helpers are the only consumers. `remote-debug-access` uses the `dbDescribe` primitive that exists for exactly this. `requireDatabase()` is gone from `share-link-refusal`: with `dbDescribe` it was the second gate on the same condition. No behaviour changes when a database is configured: 1992 backend + 375 dashboard pass, same as before.
The workflow was `workflow_dispatch` only and ran two typechecks, so nothing ever executed `test/` — even though `AGENTS.md` names `bun run test` as a gate. Two defects sat in `dev` because of that: a test that opened an absolute path on the author's desktop, and a `homeDir` that Bun caches on first use, which made the injector tests write into the operator's real home directory. Adds a `test` job with disposable PostgreSQL and Redis services carrying the same image, credentials and host port as `docker-compose.test.yml`, so the URL that works in CI also works for `bun run test-db:up` locally. The job exports both service URLs; `scripts/ci-run-tests.ts` loads `.env.test` without overriding exported variables, which is what lets CI keep the file's fixed encryption key while pointing at the service containers. Triggers become `push` to `dev`/`SeeYouLater`, `pull_request`, and the existing manual dispatch, with `contents: read` and a ref-scoped concurrency group. `bun-version` is dropped from all three jobs: `oven-sh/setup-bun` reads the committed `.bun-version`, the same pin `packageManager` and `engines` carry, where `latest` let CI drift onto a Bun the lockfile was never resolved against. Verified locally against the same service shape the workflow declares (PostgreSQL on 55432, Redis db 15): 1992 backend + 375 dashboard tests pass with no skips, and a control run with a bogus port fails, proving an exported `CARTETHYIA_TEST_DATABASE_URL` overrides `.env.test` as the job relies on.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
risunCode
added a commit
that referenced
this pull request
Oct 15, 2026
The priority table had no notion of what is done, so it was starting to mislead: several items are implemented, two are blocked on undocumented wire shapes, and one (#5) rests on a premise that the code contradicts. Adds a Status column and a notes section recording WHY #2, #5 and #11 are not done - all three need a capture or donor source, not effort. Also records that the two Factory legal-risk items are excluded pending a legal read.
risunCode
added a commit
that referenced
this pull request
Oct 15, 2026
The backend version-gates SKU availability against client_version on /models independently of a model's declared minimal_client_version (donor packages/catalog/src/wire/codex.ts:24-34). We sent nothing, so discovery could omit SKUs the backend would serve at our pin - the donor's own example: a SKU hidden at 0.155.1 and listed at 0.159.0. buildCodexModelsUrl validates the version against ^\d+\.\d+\.\d+$ so a resolver value can never become a query-injection vector, and returns the bare URL when the version does not match rather than sending a malformed param. Our pin is unchanged - bumping it is a separate decision with its own request-shape risk. 14 tests pass, typecheck clean.
risunCode
added a commit
that referenced
this pull request
Oct 15, 2026
#2, #5, #8, #11, #14 were each marked blocked on undocumented wire behavior. All five were answerable from the donor checkout named in this file's own header. Records the lesson too, with the two concrete ways it bit: 'not in our repo' is not 'unknown', and a guess that compiles can still be wrong on the wire (#2 was guessed into stop_details; it is top-level).
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Combines the four branches above in the order they must land, so a single merge produces a verified tree.
Closed by the author.