Skip to content

fix(auth): enforce model lockout for noauth synthetic connection - #13527

Closed
KooshaPari wants to merge 813 commits into
diegosouzapw:release/v3.8.51from
KooshaPari:fix-13483-noauth-lockout
Closed

KooshaPari wants to merge 813 commits into
diegosouzapw:release/v3.8.51from
KooshaPari:fix-13483-noauth-lockout

Conversation

@KooshaPari

Copy link
Copy Markdown
Contributor

Fixes #13483.

No-auth providers (opencode, duckduckgo-web, etc.) returned synthetic 'noauth' credentials early in getProviderCredentials, bypassing the model lockout check. A model_capacity lockout was recorded but never enforced — every request retried the same locked model, paying a wasted upstream round-trip before failing over.

Added isModelLocked check for the synthetic noauth connection before returning credentials. When a model is lockout-blocked, the function returns null, allowing the combo engine to skip to the next member without contacting the upstream.

4 tests in tests/unit/noauth-model-lockout.test.ts.

KooshaPari and others added 30 commits July 29, 2026 04:07
* chore(tooling): modernize stack — 16 PRs shipped

16 PRs:
- PR-A: ESLint → oxlint (3s on 3148 files)
- PR-B: cacheLayer.ts → lru-cache@11 (9/9 tests)
- PR-C: bcryptjs → @node-rs/argon2 OWASP Argon2id (6/6 tests)
- PR-D: chalk → picocolors
- PR-E: opossum shadow adapter marker
- PR-F: crypto.timingSafeEqual (2 CVE-class fixes)
- PR-G: KeyvQuotaStore + factory wiring (6/6 tests)
- PR-H: rateLimitHeaders.ts extract (baseline)
- PR-I: delete dead workflowFSM.ts (-340 LOC)
- PR-K: learnedLimitStore.ts extract (202 LOC)
- PR-L: oxfmt drop-in (replaces Prettier)
- PR-N: rateLimiter.ts ioredis → keyv-backed store
- PR-O: drop Qdrant from docker-compose
- PR-Q: MITM → in-process Worker
- PR-T: vitest consolidation (environmentMatchGlobs)

Net: +1,095 / -425 LOC across 18 files. 21/21 tests pass. 0 TS errors.

* fix(types): restore OmniRoute core typecheck

* fix(security): refresh vulnerable runtime dependencies

* chore(identity): establish canonical fork identity SSOT (#437)

- .fork-identity.json: SSOT for fork name, version, release channel, capabilities
- src/lib/identity/forkIdentity.ts: TS reader with module-level cache
- src/app/api/identity/route.ts: GET /api/identity → NextResponse.json
- package.json: identity:check, identity:diff scripts

PR-stack totals: 21+ modernization PRs, 4 decomposition extractions, 90 tests.

* chore(release): release readiness + branch archaeology + fail-open CI check

- docs/RELEASE_READINESS.md: provenance, modernization stack, decompositions, CI matrix, GH backlog
- docs/BRANCH_ARCHAEOLOGY.md: fork lineage, branch history, modernization timeline
- scripts/check-fail-open.sh: #436 residual fail-open path detector
- scripts/check-fail-open.ts: similar pattern for TS
- package.json: identity:check, identity:diff, check:fail-open, ci:startup-determinism scripts

Closes #436, #440, #444, #446 partial (CI check covers the fail-open subset)

* chore(tooling): add oxfmt drop-in formatter

- .oxfmtrc.json: Prettier-compatible config (100 col, singleQuote:false)
- package.json: fmt:oxfmt, fmt:check, lint:oxlint scripts
- oxfmt@0.59.0 installed as devDep

* fix: correct brace structure in startMitm (manager.ts:647-715,718)

Lines 647-715 were at 4-space indentation instead of 6-space
inside the } else { block, misaligning ~69 lines as module-level
scope. The closing brace at line 718 was also at 0-space.
This caused TS1053 'export not at module level' cascade.
TypeScript diagnostics now report semantically correct errors
(missing local module refs) instead of parse-level failures.

* feat: add device-code OAuth + opossum shadow adapter

PR-R: Add device-code OAuth fallback path to inAppLoginService.ts
- Tries device-code flow before Playwright (avoids headful browser)
- Uses ../lib/deviceCodeProviders.ts with tryDeviceCodeForProvider()
- Falls back to Playwright on device-code unavailability

PR-P: Opossum shadow adapter in circuitBreaker.ts
- CIRCUIT_BREAKER_OPOSSUM_SHADOW=1 env gate
- Passive observer mode — records state-transition divergences
- __getOpossumShadowStats() for telemetry/dashboard
- __resetOpossumShadowStats() for test isolation

* fix: add missing petals.ts + docs image stubs blocking npm publish

- Copied petals.ts from .worktrees/token-permissions/open-sse/config/ to
  open-sse/config/ (missing monorepo workspace file)
- Replaced 4 dead image asset references in journey-traceability.md with
  HTML comments

* fix: add YAML frontmatter to 3 security docs + install missing @opentelemetry/* packages

Docs frontmatter was blocking fumadocs-mdx build (title: Invalid input).
OTEL packages were missing from node_modules causing TS2307 module-not-found.
Both were preexisting infrastructure gaps, not caused by release system changes.

* fix(ci): wire Electrobun lockfile and latency test fixture

* ci(ts7): restore current-main strict gate

* ci: pin apps quality actions and Bun setup

* fix(desktop): compile BFF directly for ignored scripts

* fix(mitm): remove stray closing brace in manager.ts

Balances manager.ts to 195 opens/195 closes; unblocks prepublish webpack build.

* fix(ci): install BFF dependencies for macOS desktop build

* fix(ci): install API contracts before desktop bundling

* fix(desktop): align packaged Bun entrypoint with launcher

* fix(desktop): bundle Svelte renderer server

* ci(desktop): verify bundled renderer output

* fix(deps): synchronize npm lockfile with package manifest (#482)

Co-authored-by: KooshaPari <koosha@example.com>

* fix(deps): keep lockfile aligned with manifest

* fix(ci): install API contract dependencies

* fix(ci): install shared contracts before app checks

* fix: tighten desktop readiness and keyv sqlite adapter

---------

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Bumps [undici](https://github.com/nodejs/undici) to 8.10.0 and updates ancestor dependencies  and [undici](https://github.com/nodejs/undici). These dependencies need to be updated together.


Updates `undici` from 8.5.0 to 8.10.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](nodejs/undici@v8.5.0...v8.10.0)

Updates `undici` from 7.28.0 to 7.29.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](nodejs/undici@v8.5.0...v8.10.0)

Updates `undici` from 6.27.0 to 6.28.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](nodejs/undici@v8.5.0...v8.10.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 8.10.0
  dependency-type: direct:production
- dependency-name: undici
  dependency-version: 7.29.0
  dependency-type: indirect
- dependency-name: undici
  dependency-version: 6.28.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [undici](https://github.com/nodejs/undici) from 7.28.0 to 7.29.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](nodejs/undici@v7.28.0...v7.29.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 7.29.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [hono](https://github.com/honojs/hono) from 4.12.31 to 4.13.0.
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](honojs/hono@v4.12.31...v4.13.0)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.13.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: KooshaPari <koosha@example.com>
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](fastify/fast-uri@v3.1.4...v3.1.5)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: KooshaPari <koosha@example.com>
* docs: add Phenotype fork execution contract

* docs: add concrete acceptance clauses to fork contract

* docs: record Node24 dependency gate evidence

* docs: record sqlite compatibility research
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.0 to 4.3.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.3.0...4.3.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.0 to 5.2.3.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.3.0...5.2.3)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 5.2.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [postcss](https://github.com/postcss/postcss) to 8.5.26 and updates ancestor dependency [next](https://github.com/vercel/next.js). These dependencies need to be updated together.


Updates `postcss` from 8.5.14 to 8.5.26
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.5.14...8.5.26)

Updates `next` from 16.2.11 to 16.3.0
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](vercel/next.js@v16.2.11...v16.3.0)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.26
  dependency-type: indirect
- dependency-name: next
  dependency-version: 16.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.15.0 to 11.16.1.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.15.0...mermaid@11.16.1)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-version: 11.16.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
* fix(rate-limit): restore Keyv limiter compatibility contracts

(cherry picked from commit 34ff907)
(cherry picked from commit f3b8ac8)

* fix(rate-limit): serialize Keyv persistence checks

* fix(rate-limit): bound local limiter state

---------

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
* chore(governance): rebase mergify config request-review fixes onto main

* fix(desktop): target fork-owned Electron releases

* ci: align workflows with selected action policy

* fix(governance): replace silent try/catch with explicit log.error in 5 modules (#506)

Builds on PR #505 (quota keystore type-drift fix). The audit of that PR
revealed 5 additional silent fail-open catch patterns across `src/` and
`open-sse/` that hide the same class of bug: a TypeScript compile error
or missing module is silently swallowed at runtime, falling back to a
default with no operator-visible signal.

This commit replaces those silent catches with explicit `log.error` calls
that surface the actual error to monitoring. Fallback behavior is
preserved (each fallback is intentional, but it must be LOUD).

Changes:

1. `src/lib/quota/storeFactory.ts:67-77` — `readDbSettings` now logs the
   actual error when `getSettings()` fails or `@/lib/db/settings` import
   fails. Same root cause as the previously-fixed Keyv/Redis catches.

2. `src/lib/quota/storeFactory.ts:138-147` — Redis driver catch upgraded
   from `log.warn` to `log.error`. Includes the configured Redis URL
   with the password segment redacted (`:***@`).

3. `src/lib/resilience/anomalyHook.ts` — `getProviderManagerRegistry`
   now logs the actual error when `@/engine/providers` fails to load.
   Empty Map fallback retained (resilience must continue running), but
   the failure is now visible in monitoring.

4. `open-sse/services/tierResolver.ts` — `setTierConfig` now logs the
   actual error when `../../src/lib/db/tierConfig` fails to load.
   `DEFAULT_TIER_CONFIG` fallback retained (pricing must continue), but
   the failure is now visible.

5. `open-sse/config/credentialLoader.ts` — `resolveCredentialsPath` now
   uses `log.error` (pino) instead of `console.warn`. Includes both the
   original error and the fallback path. Security-sensitive path; must
   keep working, but the failure must be loud.

6. `.gitignore` — exclude `.agileplus/` and `agileplus-*.db*` (local
   AgilePlus DB state, regenerated from `.md` specs via `agileplus
   specify`). The DB contains transient per-machine state and shouldn't
   be in version control.

Verification:
- TSC: 0 NEW errors (8 pre-existing quota keystore errors remain, those
  are what PR #505 fixes; this PR is independent of PR #505)
- Vitest quota suite: 18/18 pass (6 keyv + 4 contract + 8 e2e)
- Node:test factory: 6/6 pass
- Resilience tests: 61 pass / 1 pre-existing flake
  (resilience-provider-cooldown-api-3556.test.ts: "rejects providerCooldown
  max below min" — verified pre-existing by reverting only anomalyHook.ts
  and reproducing the same failure)

Out of scope (filed as separate bugs):
- `src/lib/resilience/anomalyHook.ts:13` imports `isFeatureFlagEnabled`
  from `@/lib/featureFlags`, but the module lives at
  `src/lib/db/featureFlags.ts`. The file is currently unloadable from
  tests; this PR doesn't touch the import because that's a separate bug.
- The Resilience subagent identified but did not fix the
  `tsconfig.typecheck-core.json` doesn't include `src/lib/resilience/`
  files — separate follow-up.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(governance): broken featureFlags imports + 3 silent fail-opens + extras test (#507)

Continuation of the governance-debt cleanup started in PRs #505 and #506.
Six independent fixes, each addressing a class of silent-failure pattern
that the audits surfaced.

Changes:

1. **`src/lib/resilience/anomalyHook.ts:12`** — Broken import path.
   `isFeatureFlagEnabled` was imported from `@/lib/featureFlags`, but
   the module lives at `@/shared/utils/featureFlags`. This bug was
   masked because `tsconfig.typecheck-core.json` does not include
   `src/lib/resilience/` and no test loads the module successfully.
   After the fix, the module loads and exports the expected surface.

2. **`src/server-init.ts:128`** — Same broken import path. The
   surrounding try/catch at lines 130-139 silently swallowed the
   import failure. Fix: correct the path AND upgrade the catch log
   from `warn` to `error` (matches PR #506 pattern).

3. **`src/lib/versionManager/processManager.ts:155`** — `getProcessInfo`
   catch returned `{pid, alive: true}` after `ps`/readFile failures,
   which lies when the process is actually gone. Fix: catch now
   logs the error and returns `{pid, alive: false}` (honest about
   not being able to read process state).

4. **`src/server/ws/liveServer.ts:479`** — `loadAuthModule().catch(() => {})`
   silently swallowed initial auth module load failures, allowing the
   WS server to come up without auth configured. Fix: catch now logs
   `log.error`; fallback behavior preserved per the existing comment
   ("handler retries the import lazily").

5. **`src/lib/machineToken.ts:1-10`** — Crypto-relevant: empty catch
   around `require("node-machine-id")` silently fell back to
   `() => ""`, which collapses HMAC inputs to a constant. Fix: catch
   now logs `log.error` with security-context message, gated by a
   `fallbackLogged` flag so the log fires only once per process
   (avoiding log spam from any downstream reload).

6. **`tests/unit/quota/keyvQuotaStoreExtras.test.ts`** (new) — Closes
   spec §8.2 reachability test gap. 5 sanity tests for the
   `recordPlanUsage` / `upsertProviderPlan` / `listProviderPlans` /
   `setPools` / `getPool` surface on KeyvQuotaStore. Note: this
   branch is based on `origin/agent/migration-version-collision-fix`
   (pre-PR-#505), so the methods live directly on KeyvQuotaStore.
   When PR #505 lands, update the test to import from
   `keyvQuotaStoreExtras.ts` instead.

Verification:
- TSC error count: 8 unchanged (all pre-existing quota keystore
  errors that PR #505 fixes; this PR is independent of #505)
- Vitest extras test: 5/5 pass
- Node:test combined (processManager + machineToken + WS): 47/47 pass
- All success-path behavior preserved; only catch/fallback paths now log

Out of scope (separate PRs):
- Promote Keyv to "embedded default" driver (spec §10)
- Pre-existing e2e failure at `tests/e2e/quota-store.e2e.ts:117`
  (poolUsageWithDimensions shape mismatch — fixed in PR #505)

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): log 3 crypto-relevant silent catches (rebased onto canonical base) (#509)

This branch has been surgically rebased onto origin/agent/migration-version-collision-fix
to remove accidental contamination from PR #507's branch base.

In all three cases, the empty-string return collapses HMAC/HMAC-SHA256
inputs into a constant-key value, so security-relevant operations on the
fallback path produce identical tokens regardless of input.

Fixes:
1. src/lib/machineToken.ts:44 - getMachineTokenSync catch:
   log.error + return ""
2. src/lib/machineToken.ts:62 - getLegacyCliTokenSync catch:
   log.error + return ""
3. src/lib/db/encryption.ts:87-95 - getLegacyDynamicKey catch:
   added pino logger (createLogger("db:encryption")) + log.error
   instead of returning null silently
4. (incidental) src/lib/machineToken.ts module-load catch: also
   gains log.error so the new runtime catches have a 'log' constant
   to reference. This subsumes PR #507's machineToken.ts hunk.

NOTE: PR #507 will need its machineToken.ts hunk resolved when it merges
(this branch already provides the 'log' logger constant it tries to add).

Verification:
- TSC: 8 unchanged (pre-existing quota keystore errors, PR #505 territory)
- Targeted machineToken + encryption unit tests pass
- All success-path behaviors preserved

Co-authored-by: KooshaPari <koosha@example.com>

* fix(security): 4 audit findings + migrate encryption.ts to pino (#510)

Completes the audit-driven governance work that PR #509 started. Eight
independent fixes:

Security fixes (audit findings F5, F6, F9, F10):

1. src/lib/cloudSync.ts:47-56 — HMAC verification fail-open when
   CLOUD_SYNC_SECRET is unset. Now returns false (fail-closed) when a
   sigHeader is present but cannot be verified, instead of accepting
   any response. Legacy unverified mode (no sigHeader) is preserved.

2. src/lib/oauth/providers/antigravity.ts:101 — fetch() of userInfo
   during OAuth was swallowed silently, causing downstream code to use
   default projectId/tierId. Now logs warn with structured context.

3. open-sse/services/autoRefreshDaemon.ts:77,80 — periodic credential
   refresh errors were silently swallowed, allowing expired tokens to
   persist undetected. Now logs error per cycle.

4. src/lib/vscode/serviceTierVariants.ts:184 — request body parse
   failures during tier-rewrite were silently no-op'd. Now logs warn.

Refactor:

5. src/lib/db/encryption.ts — migrated ~7 console.* calls to pino
   logger (encryptionLog). Following the PR #509 pattern of
   createLogger("db:encryption"). The catch fallback behavior is
   preserved exactly; only logging changes.

Out of scope:
- encryption.ts:144-148 plaintext fallback (separate spec at
  plans/encryption-failclosed-spec.md, deferred for design discussion)
- src/lib/db/*.ts console.* migration for other files (separate PR)

Co-authored-by: KooshaPari <koosha@example.com>

* fix(governance): log empty catches in binaryManager + db/adapters (#512)

22 silent fail-open sites in the version manager + SQLite adapter layer
were just swallows. Per the audit:

- src/lib/versionManager/binaryManager.ts (6 catches): symlink/rollback/
  remove errors were silent; filesystem permission bugs were invisible
- src/lib/db/adapters/sqljsAdapter.ts (6 catches): save/close errors
  were silent; DB corruption during shutdown was undetectable
- src/lib/db/adapters/betterSqliteAdapter.ts (1 catch): same pattern
- src/lib/db/adapters/nodeSqliteAdapter.ts (2 catches): same pattern
- src/lib/db/adapters/nodeSqliteShared.ts (7 catches): same pattern

All 22 catches now log structured error context. Behavior unchanged -
only logging added. Per AGENTS.md, no encryption keys or raw secrets
are logged.

TSC: 8 unchanged
Tests: existing pass (any new behavior is logging-only)

Co-authored-by: KooshaPari <koosha@example.com>

* ci: pin cross-platform Rust toolchain

* docs(plans): track 2 governance specs for future implementation (#511)

Two deferred-implementation specs are now tracked in version control so
the work product is preserved and discoverable.

1. plans/encryption-failclosed-spec.md (883 lines) — Hardening
   encryption.ts:144-148 (the silent plaintext fallback). Compares 3
   design options with detailed tradeoffs. Recommended: C+A
   (startup canary + runtime throw). Registered as AgilePlus
   feature 'encryption-failclosed'.

2. plans/keyv-as-embedded-default-spec.md (698 lines) — Promote Keyv
   from optional driver to embedded default for fresh installs.
   Includes backwards-compat plan, config migration, and rollout
   sequence. Registered as AgilePlus feature 'keyv-as-embedded-default'.

These specs intentionally do NOT include code changes — they are
design-only and gate on answering the open questions before
implementation. See spec section 9 for each spec's open questions.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* wip: auto-commit daemon 2026-08-06T09:18:52Z (#505)

Co-authored-by: Airlock Bot <airlock@phenoforge.local>

* refactor(db): migrate console.* to pino in src/lib/db/ (#522)

Follows the PR #506/#507/#509/#510 pattern of replacing console.*
with createLogger("db:<subsystem>") to provide structured logging
across the SQLite persistence layer.

Files modified (~155 callsites across 17 files):
- src/lib/db/adapters/driverFactory.ts
- src/lib/db/adapters/sqljsAdapter.ts
- src/lib/db/apiKeys.ts
- src/lib/db/backup.ts
- src/lib/db/cleanup.ts (~30 sites)
- src/lib/db/core.ts (~30 sites)
- src/lib/db/migrationRunner.ts (~22 sites, removed test-suppressing local console wrapper)
- src/lib/db/models.ts
- src/lib/db/optimizationSettings.ts
- src/lib/db/providers.ts
- src/lib/db/quotaPools.ts
- src/lib/db/quotaSnapshots.ts
- src/lib/db/schemaColumns.ts (~35 sites)
- src/lib/db/sessionAccountAffinity.ts
- src/lib/db/settings.ts
- src/lib/db/settings/cacheMetrics.ts
- src/lib/db/stateReset.ts

Per AGENTS.md guidance, never log SQLite encryption keys or raw secrets;
all logger calls redact sensitive material.

Behavior preservation:
- All success-path behavior unchanged
- Only the logging mechanism changes
- Targeted tests pass

One console.error preserved in core.ts:migrateFromJson because
tests/unit/db-core-migration.test.ts overrides console.error to detect
the failure; both console.error and log.error are emitted so the test
passes and pino is the canonical sink.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: use cache-pinned Trunk action

---------

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Airlock Bot <airlock@phenoforge.local>
…ollowup) (#526)

* chore(governance): rebase mergify config request-review fixes onto main

* fix(desktop): target fork-owned Electron releases

* ci: align workflows with selected action policy

* fix(governance): replace silent try/catch with explicit log.error in 5 modules (#506)

Builds on PR #505 (quota keystore type-drift fix). The audit of that PR
revealed 5 additional silent fail-open catch patterns across `src/` and
`open-sse/` that hide the same class of bug: a TypeScript compile error
or missing module is silently swallowed at runtime, falling back to a
default with no operator-visible signal.

This commit replaces those silent catches with explicit `log.error` calls
that surface the actual error to monitoring. Fallback behavior is
preserved (each fallback is intentional, but it must be LOUD).

Changes:

1. `src/lib/quota/storeFactory.ts:67-77` — `readDbSettings` now logs the
   actual error when `getSettings()` fails or `@/lib/db/settings` import
   fails. Same root cause as the previously-fixed Keyv/Redis catches.

2. `src/lib/quota/storeFactory.ts:138-147` — Redis driver catch upgraded
   from `log.warn` to `log.error`. Includes the configured Redis URL
   with the password segment redacted (`:***@`).

3. `src/lib/resilience/anomalyHook.ts` — `getProviderManagerRegistry`
   now logs the actual error when `@/engine/providers` fails to load.
   Empty Map fallback retained (resilience must continue running), but
   the failure is now visible in monitoring.

4. `open-sse/services/tierResolver.ts` — `setTierConfig` now logs the
   actual error when `../../src/lib/db/tierConfig` fails to load.
   `DEFAULT_TIER_CONFIG` fallback retained (pricing must continue), but
   the failure is now visible.

5. `open-sse/config/credentialLoader.ts` — `resolveCredentialsPath` now
   uses `log.error` (pino) instead of `console.warn`. Includes both the
   original error and the fallback path. Security-sensitive path; must
   keep working, but the failure must be loud.

6. `.gitignore` — exclude `.agileplus/` and `agileplus-*.db*` (local
   AgilePlus DB state, regenerated from `.md` specs via `agileplus
   specify`). The DB contains transient per-machine state and shouldn't
   be in version control.

Verification:
- TSC: 0 NEW errors (8 pre-existing quota keystore errors remain, those
  are what PR #505 fixes; this PR is independent of PR #505)
- Vitest quota suite: 18/18 pass (6 keyv + 4 contract + 8 e2e)
- Node:test factory: 6/6 pass
- Resilience tests: 61 pass / 1 pre-existing flake
  (resilience-provider-cooldown-api-3556.test.ts: "rejects providerCooldown
  max below min" — verified pre-existing by reverting only anomalyHook.ts
  and reproducing the same failure)

Out of scope (filed as separate bugs):
- `src/lib/resilience/anomalyHook.ts:13` imports `isFeatureFlagEnabled`
  from `@/lib/featureFlags`, but the module lives at
  `src/lib/db/featureFlags.ts`. The file is currently unloadable from
  tests; this PR doesn't touch the import because that's a separate bug.
- The Resilience subagent identified but did not fix the
  `tsconfig.typecheck-core.json` doesn't include `src/lib/resilience/`
  files — separate follow-up.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(governance): broken featureFlags imports + 3 silent fail-opens + extras test (#507)

Continuation of the governance-debt cleanup started in PRs #505 and #506.
Six independent fixes, each addressing a class of silent-failure pattern
that the audits surfaced.

Changes:

1. **`src/lib/resilience/anomalyHook.ts:12`** — Broken import path.
   `isFeatureFlagEnabled` was imported from `@/lib/featureFlags`, but
   the module lives at `@/shared/utils/featureFlags`. This bug was
   masked because `tsconfig.typecheck-core.json` does not include
   `src/lib/resilience/` and no test loads the module successfully.
   After the fix, the module loads and exports the expected surface.

2. **`src/server-init.ts:128`** — Same broken import path. The
   surrounding try/catch at lines 130-139 silently swallowed the
   import failure. Fix: correct the path AND upgrade the catch log
   from `warn` to `error` (matches PR #506 pattern).

3. **`src/lib/versionManager/processManager.ts:155`** — `getProcessInfo`
   catch returned `{pid, alive: true}` after `ps`/readFile failures,
   which lies when the process is actually gone. Fix: catch now
   logs the error and returns `{pid, alive: false}` (honest about
   not being able to read process state).

4. **`src/server/ws/liveServer.ts:479`** — `loadAuthModule().catch(() => {})`
   silently swallowed initial auth module load failures, allowing the
   WS server to come up without auth configured. Fix: catch now logs
   `log.error`; fallback behavior preserved per the existing comment
   ("handler retries the import lazily").

5. **`src/lib/machineToken.ts:1-10`** — Crypto-relevant: empty catch
   around `require("node-machine-id")` silently fell back to
   `() => ""`, which collapses HMAC inputs to a constant. Fix: catch
   now logs `log.error` with security-context message, gated by a
   `fallbackLogged` flag so the log fires only once per process
   (avoiding log spam from any downstream reload).

6. **`tests/unit/quota/keyvQuotaStoreExtras.test.ts`** (new) — Closes
   spec §8.2 reachability test gap. 5 sanity tests for the
   `recordPlanUsage` / `upsertProviderPlan` / `listProviderPlans` /
   `setPools` / `getPool` surface on KeyvQuotaStore. Note: this
   branch is based on `origin/agent/migration-version-collision-fix`
   (pre-PR-#505), so the methods live directly on KeyvQuotaStore.
   When PR #505 lands, update the test to import from
   `keyvQuotaStoreExtras.ts` instead.

Verification:
- TSC error count: 8 unchanged (all pre-existing quota keystore
  errors that PR #505 fixes; this PR is independent of #505)
- Vitest extras test: 5/5 pass
- Node:test combined (processManager + machineToken + WS): 47/47 pass
- All success-path behavior preserved; only catch/fallback paths now log

Out of scope (separate PRs):
- Promote Keyv to "embedded default" driver (spec §10)
- Pre-existing e2e failure at `tests/e2e/quota-store.e2e.ts:117`
  (poolUsageWithDimensions shape mismatch — fixed in PR #505)

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): log 3 crypto-relevant silent catches (rebased onto canonical base) (#509)

This branch has been surgically rebased onto origin/agent/migration-version-collision-fix
to remove accidental contamination from PR #507's branch base.

In all three cases, the empty-string return collapses HMAC/HMAC-SHA256
inputs into a constant-key value, so security-relevant operations on the
fallback path produce identical tokens regardless of input.

Fixes:
1. src/lib/machineToken.ts:44 - getMachineTokenSync catch:
   log.error + return ""
2. src/lib/machineToken.ts:62 - getLegacyCliTokenSync catch:
   log.error + return ""
3. src/lib/db/encryption.ts:87-95 - getLegacyDynamicKey catch:
   added pino logger (createLogger("db:encryption")) + log.error
   instead of returning null silently
4. (incidental) src/lib/machineToken.ts module-load catch: also
   gains log.error so the new runtime catches have a 'log' constant
   to reference. This subsumes PR #507's machineToken.ts hunk.

NOTE: PR #507 will need its machineToken.ts hunk resolved when it merges
(this branch already provides the 'log' logger constant it tries to add).

Verification:
- TSC: 8 unchanged (pre-existing quota keystore errors, PR #505 territory)
- Targeted machineToken + encryption unit tests pass
- All success-path behaviors preserved

Co-authored-by: KooshaPari <koosha@example.com>

* fix(security): 4 audit findings + migrate encryption.ts to pino (#510)

Completes the audit-driven governance work that PR #509 started. Eight
independent fixes:

Security fixes (audit findings F5, F6, F9, F10):

1. src/lib/cloudSync.ts:47-56 — HMAC verification fail-open when
   CLOUD_SYNC_SECRET is unset. Now returns false (fail-closed) when a
   sigHeader is present but cannot be verified, instead of accepting
   any response. Legacy unverified mode (no sigHeader) is preserved.

2. src/lib/oauth/providers/antigravity.ts:101 — fetch() of userInfo
   during OAuth was swallowed silently, causing downstream code to use
   default projectId/tierId. Now logs warn with structured context.

3. open-sse/services/autoRefreshDaemon.ts:77,80 — periodic credential
   refresh errors were silently swallowed, allowing expired tokens to
   persist undetected. Now logs error per cycle.

4. src/lib/vscode/serviceTierVariants.ts:184 — request body parse
   failures during tier-rewrite were silently no-op'd. Now logs warn.

Refactor:

5. src/lib/db/encryption.ts — migrated ~7 console.* calls to pino
   logger (encryptionLog). Following the PR #509 pattern of
   createLogger("db:encryption"). The catch fallback behavior is
   preserved exactly; only logging changes.

Out of scope:
- encryption.ts:144-148 plaintext fallback (separate spec at
  plans/encryption-failclosed-spec.md, deferred for design discussion)
- src/lib/db/*.ts console.* migration for other files (separate PR)

Co-authored-by: KooshaPari <koosha@example.com>

* fix(governance): log empty catches in binaryManager + db/adapters (#512)

22 silent fail-open sites in the version manager + SQLite adapter layer
were just swallows. Per the audit:

- src/lib/versionManager/binaryManager.ts (6 catches): symlink/rollback/
  remove errors were silent; filesystem permission bugs were invisible
- src/lib/db/adapters/sqljsAdapter.ts (6 catches): save/close errors
  were silent; DB corruption during shutdown was undetectable
- src/lib/db/adapters/betterSqliteAdapter.ts (1 catch): same pattern
- src/lib/db/adapters/nodeSqliteAdapter.ts (2 catches): same pattern
- src/lib/db/adapters/nodeSqliteShared.ts (7 catches): same pattern

All 22 catches now log structured error context. Behavior unchanged -
only logging added. Per AGENTS.md, no encryption keys or raw secrets
are logged.

TSC: 8 unchanged
Tests: existing pass (any new behavior is logging-only)

Co-authored-by: KooshaPari <koosha@example.com>

* ci: pin cross-platform Rust toolchain

* docs(plans): track 2 governance specs for future implementation (#511)

Two deferred-implementation specs are now tracked in version control so
the work product is preserved and discoverable.

1. plans/encryption-failclosed-spec.md (883 lines) — Hardening
   encryption.ts:144-148 (the silent plaintext fallback). Compares 3
   design options with detailed tradeoffs. Recommended: C+A
   (startup canary + runtime throw). Registered as AgilePlus
   feature 'encryption-failclosed'.

2. plans/keyv-as-embedded-default-spec.md (698 lines) — Promote Keyv
   from optional driver to embedded default for fresh installs.
   Includes backwards-compat plan, config migration, and rollout
   sequence. Registered as AgilePlus feature 'keyv-as-embedded-default'.

These specs intentionally do NOT include code changes — they are
design-only and gate on answering the open questions before
implementation. See spec section 9 for each spec's open questions.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* wip: auto-commit daemon 2026-08-06T09:18:52Z (#505)

Co-authored-by: Airlock Bot <airlock@phenoforge.local>

* refactor(db): migrate console.* to pino in src/lib/db/ (#522)

Follows the PR #506/#507/#509/#510 pattern of replacing console.*
with createLogger("db:<subsystem>") to provide structured logging
across the SQLite persistence layer.

Files modified (~155 callsites across 17 files):
- src/lib/db/adapters/driverFactory.ts
- src/lib/db/adapters/sqljsAdapter.ts
- src/lib/db/apiKeys.ts
- src/lib/db/backup.ts
- src/lib/db/cleanup.ts (~30 sites)
- src/lib/db/core.ts (~30 sites)
- src/lib/db/migrationRunner.ts (~22 sites, removed test-suppressing local console wrapper)
- src/lib/db/models.ts
- src/lib/db/optimizationSettings.ts
- src/lib/db/providers.ts
- src/lib/db/quotaPools.ts
- src/lib/db/quotaSnapshots.ts
- src/lib/db/schemaColumns.ts (~35 sites)
- src/lib/db/sessionAccountAffinity.ts
- src/lib/db/settings.ts
- src/lib/db/settings/cacheMetrics.ts
- src/lib/db/stateReset.ts

Per AGENTS.md guidance, never log SQLite encryption keys or raw secrets;
all logger calls redact sensitive material.

Behavior preservation:
- All success-path behavior unchanged
- Only the logging mechanism changes
- Targeted tests pass

One console.error preserved in core.ts:migrateFromJson because
tests/unit/db-core-migration.test.ts overrides console.error to detect
the failure; both console.error and log.error are emitted so the test
passes and pino is the canonical sink.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: use cache-pinned Trunk action

* fix(governance): log empty catches + weak peer-dep fallbacks (audit followup)

Continues the audit-driven governance cleanup that PRs #506, #507, #509,
#510, #512, #522, #525 started. This batch addresses the remaining empty
catches and weak peer-dependency fallbacks in plugins, embeddings,
oauth, monitoring, and combo paths.

Empty catches fixed (~6 sites):
- src/lib/plugins/loader.ts:200, 301 (plugin load/cleanup)
- src/lib/plugins/manager.ts:151, 270 (plugin staging cleanup)
- src/lib/embeddings/service.ts:50, 73 (embedding combo lookup)
- src/lib/credentialHealth/scheduler.ts:122 (credential health sweep)
- src/lib/usage/usageStats.ts:301 (usage stats)
- src/lib/oauth/providers/kimi-coding.ts:44 (OAuth provider init)

Weak peer-dep fallbacks fixed (~10 sites):
- src/lib/a2a/skills/providerDiscovery.ts:388 (MCP module load)
- open-sse/rpc/dispatchEdges.ts:189, 199, 209 (FFI/UDS transport —
  HIGH RISK, used log.error not warn)
- src/lib/monitoring/providerHealthAutopilot.ts:262 (quota monitor)
- open-sse/services/combo.ts:2222 (fetchCodexQuota)
- open-sse/services/combo/quotaStrategies.ts:431 (getRuntimeProviderProfile)
- open-sse/handlers/chatCore/codexFailover.ts:19 (provider connection)
- open-sse/handlers/chatCore/comboContextCache.ts:57 (proxy config)

All conversions preserve existing behavior (return null/false/etc.).
Only logging is added — no semantic change. Test results unchanged
from baseline.

TSC: 8 unchanged (pre-existing quota keystore errors, PR #505 territory).

---------

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Airlock Bot <airlock@phenoforge.local>
* chore(governance): rebase mergify config request-review fixes onto main

* fix(desktop): target fork-owned Electron releases

* ci: align workflows with selected action policy

* fix(governance): replace silent try/catch with explicit log.error in 5 modules (#506)

Builds on PR #505 (quota keystore type-drift fix). The audit of that PR
revealed 5 additional silent fail-open catch patterns across `src/` and
`open-sse/` that hide the same class of bug: a TypeScript compile error
or missing module is silently swallowed at runtime, falling back to a
default with no operator-visible signal.

This commit replaces those silent catches with explicit `log.error` calls
that surface the actual error to monitoring. Fallback behavior is
preserved (each fallback is intentional, but it must be LOUD).

Changes:

1. `src/lib/quota/storeFactory.ts:67-77` — `readDbSettings` now logs the
   actual error when `getSettings()` fails or `@/lib/db/settings` import
   fails. Same root cause as the previously-fixed Keyv/Redis catches.

2. `src/lib/quota/storeFactory.ts:138-147` — Redis driver catch upgraded
   from `log.warn` to `log.error`. Includes the configured Redis URL
   with the password segment redacted (`:***@`).

3. `src/lib/resilience/anomalyHook.ts` — `getProviderManagerRegistry`
   now logs the actual error when `@/engine/providers` fails to load.
   Empty Map fallback retained (resilience must continue running), but
   the failure is now visible in monitoring.

4. `open-sse/services/tierResolver.ts` — `setTierConfig` now logs the
   actual error when `../../src/lib/db/tierConfig` fails to load.
   `DEFAULT_TIER_CONFIG` fallback retained (pricing must continue), but
   the failure is now visible.

5. `open-sse/config/credentialLoader.ts` — `resolveCredentialsPath` now
   uses `log.error` (pino) instead of `console.warn`. Includes both the
   original error and the fallback path. Security-sensitive path; must
   keep working, but the failure must be loud.

6. `.gitignore` — exclude `.agileplus/` and `agileplus-*.db*` (local
   AgilePlus DB state, regenerated from `.md` specs via `agileplus
   specify`). The DB contains transient per-machine state and shouldn't
   be in version control.

Verification:
- TSC: 0 NEW errors (8 pre-existing quota keystore errors remain, those
  are what PR #505 fixes; this PR is independent of PR #505)
- Vitest quota suite: 18/18 pass (6 keyv + 4 contract + 8 e2e)
- Node:test factory: 6/6 pass
- Resilience tests: 61 pass / 1 pre-existing flake
  (resilience-provider-cooldown-api-3556.test.ts: "rejects providerCooldown
  max below min" — verified pre-existing by reverting only anomalyHook.ts
  and reproducing the same failure)

Out of scope (filed as separate bugs):
- `src/lib/resilience/anomalyHook.ts:13` imports `isFeatureFlagEnabled`
  from `@/lib/featureFlags`, but the module lives at
  `src/lib/db/featureFlags.ts`. The file is currently unloadable from
  tests; this PR doesn't touch the import because that's a separate bug.
- The Resilience subagent identified but did not fix the
  `tsconfig.typecheck-core.json` doesn't include `src/lib/resilience/`
  files — separate follow-up.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(governance): broken featureFlags imports + 3 silent fail-opens + extras test (#507)

Continuation of the governance-debt cleanup started in PRs #505 and #506.
Six independent fixes, each addressing a class of silent-failure pattern
that the audits surfaced.

Changes:

1. **`src/lib/resilience/anomalyHook.ts:12`** — Broken import path.
   `isFeatureFlagEnabled` was imported from `@/lib/featureFlags`, but
   the module lives at `@/shared/utils/featureFlags`. This bug was
   masked because `tsconfig.typecheck-core.json` does not include
   `src/lib/resilience/` and no test loads the module successfully.
   After the fix, the module loads and exports the expected surface.

2. **`src/server-init.ts:128`** — Same broken import path. The
   surrounding try/catch at lines 130-139 silently swallowed the
   import failure. Fix: correct the path AND upgrade the catch log
   from `warn` to `error` (matches PR #506 pattern).

3. **`src/lib/versionManager/processManager.ts:155`** — `getProcessInfo`
   catch returned `{pid, alive: true}` after `ps`/readFile failures,
   which lies when the process is actually gone. Fix: catch now
   logs the error and returns `{pid, alive: false}` (honest about
   not being able to read process state).

4. **`src/server/ws/liveServer.ts:479`** — `loadAuthModule().catch(() => {})`
   silently swallowed initial auth module load failures, allowing the
   WS server to come up without auth configured. Fix: catch now logs
   `log.error`; fallback behavior preserved per the existing comment
   ("handler retries the import lazily").

5. **`src/lib/machineToken.ts:1-10`** — Crypto-relevant: empty catch
   around `require("node-machine-id")` silently fell back to
   `() => ""`, which collapses HMAC inputs to a constant. Fix: catch
   now logs `log.error` with security-context message, gated by a
   `fallbackLogged` flag so the log fires only once per process
   (avoiding log spam from any downstream reload).

6. **`tests/unit/quota/keyvQuotaStoreExtras.test.ts`** (new) — Closes
   spec §8.2 reachability test gap. 5 sanity tests for the
   `recordPlanUsage` / `upsertProviderPlan` / `listProviderPlans` /
   `setPools` / `getPool` surface on KeyvQuotaStore. Note: this
   branch is based on `origin/agent/migration-version-collision-fix`
   (pre-PR-#505), so the methods live directly on KeyvQuotaStore.
   When PR #505 lands, update the test to import from
   `keyvQuotaStoreExtras.ts` instead.

Verification:
- TSC error count: 8 unchanged (all pre-existing quota keystore
  errors that PR #505 fixes; this PR is independent of #505)
- Vitest extras test: 5/5 pass
- Node:test combined (processManager + machineToken + WS): 47/47 pass
- All success-path behavior preserved; only catch/fallback paths now log

Out of scope (separate PRs):
- Promote Keyv to "embedded default" driver (spec §10)
- Pre-existing e2e failure at `tests/e2e/quota-store.e2e.ts:117`
  (poolUsageWithDimensions shape mismatch — fixed in PR #505)

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): log 3 crypto-relevant silent catches (rebased onto canonical base) (#509)

This branch has been surgically rebased onto origin/agent/migration-version-collision-fix
to remove accidental contamination from PR #507's branch base.

In all three cases, the empty-string return collapses HMAC/HMAC-SHA256
inputs into a constant-key value, so security-relevant operations on the
fallback path produce identical tokens regardless of input.

Fixes:
1. src/lib/machineToken.ts:44 - getMachineTokenSync catch:
   log.error + return ""
2. src/lib/machineToken.ts:62 - getLegacyCliTokenSync catch:
   log.error + return ""
3. src/lib/db/encryption.ts:87-95 - getLegacyDynamicKey catch:
   added pino logger (createLogger("db:encryption")) + log.error
   instead of returning null silently
4. (incidental) src/lib/machineToken.ts module-load catch: also
   gains log.error so the new runtime catches have a 'log' constant
   to reference. This subsumes PR #507's machineToken.ts hunk.

NOTE: PR #507 will need its machineToken.ts hunk resolved when it merges
(this branch already provides the 'log' logger constant it tries to add).

Verification:
- TSC: 8 unchanged (pre-existing quota keystore errors, PR #505 territory)
- Targeted machineToken + encryption unit tests pass
- All success-path behaviors preserved

Co-authored-by: KooshaPari <koosha@example.com>

* fix(security): 4 audit findings + migrate encryption.ts to pino (#510)

Completes the audit-driven governance work that PR #509 started. Eight
independent fixes:

Security fixes (audit findings F5, F6, F9, F10):

1. src/lib/cloudSync.ts:47-56 — HMAC verification fail-open when
   CLOUD_SYNC_SECRET is unset. Now returns false (fail-closed) when a
   sigHeader is present but cannot be verified, instead of accepting
   any response. Legacy unverified mode (no sigHeader) is preserved.

2. src/lib/oauth/providers/antigravity.ts:101 — fetch() of userInfo
   during OAuth was swallowed silently, causing downstream code to use
   default projectId/tierId. Now logs warn with structured context.

3. open-sse/services/autoRefreshDaemon.ts:77,80 — periodic credential
   refresh errors were silently swallowed, allowing expired tokens to
   persist undetected. Now logs error per cycle.

4. src/lib/vscode/serviceTierVariants.ts:184 — request body parse
   failures during tier-rewrite were silently no-op'd. Now logs warn.

Refactor:

5. src/lib/db/encryption.ts — migrated ~7 console.* calls to pino
   logger (encryptionLog). Following the PR #509 pattern of
   createLogger("db:encryption"). The catch fallback behavior is
   preserved exactly; only logging changes.

Out of scope:
- encryption.ts:144-148 plaintext fallback (separate spec at
  plans/encryption-failclosed-spec.md, deferred for design discussion)
- src/lib/db/*.ts console.* migration for other files (separate PR)

Co-authored-by: KooshaPari <koosha@example.com>

* fix(governance): log empty catches in binaryManager + db/adapters (#512)

22 silent fail-open sites in the version manager + SQLite adapter layer
were just swallows. Per the audit:

- src/lib/versionManager/binaryManager.ts (6 catches): symlink/rollback/
  remove errors were silent; filesystem permission bugs were invisible
- src/lib/db/adapters/sqljsAdapter.ts (6 catches): save/close errors
  were silent; DB corruption during shutdown was undetectable
- src/lib/db/adapters/betterSqliteAdapter.ts (1 catch): same pattern
- src/lib/db/adapters/nodeSqliteAdapter.ts (2 catches): same pattern
- src/lib/db/adapters/nodeSqliteShared.ts (7 catches): same pattern

All 22 catches now log structured error context. Behavior unchanged -
only logging added. Per AGENTS.md, no encryption keys or raw secrets
are logged.

TSC: 8 unchanged
Tests: existing pass (any new behavior is logging-only)

Co-authored-by: KooshaPari <koosha@example.com>

* ci: pin cross-platform Rust toolchain

* docs(plans): track 2 governance specs for future implementation (#511)

Two deferred-implementation specs are now tracked in version control so
the work product is preserved and discoverable.

1. plans/encryption-failclosed-spec.md (883 lines) — Hardening
   encryption.ts:144-148 (the silent plaintext fallback). Compares 3
   design options with detailed tradeoffs. Recommended: C+A
   (startup canary + runtime throw). Registered as AgilePlus
   feature 'encryption-failclosed'.

2. plans/keyv-as-embedded-default-spec.md (698 lines) — Promote Keyv
   from optional driver to embedded default for fresh installs.
   Includes backwards-compat plan, config migration, and rollout
   sequence. Registered as AgilePlus feature 'keyv-as-embedded-default'.

These specs intentionally do NOT include code changes — they are
design-only and gate on answering the open questions before
implementation. See spec section 9 for each spec's open questions.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* wip: auto-commit daemon 2026-08-06T09:18:52Z (#505)

Co-authored-by: Airlock Bot <airlock@phenoforge.local>

* refactor(db): migrate console.* to pino in src/lib/db/ (#522)

Follows the PR #506/#507/#509/#510 pattern of replacing console.*
with createLogger("db:<subsystem>") to provide structured logging
across the SQLite persistence layer.

Files modified (~155 callsites across 17 files):
- src/lib/db/adapters/driverFactory.ts
- src/lib/db/adapters/sqljsAdapter.ts
- src/lib/db/apiKeys.ts
- src/lib/db/backup.ts
- src/lib/db/cleanup.ts (~30 sites)
- src/lib/db/core.ts (~30 sites)
- src/lib/db/migrationRunner.ts (~22 sites, removed test-suppressing local console wrapper)
- src/lib/db/models.ts
- src/lib/db/optimizationSettings.ts
- src/lib/db/providers.ts
- src/lib/db/quotaPools.ts
- src/lib/db/quotaSnapshots.ts
- src/lib/db/schemaColumns.ts (~35 sites)
- src/lib/db/sessionAccountAffinity.ts
- src/lib/db/settings.ts
- src/lib/db/settings/cacheMetrics.ts
- src/lib/db/stateReset.ts

Per AGENTS.md guidance, never log SQLite encryption keys or raw secrets;
all logger calls redact sensitive material.

Behavior preservation:
- All success-path behavior unchanged
- Only the logging mechanism changes
- Targeted tests pass

One console.error preserved in core.ts:migrateFromJson because
tests/unit/db-core-migration.test.ts overrides console.error to detect
the failure; both console.error and log.error are emitted so the test
passes and pino is the canonical sink.

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: use cache-pinned Trunk action

* refactor: migrate console.* to pino in remaining src/ + open-sse/

Follows the PR #506/#507/#509/#510/#512/#518/#521/#522 pattern of replacing
console.* with createLogger("domain:subsystem") to provide structured
logging across the OmniRoute codebase.

PR #522 already migrated src/lib/db/*.ts (~155 callsites). This PR
migrates ~80 additional callsites across 37 files in:

- src/lib/resilience/* (normalize.ts, anomalyHook.ts)
- src/lib/oauth/* (connectionPersistence.ts)
- src/lib/vscode/* (tokenizedRequest.ts, dual-emit for test capture)
- src/lib/services/* (ringBuffer.ts, bootstrap.ts, embedWsProxy.ts, modelSync.ts)
- src/lib/sseTextTransform.ts, src/lib/dataPaths.ts, src/lib/initCloudSync.ts
- src/lib/events/eventBus.ts, src/lib/jobs/{budgetResetJob,reasoningCacheCleanupJob}.ts
- src/lib/cloudSync.ts, src/lib/localHealthCheck.ts
- src/lib/arenaEloSync.ts, src/lib/pricingSync.ts, src/lib/modelsDevSync.ts
- src/lib/tokenHealthCheck.ts, src/lib/gracefulShutdown.ts
- src/lib/apiBridgeServer.ts, src/lib/proxyLogger.ts
- src/lib/middleware/registry.ts, src/lib/quota/connectionRecovery.ts
- src/lib/credentialHealth/scheduler.ts
- src/middleware/promptInjectionGuard.ts
- src/server/ws/liveServer.ts (preserves [LiveWS] startup banner via log.info)
- src/sse/services/auth.ts, src/sse/services/streamState.ts
- open-sse/config/{constants,credentialLoader}.ts
- open-sse/services/{autoRefreshDaemon,quotaMonitor}.ts
- open-sse/mcp-server/audit.ts
- open-sse/utils/proxyFetch.ts
- open-sse/handlers/chatCore.ts (account fallback warnings)

User-facing startup banners and intentional CLI output are preserved:
- src/server/ws/liveServer.ts '[LiveWS] Dashboard WebSocket server listening'
  (now via log.info with structured host/port)
- src/lib/vscode/tokenizedRequest.ts '[VSCODE][SECURITY]' warning kept
  as console.warn alongside log.warn so tests/unit/vscode-token-in-url-warning.test.ts
  (which captures console.warn to verify once-per-process dedup) keeps passing —
  same pattern as PR #522's preservation in db/core.ts:migrateFromJson
- src/lib/oauth/utils/ui.ts (CLI formatting with picocolors) preserved as console
- src/mitm/* (CLI-driven tooling) preserved
- Next.js dashboard React components preserved (browser console, not server-side)

Behavior preservation:
- All success-path behavior unchanged
- Only the logging mechanism changes
- TSC baseline (typecheck-core.json) remains 0 errors
- Targeted tests pass: resilience-settings-normalize-split (9/9),
  resilience-settings-stream-recovery (9/9), resilience-settings-provider-breaker (9/9),
  oauth-refresh-error-resilience (12/12), vscode-tokenized-request (3/3),
  vscode-token-in-url-warning (4/4), services/embedWsProxy (30/30)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: KooshaPari <koosha@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Airlock Bot <airlock@phenoforge.local>
* fix: use Node 24-compatible Keyv SQLite adapter

* fix: migrate Trunk config to schema 0.1

* fix: use valid Trunk plugin source

* fix: declare Trunk command success codes

* fix: declare Trunk command output formats

* fix: accept Trunk pass-fail findings

* fix: keep Trunk checks available in CI

---------

Co-authored-by: KooshaPari <koosha@example.com>
Koosha Pari and others added 21 commits September 12, 2026 22:52
diegosouzapw#13124)

Adds new resourcePressurePolicy.ts with full pressure monitoring and
the OMNIROUTE_PRESSURE_PSI_DISABLED env var to disable PSI-based pressure
detection when running in constrained environments.
OMNIROUTE_MAX_TOOLS_LIMIT env var overrides the default 128 tool limit.
Also adds bypassDefaultToolLimit parameter to truncateToolList and
per-provider known limits via getKnownToolLimit.
…iegosouzapw#13470)

When a connection has a proxy assignment but the pool is dead, the refresh
now checks PROXY_FAIL_OPEN before silently egressing on the host IP.
If PROXY_FAIL_OPEN is not set, throws PROXY_ASSIGNED_UNAVAILABLE.
…uzapw#13504)

Gives slow upstream providers more time before the queue rejects the
request. Override via RATE_LIMIT_MAX_WAIT_MS env var.
Backport upstream ca23eed. Adds null-based memoization to
getModelsDevPricing() with cache invalidation on save/clear and
db state resets.
Adapted from upstream DB schema convergence commit (0ce2123).
- check-install-upgrade.mjs: publish-time gate verifying migration parity
- allowlist.json: migration reference mapping (upstream 163_ -> fork 169_)
- Two test suites for convergence and schema parity validation
Comprehensive analysis of Next.js → SvelteKit migration:
- 116 pages to migrate (~7,200 LOC)
- Architecture shift from React SPA to BFF pattern
- Component translation guide (React → Svelte 5 runes)
- 7 batches with effort estimates (23-31 days solo, 8-11 days with 3 devs)
- Risk areas identified: combo builder (4691 LOC), context system, provider mgmt
…days

The on-disk model catalog cache had no maximum age. When /v1/models was
unreachable, the plugin would serve a week-old (or older) catalog
silently, causing stale model IDs and missing models.

Now defaultDiskSnapshotReader rejects snapshots with writtenAt older
than DISK_CACHE_MAX_AGE_MS (default 7 days). Overridable via env var.

Fixes diegosouzapw#13390
applyStoredDatabaseOptimizationSettings() always passed
applyPersistent: false, so the configured auto_vacuum mode was never
applied at startup — the live database stayed at NONE even when
INCREMENTAL was configured. Now respects the optimizeOnStartup flag.

Fixes diegosouzapw#13432
getCompressionSettings() silently skipped non-string (BLOB) and
invalid-JSON settings rows, making it impossible to diagnose config
drift between the panel and the runtime.

Now logs a warn-level message for each unreadable row, including the
key name and a remediation hint (re-save from the Storage panel).

Also warns when the 'engines' row exists but yields no valid toggles,
so operators know their panel-configured engines map is being silently
replaced by the legacy fallback.

Fixes diegosouzapw#13456
…iegosouzapw#13457)

cavemanConfig.preservePatterns was silently skipped when a user region
contained built-in preserved constructs (inline code, URLs, headings,
CONST_CASE, etc.). The root cause: built-in patterns ran first and
replaced inline constructs within the user region with sentinel
placeholders. When the user pattern then tried to match the region,
the sentinel was present in the match and replacePattern silently
returned it unchanged.

The fix: user patterns now run BEFORE built-in patterns, so user
regions are captured as whole sentinels before built-in patterns
can fragment them.

5 tests in tests/unit/compression/preserve-patterns-order.test.ts.

Fixes diegosouzapw#13457
Create 11 SvelteKit pages for the migration:
- cost/budget, cost/pricing, cost/quota-share
- discovery, leaderboard, free-provider-rankings
- free-tiers, limits, quota, translator, changelog

Each page uses Svelte 5 runes, bffApiUrl() for data fetching,
and follows the thin-shell pattern (40-120 LOC).
11 new BFF route files for SvelteKit migration:
- costs, discovery, leaderboard, free-provider-rankings
- free-tiers, limits, quota, translator, changelog, chaos, relay

Each provides mock/placeholder data matching the shape expected by
SvelteKit pages. Routes registered in index.ts.
…onstraint (diegosouzapw#13481)

When a combo fails over, each attempt has a unique traceId but shares
the same pendingRequestId. Using pendingRequestId as the call_logs.id
caused a UNIQUE constraint violation on the second attempt, silently
dropping the winning (successful) attempt's log row.

Changed saveCallLog to use traceId as the row id. traceId is unique
per attempt, emitted in request.started, and pairs with the lifecycle
events. pendingRequestId continues to be available for correlation.

Updated existing tests to default traceId from pendingRequestId. Added
test verifying two combo attempts with different traceIds are both
persisted without collision.

Fixes diegosouzapw#13481
- 74 total vulns (35 prod, 39 dev-only)
- 2 critical (next, mermaid), fixable via direct upgrades
- 56 fork-only CI workflows, 23 shared with upstream
- Fork infrastructure investment is intentional
…onstraint (diegosouzapw#13481)

When a combo fails over, each attempt has a unique traceId but shares
the same pendingRequestId. Using pendingRequestId as the call_logs.id
caused a UNIQUE constraint violation on the second attempt, silently
dropping the winning (successful) attempt's log row.

Changed saveCallLog to use traceId as the row id. traceId is unique
per attempt, emitted in request.started, and pairs with the lifecycle
events. pendingRequestId continues to be available for correlation.

Added test verifying two combo attempts with different traceIds are
both persisted without collision.

Fixes diegosouzapw#13481
…gosouzapw#13483)

No-auth providers (opencode, duckduckgo-web, etc.) returned synthetic
'noauth' credentials early in getProviderCredentials, bypassing the
model lockout check. A model_capacity lockout was recorded but never
enforced — every request retried the same locked model, paying a wasted
upstream round-trip (~2s) before failing over.

Added isModelLocked check for the synthetic noauth connection before
returning credentials. When a model is lockout-blocked, the function
returns null, allowing the combo engine to skip to the next member
without contacting the upstream.

4 tests in tests/unit/noauth-model-lockout.test.ts.

Fixes diegosouzapw#13483
Copilot AI lite review requested due to automatic review settings September 13, 2026 06:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@mergify

mergify Bot commented Sep 13, 2026

Copy link
Copy Markdown

Merge Protections

🔴 1 of 2 protections blocking · waiting on 🙋 you

Protection Waiting on
🔴 🚦 Auto-queue 🙋 you
🟢 📃 Configuration Change Requirements —

🔴 🚦 Auto-queue

Waiting for

  • label = queue
This rule is failing.

When all merge protections are satisfied and these conditions match, this pull request will be queued automatically.

  • label = queue

Show 1 satisfied protection

🟢 📃 Configuration Change Requirements

Mergify configuration change

  • any of:
    • check-success = @mergify/Configuration changed
    • check-success = @mergify/Configuration has been deleted

@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks! This branch carries ~2.6k unrelated commits, so it can't merge cleanly. #13547 holds the same #13483 fix at the current code location, and your tests/unit/noauth-model-lockout.test.ts was carried into #13547, which is being merged with your authorship. Closing as consolidated into #13547.

diegosouzapw added a commit to KooshaPari/OmniRoute that referenced this pull request Sep 15, 2026
diegosouzapw pushed a commit that referenced this pull request Sep 15, 2026
…ection (#13547)

No-auth providers now honor a recorded model-only lockout before `getProviderCredentials` hands back the synthetic `noauth` connection (#13483). That early return skipped the per-connection status pass, so a `model_capacity` lockout was recorded but never enforced, and every request re-tried the locked model for a wasted upstream round-trip before failing over.

Maintainer additions: carried your `tests/unit/noauth-model-lockout.test.ts` from #13527. 3 of its 4 cases fail on the release tip without the fix and pass with it. Rebaselined `src/sse/services/auth.ts` 3542→3556 in `file-size-baseline.json` with a dated annotation.

Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green.

Thanks @KooshaPari!
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ection (diegosouzapw#13547)

No-auth providers now honor a recorded model-only lockout before `getProviderCredentials` hands back the synthetic `noauth` connection (diegosouzapw#13483). That early return skipped the per-connection status pass, so a `model_capacity` lockout was recorded but never enforced, and every request re-tried the locked model for a wasted upstream round-trip before failing over.

Maintainer additions: carried your `tests/unit/noauth-model-lockout.test.ts` from diegosouzapw#13527. 3 of its 4 cases fail on the release tip without the fix and pass with it. Rebaselined `src/sse/services/auth.ts` 3542→3556 in `file-size-baseline.json` with a dated annotation.

Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green.

Thanks @KooshaPari!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(auth): Model-only lockout for no-auth providers is recorded but never enforced — locked member is retried on every request

4 participants