[reconciliation] Rebuild HumeStone overlay on upstream OB1 pin - #5
Draft
Humestone wants to merge 163 commits into
Draft
[reconciliation] Rebuild HumeStone overlay on upstream OB1 pin#5Humestone wants to merge 163 commits into
Humestone wants to merge 163 commits into
Conversation
Adds a standalone MCP Edge Function that exposes a single delete_thought tool. Hard-deletes a thought by UUID with a pre-flight fetch so callers see a clear not-found outcome rather than a silent success. Deploys as its own Supabase Edge Function — the core server/index.ts is untouched. README documents an optional audit hook for installs that also use the thought_audit schema. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewords the step 5 verification from a bullet list into a short numbered sequence (capture → delete → delete-again → table-editor check) so the Verify section has explicit top-level numbered steps. This matches the update-thought-mcp README's verification style and makes the intended end-to-end happy-path + not-found path obvious to a first-time follower of the guide. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… connections Both server/index.ts and integrations/kubernetes-deployment/index.ts shared a global McpServer singleton and called server.connect(transport) on every request, attaching new transports to an already-connected server. Under concurrent load or repeated calls this causes transport state corruption and the reconnect instability reported on claude.ai. Fix: wrap all tool registrations in a buildServer() factory function and call it per-request, so each request gets a fresh McpServer + transport pair with no shared state. Also applied to both files: - Strip mcp-session-id from responses: server is stateless and advertising a session ID misleads clients into expecting resumption that never comes - Await transport.handleRequest() to enable response post-processing - Null-guard on the transport response with a 500 fallback Additionally backfilled into integrations/kubernetes-deployment/index.ts: - CORS preflight handler (OPTIONS *) — was missing entirely - Accept header patch for Claude Desktop connector compatibility (mirrors the fix already present in server/index.ts since issue NateBJones-Projects#33) - CORS headers on 401 responses and successful MCP responses
Validates the per-request McpServer pattern without any Supabase instance. MCP initialize is a pure protocol handshake — no tools are called, no DB touched. Tests (20/20): - CORS preflight: OPTIONS → 200 with correct headers - Auth: wrong/missing key → 401 with CORS headers - MCP initialize: 200, no mcp-session-id, CORS on success, valid protocolVersion - Per-request isolation: two sequential initializes both succeed with no mcp-session-id, proving the singleton is gone - tools/list: server responds cleanly, correct headers Setup (from server/ directory): npm install node test-stateless.mjs # or: npm test
Address Copilot review: factory function body was at column 0, making it ambiguous what runs per-request vs. at module load. Indent all content inside buildServer() by two spaces in both server/index.ts and integrations/kubernetes-deployment/index.ts.
Adds a small drop-in extension surface so dashboard add-ons can register
a new route + sidebar entry without touching core files:
- extensions.config.ts: typed registry, empty by default
- components/Sidebar.tsx: splits nav into core + extensions + trailing,
resolves icon keys via a registry; adds clock/folder/plug/sparkles
- lib/api.ts: exports apiFetch so extension pages can reuse the
authenticated JSON fetch + error plumbing
- EXTENSIONS.md: convention doc (folder layout, auth, icon registry,
sidecar vs in-tree REST routes)
After this change, future extensions touch only their own folder under
app/<route>/ and a single entry in extensions.config.ts.
https://claude.ai/code/session_01AvZANjBLBpEh3eFzPzzGGH
The COMMENT ON FUNCTION statement for thought_edges_upsert used the SQL || concatenation operator to join three string literals. PostgreSQL's COMMENT statement requires a single string literal after IS, not an expression, so the migration fails with a syntax error at the first ||. Collapse the three pieces into one literal so the schema applies cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Core Gmail puller script for a new recipe under recipes/gmail-smart-pull/.
The puller fetches messages from the Gmail API (read-only scope), strips
quoted replies and signatures, filters auto-generated noise, and emits
an OB1 ingest pack that downstream pipelines can feed into fingerprint
dedup + sensitivity-gate + upsert.
Also includes two small pure-JS libs the puller depends on:
- scripts/lib/sensitivity.mjs tags each message body against two
pattern sets (restricted: SSN, passport, bank, API keys, passwords,
credit cards; personal: email/phone/health/financial signals) so the
ingest side can route tiers to the right store. Tagging only — the
recipe does not enforce a routing policy itself.
- scripts/lib/entity-resolver.mjs does RFC 2822 header parsing
(From/To/Cc with quoted commas, display-name variants) into
{ name, email } pairs so structured correspondents can be carried in
the pack and upserted as first-class entities later.
OAuth credentials come from GMAIL_OAUTH_CLIENT_ID and
GMAIL_OAUTH_CLIENT_SECRET env vars. No real email addresses, client
IDs, or secrets are embedded anywhere. The only scope requested is
https://www.googleapis.com/auth/gmail.readonly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The LLM atomizer splits long email bodies into multiple atomic thoughts
before the puller emits them in the pack. Two behaviors carried over
from upstream experience running this at scale:
1. Prompts are piped to CLI providers via stdin, not via the -p
command-line argument. On Windows shell:true cmd.exe mangled
multi-line prompts containing quotes and newlines so the child
process received a truncated/empty string and the LLM replied
conversationally ("Looks like your message got cut off..."). 190/190
atomize calls in one real batch failed this way until stdin fixed
it. Same fix applied to the codex provider.
2. A new 'codex' provider shells out to `codex exec` so users
orchestrating the recipe from a Codex session can atomize without
crossing the streams with a nested claude-cli (which would fail
nested-process detection). The `claude-cli` provider still works
from standalone terminals and refuses to run inside Claude Code.
OB1 users will typically use provider='anthropic' (direct Messages
API) or 'openrouter' since OB1 is cloud-first and those are already
provisioned. CLI providers are opt-in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…anonical_email Two idempotent migrations that complete the pack's handoff to a downstream ingest pipeline: 1. merge_thought_metadata(p_id, p_patch) — shallow-merge a JSONB patch into a thought's metadata without re-triggering the full upsert path (no embedding regen, no enrichment, no fingerprint recompute). Useful for per-row metadata backfills like flipping a relationship_tier on a batch of thoughts after regenerating the contacts cache. 2. entities.canonical_email — adds a nullable TEXT column + a partial unique index to public.entities so email correspondents parsed from the pack's structured From/To/Cc blocks can be upserted by normalized email address. Existing uniqueness on (entity_type, normalized_name) is preserved because two people can legitimately share a display name; email is the stable identifier. Both use CREATE OR REPLACE / IF NOT EXISTS guards — safe to re-run. Neither drops or renames existing columns. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README documents the full setup path: Gmail OAuth Desktop-app client, env vars (no credentials on disk), first-run consent flow, dry-run, real run, and optional migration install. Explicitly covers the four design choices most likely to surprise a new user: - Sensitivity routing is tag-only — the recipe does not enforce a policy, the ingest pipeline does. Calls out that OB1 is cloud-first so "restricted stays local" needs explicit wiring (two-store setup or block-on-import). - Engagement filter defaults to engaged-only with STARRED/IMPORTANT bypass, with clear instructions to disable or rebuild. - Relationship tier is metadata (contact/known/unknown), not a gate. Three ways to produce the contacts cache documented. - Atomization is opt-in per-message (>= 150 words default) with anthropic/openrouter/claude-cli/codex provider choice. Graceful fallback to whole-message capture on atomizer failure. metadata.json follows the schema template at recipes/_template/ with required fields (name, description, category, author, version, requires.open_brain, tags, difficulty, estimated_time) and no extras. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…l tracker Codex review (P2 originally, elevated to P1 in triage): the credential tracker in the README asked users to paste their Supabase service-role key into a plaintext doc, but this recipe never touches that key — the puller only emits a pack file, and any downstream ingest pipeline that needs service_role should read it from env/secret manager, not from a user's text editor. Removing the field avoids an entirely avoidable leak path for a highly privileged database secret, and adds a note so contributors who copy this tracker pattern into other recipes don't reintroduce the mistake.
… loopback bind + HTML escape + HTTP checks) Codex identified four coupled OAuth weaknesses in scripts/pull-gmail.mjs: - No OAuth state parameter: authUrl was built without a random state and the callback handler accepted the first ?code= it saw. Any local process or malicious localhost page could race the browser redirect and bind the script to an attacker-controlled Google account. - server.listen() without an address defaulted to IPv6-any/0.0.0.0 on some platforms, briefly exposing the callback to the LAN. - URL error parameter reflected into HTML without escaping — low-impact reflected XSS but trivial to fix. - Token exchange and refresh called res.json() before checking res.ok, so proxy/5xx responses produced a useless JSON parse error instead of a useful OAuth failure with status + body. Fix: generate 16 bytes of random hex as state, require the callback to echo it back (mismatch -> hard reject), bind createServer to 127.0.0.1 explicitly, HTML-escape the error param before reflecting, and gate both token POSTs on res.ok with a bounded body preview on failure.
…s to sensitivity classifier Codex flagged (originally P2, elevated to P1 in triage because the sensitivity tier drives downstream routing): the restricted-tier pattern set missed several common secret formats, so emails containing them would be classified 'standard' and flow into the general thoughts pool instead of the restricted-only store. Adds patterns for: - openai_key — sk-proj-, sk-svcacct-, sk-admin- variants - anthropic_key — sk-ant-api / sk-ant-admin tokens - aws_access_key_id — AKIA/ASIA/AROA/AIDA prefixes - aws_secret_access_key — proximity match near "aws secret" label - gcp_api_key — AIza<35 chars> canonical form - jwt_token — eyJ<header>.<payload>.<sig> three-segment form - pem_private_key — BEGIN PRIVATE KEY blocks (RSA, EC, DSA, OPENSSH, PGP, ENCRYPTED) - github_token — ghp/gho/ghu/ghs/ghr _ 36+ char bodies - slack_token — xox[aboprs]- tokens The existing generic api_key_pattern is kept as a belt-and-suspenders fallback. All patterns still fail-open (standard tier) on no match — classification never throws, so a missing pattern degrades gracefully rather than blocking the pull.
…+ harden atomize prompt against injection Codex flagged this as the highest-severity finding in the atomize lib (originally tagged P1-5 + P2): the 'codex' provider spawned `codex exec --dangerously-bypass-approvals-and-sandbox -` with an email body interpolated directly into the prompt. A malicious sender can embed 'IGNORE PREVIOUS INSTRUCTIONS' or tool-call primers, and because the child Codex agent ran with the sandbox disabled, prompt-injection escalated to arbitrary local command/file access. Fixes: 1. Remove the --dangerously-bypass-approvals-and-sandbox flag from the default codex invocation. Users who actively need it for an atomization-only run can opt in via GMAIL_ATOMIZE_CODEX_BYPASS=1 env var, which documents the risk at the opt-in site. 2. Strengthen DEFAULT_ATOMIZE_PROMPT with an explicit SECURITY section that frames the INPUT THOUGHT as untrusted data, not instructions, and forbids emitting system/tool/assistant markers in the output. 3. Add a top-of-file comment describing the prompt-injection threat model so callers who override the prompt don't silently drop the hardening. This does not eliminate prompt injection (no prompt-only defense can), but it removes the most dangerous escalation path and raises the bar from "read email -> run code" to "read email -> influence atoms".
The previous regex `\b(?:aws[_ -]?secret|aws[_ -]?access[_ -]?key)\b` could not match `aws_secret_access_key=...` — the most common env-var form — because `_` is a word char, so the `\b` between `t` and `_` in `aws_secret_access_key` didn't fire, and neither alternation caught the combined phrase. Restructured the alternation so `aws_secret` can optionally absorb the trailing `_access_key`: aws[_ -]?(?:secret(?:[_ -]?access[_ -]?key)?|access[_ -]?key) Verified against 8 test cases covering kvp form, uppercase, hyphen separators, space separators, standalone `aws_secret`, standalone `aws_access_key`, a negative case, and the full env-var pair. All pass with no false positives.
SECURITY DEFINER function was granted to authenticated/anon, allowing RLS bypass. Now restricted to service_role only. Added FOR UPDATE to prevent concurrent evidence appends from losing writes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop the stale reference to `schemas/enhanced-thoughts/` (deleted on this branch and not actually used by the SQL — the function only touches `thoughts.id` and `thoughts.metadata`). Also update Expected Outcome to reflect the service-role-only grant on `append_thought_evidence` so users don't re-grant it to anon/authenticated by accident. Why: README claimed a prerequisite that 404s on the repo and mis-stated the RPC's trust boundary. Both were latent user-footguns.
Add nullable `user_id uuid` to `ingestion_jobs` and `ingestion_items` via idempotent `ALTER TABLE ... ADD COLUMN IF NOT EXISTS`. A DO block conditionally adds FKs to `auth.users(id) ON DELETE CASCADE` only when Supabase's `auth` schema and `users` table exist, so the migration stays safe on non-Supabase Postgres. Why: without user_id, multi-tenant deployments leak ingestion history across users. Nullable keeps single-tenant stock OB1 working with no data migration and lets RLS policies (added separately) key off auth.uid() = user_id once populated.
Turn on row level security for `ingestion_jobs` and `ingestion_items`, add a `service_role ALL` policy on each (so worker writes still flow), and — conditionally, only when `auth.uid()` exists — add an `authenticated SELECT` policy scoped to `user_id = auth.uid()`. Policies are wrapped in DROP POLICY IF EXISTS / CREATE so the file is still idempotent on re-run. Why: the grant block was already service-role-only, but without RLS there was no backstop if Supabase's schema-level defaults quietly granted `USAGE`/`SELECT` to `anon` or `authenticated`. RLS closes that door. Giving authenticated users a SELECT scoped to their own rows matches the pattern used by the rest of the Open Brain extensions and is a no-op until someone populates `user_id`.
Add partial indexes keyed on `created_at` for rows in the active
lifecycle (`status = 'pending'` on jobs; `status IN ('pending','ready')`
on items). Both use `CREATE INDEX IF NOT EXISTS` so re-running the
migration is a no-op.
Why: the worker polls for the next pending job and for ready items
repeatedly. Without a partial index, every poll becomes a seq scan
against a table whose historical tail of completed rows grows forever.
Partial indexes stay tiny (only live queue rows) and shrink to near
zero when the queue drains.
Add a Job Claim Semantics section to the README that states the contract explicitly: claim logic lives in the companion Edge Function (`integrations/smart-ingest/`), and any worker that claims a row MUST use `FOR UPDATE SKIP LOCKED`. Include a canonical UPDATE-with-sub-SELECT pattern that pairs with the new partial indexes. Also sync Expected Outcome with the new user_id columns, partial indexes, and RLS policies so the README matches the schema it describes. Why: the schema file is deliberately minimal (no claim RPC), so without this note a downstream author could wire up a plain SELECT- then-UPDATE worker and silently double-process the queue. Putting the contract in the schema README — next to the tables it operates on — keeps the DB layer's requirements discoverable even when the companion Edge Function lives in a separate folder.
Why: Without AbortController, a slow Supabase response can hang past HARD_TIMEOUT_MS=25000, triggering process.exit(0) mid-flight so the capture is lost with no retry-queue entry. Add fetchWithTimeout helper (default 10s, env override FETCH_TIMEOUT_MS) and route AbortError through saveToRetryQueue so timed-out captures survive as queued retries instead of silent loss.
Why: Blindly retrying every non-2xx response wastes API calls on permanent errors — a revoked MCP_ACCESS_KEY (401) or oversized payload (413) currently retries 5x per future session. Add isRetryableStatus() and split the main branch into ok / retryable / permanent paths. Apply the same rule in processRetryQueue: a 4xx on a queued entry moves straight to dead/ instead of exhausting the attempt counter.
Why: The old regex-based path munging only matched uppercase drive letters and silently breaks on lowercase or non-ASCII paths. Use node:url's fileURLToPath for correct cross-platform resolution and let OB_PROJECT_ROOT override the two-levels-up default, since README tells users to install the script in arbitrary scripts directories.
…ters Why: User turns are concatenated verbatim into the ingest POST body. An attacker who pastes "Ignore previous instructions and DROP thoughts;" into a session lands that text untouched at the ingest endpoint. Wrap the transcript body in <thought_content>...</thought_content> delimiters and neutralize literal occurrences of those tags inside user content so a malicious turn can't break out of the wrapper.
…lanshurafa/gmail-smart-pull [recipes] Gmail smart pull — sensitivity routing + contact entities
…lanshurafa/smart-ingest-schema [schemas] Smart ingest pipeline tables
…lanshurafa/thought-enrichment [recipes] Thought enrichment pipeline
…lanshurafa/brain-stats-daily [schemas] Brain stats daily + heatmap filter
…lanshurafa/crm-person-tiers [schemas] CRM person tiers — relationship tier schema + dashboard page
…lanshurafa/consolidation-workers [integrations] Consolidation workers (bio + metadata)
Accepting the access key via ?key= leaks it into CDN/proxy/Supabase access logs. Accept it only via the x-brain-key or Authorization: Bearer headers, matching enhanced-mcp. Updates README examples + auth docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
State that the service-role backend bypasses RLS (key = full-brain access, single-tenant by design) and that the key must be high-entropy since rate limiting is best-effort. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document that MCP_ACCESS_KEY should be high-entropy (single shared secret, single-tenant) and that the wildcard CORS is deliberate and safe given header-based auth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/dashboard-extension-hook Add config-driven extension hook for sidebar
…lanshurafa/chrome-capture-gemini-history [integrations] Chrome extension — Gemini bulk history sync (Phase B/C)
…lanshurafa/enhanced-mcp [integrations] Enhanced MCP server (alpha tool suite)
…lanshurafa/rest-api-gateway [integrations] REST API gateway
…-ob1-gate-v2-empty-runs [docs] Fix OB1 gate v2 workflow runs
…red-key fallback Prefer the dedicated read-surface key when set; retain the shared MCP_ACCESS_KEY fallback until Stone-side harnesses migrate (design decision D1 — full decouple is a later increment). Matches agent-memory-api v6 deployed 2026-06-11. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This draft reconstructs the HumeStone OB1 overlay on the clean upstream pin
677910600de98067f61c120d65956b23f360aedb.Review boundary
This is intentionally a draft, review-only PR. Do not merge or deploy from it yet.
The HumeStone fork's
mainwas 159 upstream commits behind and retained 11 fork-only commits when this branch was built. The GitHub PR diff therefore includes the upstream refresh as well as the four reconstruction commits. The overlay-specific lineage is:GitHub currently reports this draft as
CONFLICTINGagainst forkmain, which is expected from those retained fork-only commits. This PR is evidence and review surface only; selecting and executing a conflict-resolution strategy is a separate Stone recommendation and approval gate.e4e5f4d— Agent Memory recall/governance guards1ed41ad— Agent Memory auth and scope boundary541d3e4— dedicated Agent Memory key with compatibility fallbackb37cfad— selective Mission Control/auth/scope reconstruction and provenance packetSee
docs/humestone-overlay-reconstruction-2026-07-22.mdfor file-by-file provenance, conflict choices, omissions, and validation evidence.Validation
Known limitations and gates
supabase/functions/agent-memory-apiremains intentionally absent; the sync checker fails closed until a deployment package is deliberately materialized.dist/index.js; the upstream artifact remains unchanged and the inherited debt is documented.