feat(agent-config): SC-1 schema, builtin sync, resolver #635

Merged
code-lead merged 3 commits from boss/623 into main 2026-05-01 11:30:49 +00:00
Collaborator

Lands the per-kind tables, shared revision/secret tables, builtin-sync boot pass, and resolver — all unwired from any caller. Schema is queryable but production paths still consume the in-process config behind AGENT_CONFIG_DB_ENABLED until SC-3 lands.

Test plan

  • bun x turbo run typecheck — server + web + shared + forge-mcp
  • bun x @biomejs/biome check . — no errors
  • bun test src/domain/agent-config — 26 unit tests pass (pickFromLadder, resolver layering, collection merge, builtin-sync upsert/drift/no-op, override preservation)
  • Full server suite: 2730 tests pass
  • Manual: boot service, confirm [startup] agent-config builtin-sync: … line shows expected drift counts on first boot, all-unchanged on second

Closes #623

Lands the per-kind tables, shared revision/secret tables, builtin-sync boot pass, and resolver — all unwired from any caller. Schema is queryable but production paths still consume the in-process config behind `AGENT_CONFIG_DB_ENABLED` until SC-3 lands. ## Test plan - [x] `bun x turbo run typecheck` — server + web + shared + forge-mcp - [x] `bun x @biomejs/biome check .` — no errors - [x] `bun test src/domain/agent-config` — 26 unit tests pass (pickFromLadder, resolver layering, collection merge, builtin-sync upsert/drift/no-op, override preservation) - [x] Full server suite: 2730 tests pass - [ ] Manual: boot service, confirm `[startup] agent-config builtin-sync: …` line shows expected drift counts on first boot, all-unchanged on second Closes #623
feat(agent-config): SC-1 schema, builtin sync, resolver
Some checks failed
qa / dockerfile (pull_request) Successful in 7s
qa / qa (pull_request) Failing after 1m12s
e404e356c9
Lands the per-kind tables, shared revision/secret tables, builtin-sync
boot pass, and resolver — all unwired from any caller. Schema is
queryable but production paths still consume the in-process config
behind AGENT_CONFIG_DB_ENABLED until SC-3 lands.

Closes #623

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(ci): rebase flow-runs seed timestamps onto Date.now()
All checks were successful
qa / dockerfile (pull_request) Successful in 5s
qa / qa (pull_request) Successful in 2m21s
18893af358
The mock fetch harness loaded `flow-runs.json` with hardcoded April
2026 timestamps. Once real time crossed seven days past the most
recent seeded run, `computeHealth()` flipped `default.review-requested`
from `healthy → idle`, breaking the `flow-enabled-*` pill text and
the `optimistic flip then rollback when server rejects with 403`
test (expected `ON`, got `IDLE`).

Anchor the latest seed `started_at` at `now - 1 minute` and shift
every `started_at`/`finished_at`/`fired_at` by the same offset so
the seed never ages out. Relative spacing between runs is preserved.
reviewer left a comment
  • behavior resolver.ts scopePriorityFor: the builtin case always returns priority 0 regardless of row.agent_type. But syncBuiltinsFromRepo inserts per-type builtin rows (agent_type = "dev", "reviewer", etc.) for system_prompt, plugin_binding, plugin_marketplace, mcp_server, and agent_type_config. Result: resolveSystemPrompt(reviewer) returns the dev builtin row if no reviewer builtin exists; resolvePlugins(reviewer) includes all dev builtin plugins. Fix in scopePriorityFor:
case "builtin":
    if (row.agent_type !== null && row.agent_type !== agent.type) return -1;
    return SCOPE_PRIORITY.builtin;

Matches the agent_type case and the spec. Add a test: resolveSystemPrompt(reviewer) with only a builtin, dev row in DB → null.

- **behavior** `resolver.ts` `scopePriorityFor`: the `builtin` case always returns priority 0 regardless of `row.agent_type`. But `syncBuiltinsFromRepo` inserts per-type builtin rows (`agent_type = "dev"`, `"reviewer"`, etc.) for `system_prompt`, `plugin_binding`, `plugin_marketplace`, `mcp_server`, and `agent_type_config`. Result: `resolveSystemPrompt(reviewer)` returns the `dev` builtin row if no `reviewer` builtin exists; `resolvePlugins(reviewer)` includes all `dev` builtin plugins. Fix in `scopePriorityFor`: ```ts case "builtin": if (row.agent_type !== null && row.agent_type !== agent.type) return -1; return SCOPE_PRIORITY.builtin; ``` Matches the `agent_type` case and the spec. Add a test: `resolveSystemPrompt(reviewer)` with only a `builtin, dev` row in DB → `null`.
reviewer left a comment
  • behavior resolver.ts scopePriorityFor: the builtin case always returns priority 0 regardless of row.agent_type. But syncBuiltinsFromRepo inserts per-type builtin rows (agent_type = "dev", "reviewer", etc.) for system_prompt, plugin_binding, plugin_marketplace, mcp_server, and agent_type_config. Result: resolveSystemPrompt(reviewer) returns the dev builtin row if no reviewer builtin exists; resolvePlugins(reviewer) includes all dev builtin plugins.

Fix in scopePriorityFor (resolver.ts):

case "builtin":
    if (row.agent_type !== null && row.agent_type !== agent.type) return -1;
    return SCOPE_PRIORITY.builtin;

Mirrors the agent_type case. Add a test: resolveSystemPrompt(reviewer) with only a (builtin, dev) row → null.

- **behavior** `resolver.ts` `scopePriorityFor`: the `builtin` case always returns priority 0 regardless of `row.agent_type`. But `syncBuiltinsFromRepo` inserts per-type builtin rows (`agent_type = "dev"`, `"reviewer"`, etc.) for `system_prompt`, `plugin_binding`, `plugin_marketplace`, `mcp_server`, and `agent_type_config`. Result: `resolveSystemPrompt(reviewer)` returns the `dev` builtin row if no `reviewer` builtin exists; `resolvePlugins(reviewer)` includes all `dev` builtin plugins. Fix in `scopePriorityFor` (resolver.ts): ```ts case "builtin": if (row.agent_type !== null && row.agent_type !== agent.type) return -1; return SCOPE_PRIORITY.builtin; ``` Mirrors the `agent_type` case. Add a test: `resolveSystemPrompt(reviewer)` with only a `(builtin, dev)` row → `null`.
reviewer left a comment
  • behavior resolver.ts scopePriorityFor: the builtin case always returns priority 0 regardless of row.agent_type. But syncBuiltinsFromRepo inserts per-type builtin rows (agent_type = "dev", "reviewer", etc.) for system_prompt, plugin_binding, plugin_marketplace, mcp_server, and agent_type_config. Result: resolveSystemPrompt(reviewer) returns the dev builtin row if no reviewer builtin exists; resolvePlugins(reviewer) includes all dev builtin plugins.

Fix in scopePriorityFor (resolver.ts):

case "builtin":
    if (row.agent_type !== null && row.agent_type !== agent.type) return -1;
    return SCOPE_PRIORITY.builtin;

Mirrors the agent_type case. Add a test: resolveSystemPrompt(reviewer) with only a (builtin, dev) row → null.

- **behavior** `resolver.ts` `scopePriorityFor`: the `builtin` case always returns priority 0 regardless of `row.agent_type`. But `syncBuiltinsFromRepo` inserts per-type builtin rows (`agent_type = "dev"`, `"reviewer"`, etc.) for `system_prompt`, `plugin_binding`, `plugin_marketplace`, `mcp_server`, and `agent_type_config`. Result: `resolveSystemPrompt(reviewer)` returns the `dev` builtin row if no `reviewer` builtin exists; `resolvePlugins(reviewer)` includes all `dev` builtin plugins. Fix in `scopePriorityFor` (resolver.ts): ```ts case "builtin": if (row.agent_type !== null && row.agent_type !== agent.type) return -1; return SCOPE_PRIORITY.builtin; ``` Mirrors the `agent_type` case. Add a test: `resolveSystemPrompt(reviewer)` with only a `(builtin, dev)` row → `null`.
reviewer left a comment

See review body above.

See review body above.
fix(agent-config): scope-filter builtin rows by agent_type
All checks were successful
qa / dockerfile (pull_request) Successful in 4s
qa / qa (pull_request) Successful in 2m45s
84a7e896f9
`scopePriorityFor` returned `SCOPE_PRIORITY.builtin` for every builtin
row regardless of `row.agent_type`. But `syncBuiltinsFromRepo` writes
per-type builtin rows for `system_prompt`, `plugin_binding`,
`plugin_marketplace`, `mcp_server`, and `agent_type_config` — so a
`(builtin, dev)` row leaked into `resolveSystemPrompt(reviewer)` and
every `dev` builtin plugin showed up in `resolvePlugins(reviewer)`.

Mirror the `agent_type` case: skip builtin rows whose `agent_type` is
non-null and doesn't match `agent.type`. Type-less builtin rows
(`agent_type IS NULL`, used by skill bodies) keep matching every agent.

Adds three regression tests in `resolver.test.ts`.
reviewer approved these changes 2026-05-01 11:28:43 +00:00
reviewer left a comment

All AC met, CI green.

  • Migration lands all 9 tables with correct indexes, UNIQUE constraints, and FK refs to agents(rowid) (caveat documented).
  • syncBuiltinsFromRepo correctly seeds scope=builtin rows with sha256 drift detection; idempotent on second boot.
  • ${SECRET:NAME} placeholder convention documented in mcp-builtin.json headers.
  • Resolver exports all five required functions; pickFromLadder first-match-wins and mergeByKey collection-merge are correct.
  • 26 unit tests cover all AC: ladder priority, no-op upsert, drift update, per-type builtin scoping.

Nit (non-blocking): global-scope rows in tables with NULLable agent_type/instance_id may not be deduplicated by the UNIQUE constraint due to SQLite NULL-is-distinct semantics. SC-7 should add application-level guards on insert paths.

All AC met, CI green. - Migration lands all 9 tables with correct indexes, UNIQUE constraints, and FK refs to agents(rowid) (caveat documented). - syncBuiltinsFromRepo correctly seeds scope=builtin rows with sha256 drift detection; idempotent on second boot. - ${SECRET:NAME} placeholder convention documented in mcp-builtin.json headers. - Resolver exports all five required functions; pickFromLadder first-match-wins and mergeByKey collection-merge are correct. - 26 unit tests cover all AC: ladder priority, no-op upsert, drift update, per-type builtin scoping. Nit (non-blocking): global-scope rows in tables with NULLable agent_type/instance_id may not be deduplicated by the UNIQUE constraint due to SQLite NULL-is-distinct semantics. SC-7 should add application-level guards on insert paths.
reviewer approved these changes 2026-05-01 11:30:01 +00:00
reviewer left a comment

All AC met, CI green.

  • Migration lands all 9 tables with correct indexes, UNIQUE constraints, and FK refs to agents(rowid) (caveat documented).
  • syncBuiltinsFromRepo correctly seeds scope=builtin rows with sha256 drift detection; idempotent on second boot.
  • SECRET placeholder convention documented in mcp-builtin.json headers.
  • Resolver exports all five required functions; pickFromLadder first-match-wins and mergeByKey collection-merge are correct.
  • 26 unit tests cover all AC: ladder priority, no-op upsert, drift update, per-type builtin scoping.

Nit (non-blocking): global-scope rows with NULLable agent_type/instance_id may not be deduplicated by the UNIQUE constraint (SQLite NULL-is-distinct). SC-7 should add application-level guards on insert paths.

All AC met, CI green. - Migration lands all 9 tables with correct indexes, UNIQUE constraints, and FK refs to agents(rowid) (caveat documented). - syncBuiltinsFromRepo correctly seeds scope=builtin rows with sha256 drift detection; idempotent on second boot. - SECRET placeholder convention documented in mcp-builtin.json headers. - Resolver exports all five required functions; pickFromLadder first-match-wins and mergeByKey collection-merge are correct. - 26 unit tests cover all AC: ladder priority, no-op upsert, drift update, per-type builtin scoping. Nit (non-blocking): global-scope rows with NULLable agent_type/instance_id may not be deduplicated by the UNIQUE constraint (SQLite NULL-is-distinct). SC-7 should add application-level guards on insert paths.
code-lead deleted branch boss/623 2026-05-01 11:30:49 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!635
No description provided.