Commit Graph

3 Commits

Author SHA1 Message Date
claude code agent 227
1cfad1f6fb fix(db): jsonb double-encoding follow-ups from PR #172 review (#173)
PR #172 fixed the jsonb double-encoding for `tool_allowlist` but the same
class of bug, and the same re-derived workaround, remained elsewhere.

1. model_config (agent roles): jsonbObject still used the buggy `::jsonb`
   bind, so `ai_agent_roles.model_config` round-tripped as a jsonb STRING
   SCALAR. The read-path `typeof === 'object'` check then failed and the
   model override was SILENTLY dropped (role fell back to the default model).
   Fixed to `::text::jsonb` and added `parseModelConfig` + `normalizeRow` so
   every read self-heals already-corrupted rows (no migration).

2. Centralized the write workaround as `jsonbBind()` in database/utils.ts —
   one implementation with one explanation of the quirk — replacing the
   per-repo `jsonbArray` (mcp) and `jsonbObject` (roles).

3. Integration coverage (the fix is a DB round-trip a unit test cannot see;
   the read-side parser MASKS a write regression): new
   ai-mcp-server-repo.int-spec asserts `jsonb_typeof(tool_allowlist)='array'`
   after insert + heals a seeded string-scalar row; ai-agent-roles-repo
   int-spec gains the same for `model_config` (`'object'` + heal).

4. Updated the stale `ai-mcp-servers.types.ts` comment (the driver returns a
   JSON string for legacy rows; the repo normalizes every read).

5. Fail-open logging: a corrupt tool_allowlist degrades to "no restriction"
   (agent gets ALL tools) — normalizeRow now warns (server id only, never
   contents) so the silent widening leaves a trace.

6. Simplified parseToolAllowlist (normalize the string once, then a single
   array-of-strings check) — identical behaviour, all 12 cases still pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:36:01 +03:00
claude_code
f3fa15e746 refactor(ai-chat): shared tool-spec registry for identical tools; formalize integration db factory
Implements two architecture follow-ups from the multi-aspect review.

1. Shared, zod-agnostic tool-spec registry (packages/mcp/src/tool-specs.ts)
   for the 14 AI tools whose name + schema + model-facing description are
   genuinely identical across the standalone MCP server and the in-app
   AI-SDK chat. Both layers consume it (registerShared in index.ts;
   sharedTool in ai-chat-tools.service.ts) and keep their own execute/auth.
   - Zod-agnostic builders (z) => ZodRawShape bridge the zod v3 (mcp) vs
     zod v4 (server) split; the registry imports no zod.
   - Folds in the documented edit_page_text drift-bug fix: the stale
     "strip-and-retry tolerated" claim is gone; canonical wording states a
     formatting-only change is refused into failed[].
   - Sibling-tool references in shared descriptions are transport-neutral so
     one description is correct for both snake_case (MCP) and camelCase
     (in-app) tool names.
   - Loader fail-fast guard for a stale @docmost/mcp build.
   - The ~17 intentionally-divergent tools (security guardrails, tuned UX)
     stay per-layer, untouched.
   - Rebuilt committed mcp artifacts (also regenerates a previously stale
     build/lib/docmost-schema.js to match its already-committed source).

2. Formalize apps/server/test/integration/db.ts as the canonical
   integration-test seed factory (module doc + a shortId helper); the
   hand-written minimal seeders are kept on purpose, decoupled from the
   app service-layer side effects.

Verified: server tsc + lint clean, mcp build clean; mcp unit tests 261 pass,
ai-chat-tools.service 16 pass, public-share-chat-tools 8 pass, ai-chat suite
224 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:57:00 +03:00
claude code agent 227
04f05626ad test(server): integration harness + deferred coverage vs real Postgres/Redis
Builds the deferred integration tests from docs/backlog/feature-test-coverage-
deferred.md that needed real infra (a test Postgres + real Redis) which the repo
lacked. Runs against an isolated, auto-created docmost_test database and Redis
logical DB 15 — never the dev data.

Harness (apps/server/test/integration/, run via new `pnpm --filter server test:int`
=> jest --config test/jest-integration.json; default unit `jest` is untouched and
excludes these via the *.int-spec.ts name + rootDir):
- db.ts: buildTestDb() mirrors database.module.ts exactly (PostgresJSDialect,
  CamelCasePlugin, bigint to:20/from:[20,1700] parsing) + minimal seed helpers.
- global-setup.ts: DROP/CREATE docmost_test, CREATE EXTENSION vector, migrate to
  latest via Kysely Migrator (fails loud on any errored migration).
- global-teardown.ts: closes the pool.

Coverage (5 suites, 16 tests, all green against live PG+Redis):
- WorkspaceRepo.updateSetting: jsonb-merge persists htmlEmbed without clobbering
  sibling ai/sharing namespaces (the kill-switch write half).
- AiAgentRoleRepo: soft-delete exclusion, cross-workspace tenant isolation,
  duplicate (name,workspace) -> 23505, name reusable after softDelete (partial
  unique index WHERE deleted_at IS NULL), same name across workspaces allowed.
- page_template_references: deleting either source or referenced page cascades
  the link row (onDelete cascade) — real FK, not mocked.
- PublicShareWorkspaceLimiter vs REAL Redis: real ioredis EVAL of the sliding-
  window Lua — max boundary (3 admit / 4th deny), re-admit after the window
  slides, same-ms distinct members. Catches Lua bugs a FakeRedis cannot.
- AiChatRepo.findByCreator: role-badge join (enabled->badge; soft-deleted or
  disabled role -> null).

Review: APPROVE; applied its two hardening suggestions (fail loud on errored
migration result even without a top-level error; TEST_REDIS_URL override + ping
preflight). tsc clean; unit run excludes int-spec (verified).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 07:02:55 +03:00