Add position column to pages (migration 020), update listBySpace to ORDER BY position, title,
expose position in update(), add to patchSchema, and replace the space view flat table with a
tree renderer grouping pages by parent_id under h4 section headers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Q3: prod void DB role NOSUPERUSER (vector marked trusted; deploy/README documents it)
- Q4: buildChildEnv allow-list for the claude subprocess (no OWNER_TOKEN/DATABASE_URL/secrets leak)
- Q5: pending-change approve claims-before-applying + reopens on failure (no re-approvable dup)
- Q6: /capture/upload validates space_id (UUID+existence); pg pool statement_timeout 30s
- Q9: disabled failing syncoid-donatello timer on Z
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three more read-only tools on securityRegistry:
- pending_review: agent-proposed changes awaiting approval (injection surface)
- resource_exposure: host/url/status attack-surface inventory (resources.listExposure,
scalar cols only — no monitoring/metadata/credentials)
- token_audit: token label/last_used/revoked, never the hash
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
verifyToken loaded every non-revoked token and bcrypt-compared each (O(n) per
request — auth-latency DoS + linear scaling). New token format
vk_<selector>.<verifier>: the non-secret selector is indexed and locates exactly
one row; only the verifier is bcrypt-hashed. Legacy NULL-selector tokens still
verify via a fallback scan. Dropped the useless idx_agent_tokens_hash.
- migration 010_token_selector.sql (adds selector col + unique partial index)
- createToken/verifyToken reworked; also adds listTokenMeta (read for Yerin's
token_audit tool)
- tests/repos/token_selector.test.js
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces FTS-only /api/search in place. RRF (k=60) fuses ts_rank and
pgvector cosine distance rankings. Vector branch silently skipped when
Ollama times out / errors, keeping search snappy and resilient.
Messages have no embeddings in Plan 3, so they participate in the FTS
branch only.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
create/update on embeddable repos enqueue embed.text with a singleton
key that coalesces rapid edits. No-op when the queue is not running
(server tests construct createApp without booting pg-boss).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Unifies pgboss.job (current, per-queue partitioned) and pgboss.archive
under one SELECT for operator views. retry promotes archived rows back
into the active partition.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Single GET /api/search?q=&space_id=&kinds=&limit=&offset= unions FTS
hits across pages / refs / source_docs / messages with a `kind`
discriminator and ts_rank ordering. Each branch's to_tsvector matches
the GIN index expression on its source table so indexes are used.
Messages have no space_id and are excluded when a space filter is set.
Hybrid vector / RRF lands in Plan 3.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Apply same composite-FK pattern as 001/002 for migration 003:
- resources: add UNIQUE (id, space_id) as FK target.
- resource_dependencies: denormalize space_id, composite FKs on both endpoints
(enforces both ends of a dep live in the same space at the DB layer).
- resource_credentials: denormalize space_id, composite FK to resources.
- source_docs.resource_id: NOT NULL (tenancy anchor); resource_id was already
absent from the update FIELDS list so docs cannot move resources.
Repos derive space_id from the resource on addDependency/addCredential so callers
can't fake cross-tenant assignment. 3 regression tests added.
Three security-review findings on migration 002:
- pages.space_id and refs.space_id changed to NOT NULL + ON DELETE CASCADE
(was nullable + SET NULL, which allowed orphan rows surviving space deletion).
- pages.parent_id wrapped in composite FK (parent_id, space_id) -> pages(id, space_id)
to prevent cross-space parent linkage (same pattern as tasks.project_id in 001).
- idx_refs_external promoted to UNIQUE on (space_id, source_kind, external_id);
upsertByExternal now requires space_id and dedups per-space, not globally.
Added 3 regression tests covering composite FK rejection, CASCADE-on-space-delete,
and per-space dedup independence.