[schemas] enhanced-thoughts: harden upsert/match/search RPCs#363
[schemas] enhanced-thoughts: harden upsert/match/search RPCs#363alanshurafa wants to merge 3 commits into
Conversation
Existing installs ran older upsert/search RPCs than the reference brain. This brings them current without breaking the v1 contracts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A metadata-only re-upsert recomputed importance/quality_score/type/ source_type/sensitivity_tier to hardcoded defaults, so the merge-path COALESCE never fell through to the existing column and silently overwrote it -- including a sensitivity_tier restricted->standard privacy downgrade. Split the locals into insert-only defaults (new rows) and explicit-incoming values that are NULL when the payload omits the key, so an omitted field preserves the existing column while an explicit value still updates. Extend the user-edit guard to the promoted scalar columns so a human-owned field keeps column and metadata in agreement, strip both source/source_type aliases when either is guarded, and re-derive the task/idea status seed from the effective post-guard type so a rejected type can't seed a stale status. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reimports Restricted rows leaked through search_thoughts_text when the tier lived only in metadata (column default 'standard'); restrict if either source says 'restricted', matching how provenance-chains reads the tier. Original-fingerprint dedup returns the corrected row, but p_content is the old text; surface matched_via so open-brain-rest skips overwriting the corrected row's embedding with a stale-text vector. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6f972b990
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!matchedViaOriginalFingerprint) { | ||
| update.embedding = embedding; | ||
| } |
There was a problem hiding this comment.
Preserve corrected rows after original-fingerprint matches
When matched_via is original_fingerprint, this guard skips only embedding, but the subsequent update still writes metadata, promoted fields, and status derived from the stale original capture. That bypasses the RPC's user_edits / original_fingerprints merge protection and can overwrite the corrected row's human-owned metadata or workflow state whenever a REST reimport hits the fallback; in this branch the update should be skipped or limited to fields that cannot be stale.
Useful? React with 👍 / 👎.
| IF v_type IN ('task', 'idea') THEN | ||
| v_status := 'new'; |
There was a problem hiding this comment.
Preserve existing task statuses on duplicate upserts
On merge paths where the caller omits status, this re-derives v_status = 'new' from the effective existing type, so any duplicate or metadata-only re-upsert of a task/idea that is already planning, active, done, etc. is reset back to new and gets a fresh status_updated_at. That is a regression from preserving omitted fields/status; only seed new on insert or when the existing row's status is null.
Useful? React with 👍 / 👎.
What this does
The
enhanced-thoughtsschema shipped an older generation of theupsert_thoughtandsearch_thoughts_textRPCs than the reference brain has been running. This PR closes that gap. Everything is additive and idempotent — re-runningschema.sqlon a v1 install is a no-op, and the v1 return contract ({id, fingerprint}) plus thestatus/status_updated_athandling thatworkflow-statusdepends on are left exactly as they were.Changes
search_thoughts_textlearns date and tier filters.p_filternow reads three reserved control keys —start_date,end_date(ISO 8601), andexclude_restricted(boolean) — and applies them at the data layer. They are peeled off before themetadata @> filtercontainment check, so any other key keeps its old behavior.upsert_thoughtgets two dedup/merge guards.metadata.original_fingerprints[]array, the reimport now lands on the corrected row instead.metadata.user_editsare treated as human-owned and stripped from an incoming automated patch, so a reimport can't clobber a human correction. Guards metadata keys only.ON CONFLICT. The unique-index race thatON CONFLICThandled for free is caught explicitly and folded back into the merge path.New opt-in
match_thoughts_superseded_aware. Same shape as the corematch_thoughtsplus asuperseded_bycolumn. Thoughts that have been replaced (the target of asupersedesedge inthought_edges) take a 0.8x ranking penalty so fresh thoughts surface above their stale predecessors, without ever being excluded. The corematch_thoughtsis untouched; callers opt in by name, matching therecency-boosted-match-thoughtspattern. Installed only whenschemas/typed-reasoning-edges/is present; otherwise it's skipped with a NOTICE and the rest of the migration still applies.ID contract
All ported references use
UUIDagainstpublic.thoughts(id). The superseded-aware RPC returnssuperseded_by UUIDand readsthought_edgeswherefrom_thought_idis the newer replacement andto_thought_idis the stale thought — the directional semantics OB1'styped-reasoning-edgesalready documents.Importance scale
Some installs use a narrower 0-6 importance scale. Open Brain's upsert already accepts a wider 0-100 range, so it never clipped 0-6 values — switching to 0-6 here would retroactively rescale every existing row, which is a breaking data change, not an additive one. The 0-100 clamp stays; the README documents 0-6 as a subset for cross-scale parity.
Testing
Applied against a throwaway PostgreSQL instance with stub
thoughts+thought_edgestables:{id, fingerprint}; re-insert dedupes to the same row.titleagainst a robot overwrite.start_dateandexclude_restrictedfilters return the expected subset.match_thoughts_superseded_awarereturns the stale row at 0.8x withsuperseded_byset, fresh row at full similarity.schema.sqlis clean; droppingthought_edgestriggers the documented skip-with-NOTICE.Compatibility
Preserves the workflow-status dependency documented in #328 —
upsert_thoughtstill writesstatus/status_updated_at, and the README's Prerequisites now call outworkflow-statusexplicitly.