Skip to content

Cortex v0.2.0-51: public-ready — cleaned repo, new README, one-click connect, auto-update - #3

Merged
sarptandoven merged 738 commits into
mainfrom
feat/connect-gate-redesign
Jul 24, 2026
Merged

Cortex v0.2.0-51: public-ready — cleaned repo, new README, one-click connect, auto-update#3
sarptandoven merged 738 commits into
mainfrom
feat/connect-gate-redesign

Conversation

@sarptandoven

Copy link
Copy Markdown
Collaborator

Brings main up to the shipped, public-ready state (build 51).

Highlights

  • Public release v0.2.0-51 is live and downloadable (notarized DMG on doppl-tech/releases; live auto-update feed at build 51).
  • Repo cleaned for public — removed internal ops/strategy/launch docs and stale build artifacts; redacted all PII/infra (personal emails → support@trydoppl.com, server IP, founder local path). No live secrets were present.
  • New public README — badges, hero, the Connect/Review/Ask/Control loop, architecture diagram, install, build-from-source, docs index, honest source-available license note.
  • Product: one-click connect both directions (MCP tools + chat imports), automatic updates, ~31 UI-elevation fixes, and only-functional connect surfaces (no dead "Available soon" tiles).

Verification

  • Full pipeline e2e: 27/27 PASS. Retrieval + adaptation eval gates green. check_docs_current / ops_readiness / check_distribution_site green.
  • Notarized build 51: Gatekeeper accepted, stapled, vector ok.

Note: main had diverged (an older v0.1.0 beta-release-prep state with faq/terms/subprocessors.html + status/ scaffold). This PR supersedes it with the current product line; review the site changes before merging.

🤖 Generated with Claude Code

sarptandoven and others added 30 commits July 6, 2026 20:57
…tellation overlay + review pill

Implements the approved bottom-of-screen / notch animation set (P1–P5). One coordinator
(LiveActivityCenter) owns the bottom region and arbitrates so the surfaces never collide,
sampling the same state the menu-bar icon does:

- P1 Bottom Learning HUD: live "Learning from Notes — 11,853 / 15,899" progress capsule while
  Cortex works; morphs to a check + "Learned N new memories" on completion. Sync poll tightened
  to ~1.2s while active so the bar advances smoothly.
- P3 Ambient edge-glow: a moss bottom-edge glow that breathes ONLY while working.
- P4 "Memory formed" ripple: one-shot ring on each new learned/captured event (deduped).
- P5 Live-activity pill: idle "N to review" pill, hover-expands to Review / Open Cortex.
- P2 Constellation overlay: the interactive map (reused MemoryMapView) slides up full-screen;
  tap a node → "Explore in Ask" runs a cited query. Summoned from a Home "Open full view"
  button + right-click menu; Esc / backdrop / close to dismiss.

Adversarial review (4 dimensions → verify) found + fixed before shipping:
- HIGH: pill collided with the HUD celebration at bottom-center → pill now gated on !hud.isPresenting.
- HIGH: pill buttons needed two clicks from another app → FirstMouseHostingView (acceptsFirstMouse).
- HIGH: stale lastLearnedCount falsely celebrated after a no-op sync → reset at outermost beginMenuBarWork.
- LOW: edge-glow breathe ran forever → gated start/stop on model.active.
- LOW: edge-glow didn't follow the mouse across monitors → repositions every active tick.

Verified: swiftc -typecheck EXIT 0; both variants build. On-device behavior (windowing/animation,
multi-monitor, pill clicks) needs the founder's click-test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Swap site/downloads to build 10, regenerate feeds, bump version strings. Build 10 adds the
bottom-of-screen live-activity set (Learning HUD, edge glow, memory ripple, Constellation
overlay, review pill).

Verified: shasum -c OK; latest.json dmg sha256 == on-disk DMG sha256; validate_update_
manifest.py status ok (build 10, 3 artifacts); DMG boots (/ready ok, model2vec 256-dim,
sqlite-vec v0.1.9).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… dismissable

Two reported issues:

1. "Importing my Claude chats takes forever and never finishes." importFromPath sent
   processing:sync with max_records:5000 — the backend then fully extracts + embeds every
   conversation INLINE in the one HTTP request, which blocks for minutes on a large export
   (and, because the request never returns, keeps menuBarWork raised so the bottom HUD stays
   pinned the whole time). Switched to processing:async + a paginated loop: the POST parses +
   enqueues each conversation as a background job and returns fast; the queued jobs then drain
   via the existing job poll with the Learning HUD showing live progress and auto-dismissing on
   completion. Paginates (has_more/next_offset) so the FULL export imports, capped at 40 pages.

2. "The bottom bar always stays there and you cannot cancel it." Root cause of the stuck bar was
   #1 (perpetual work). On top of that:
   - Added a master switch: liveActivityEnabled (persisted, default on) + a checkable
     "Show live activity" item in the menu-bar right-click menu. When off, the coordinator hides
     everything (new hud.forceHide) and does no work.
   - Made the "N to review" pill NON-PINNING: it now flashes only when the review backlog GROWS
     and auto-dismisses after ~8s (hover keeps it), instead of sitting there permanently.

Verified: swiftc -typecheck EXIT 0; both variants build. Backend unchanged (already supported
async imports). On-device import + bottom-bar behavior needs the founder's click-test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fresh site feeds

Verified: shasum -c OK; latest.json dmg sha256 == on-disk DMG sha256; validate_update_manifest
status ok (build 11, 3 artifacts); DMG boots (/ready ok, model2vec 256-dim, sqlite-vec v0.1.9).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e gloss)

The "Building your memory" bar now animates while working: an indeterminate segment sweeps
left→right when the conversation count isn't known yet, and once the total is known a moss
fill (eased) carries a soft gloss sweep so it reads as live between poll samples.

Adversarial review found + fixed two motion defects before commit:
- The hosting view is REUSED across presents (not torn down), so a re-present in the same mode
  didn't restart the sweep (frozen bar). Fixed: bump a barEpoch on each fresh present and .id the
  bar on it, forcing a fresh identity + onAppear per present.
- Re-animating a property with a finite animation doesn't reliably cancel a repeatForever. Fixed:
  hardStop() snaps phases to 0 via Transaction(disablesAnimations:true) before starting the new
  loop, plus an onDisappear hard-stop.

Verified: swiftc -typecheck EXIT 0. Staged for the next DMG build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…connector copy

The browser sign-in flow (PKCE loopback + token exchange + refresh + auto-sync) is already built;
it's un-provisioned, not missing — a connector needs a registered OAuth client ID to flip from a
badge to a working "Sign in."

- docs/OAUTH_SETUP.md: verified (deep-research) per-provider provisioning checklist — register
  steps/console URLs, exact loopback redirect URIs, read-only scopes, PKCE-vs-secret, verification
  process/cost/time, and a recommended sequence. Key facts: Google Calendar/Docs = sensitive
  (verification, no CASA), Gmail + full Drive = restricted (CASA, the long pole); Slack + Microsoft
  = truly secretless PKCE; Notion + GitHub require a secret → need a hosted broker (or GitHub
  device flow). Priority: Notion → Google Calendar → Docs/drive.file → Microsoft → Slack → GitHub →
  Gmail last.
- ConnectionsPrivacySheet: unconfigured managed-OAuth connectors now read "Coming soon" (was
  "Setup required" / "Not configured", which wrongly implied the USER must do something — it's a
  Cortex rollout gap, not user action).

Verified: swiftc -typecheck EXIT 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rs (Notion/GitHub)

Option A: a hosted-only token-exchange broker so confidential-client providers (Notion needs
HTTP Basic client_id:secret with no PKCE; GitHub needs a secret unless device flow) can offer
one-tap "Sign in" without embedding a secret in the distributed desktop app.

- backend/app/oauth_broker.py: env-driven provider registry (CORTEX_BROKER_<PROVIDER>_CLIENT_ID/
  _CLIENT_SECRET, optional _AUTHORIZE_URL/_TOKEN_URL/_SCOPES), builds authorize URLs and performs
  code→token exchange + refresh. Notion uses Basic auth; GitHub posts creds + supports PKCE. Returns
  tokens WITHOUT persisting them (auth handshake transits the server; content sync stays local).
  Load-bearing guard: redirect_uri is ALLOWLISTED to the app's loopback callback paths so the broker
  can't be abused as an open token-minting oracle. Stdlib-only HTTP; token_request is injectable.
- Registered on the hosted FastAPI app (register_oauth_broker_routes) at /oauth/broker/{providers,
  start,exchange,refresh}; a no-op 503 surface until env vars are set + deployed. NOT on the local
  per-user server.
- 10 unit tests (mocked provider) cover: unconfigured→503, unknown→404, Notion authorize URL,
  redirect allowlist (blocks foreign/https/bad-path, permits loopback any-port + google path),
  Notion Basic auth + no-secret-in-body, GitHub POST creds + PKCE, missing-access-token→502,
  refresh grant + expiry, missing-secret→503. All pass.

Verified: py_compile OK; pytest backend/tests/test_oauth_broker.py 10/10.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
docs/OAUTH_SETUP.md gains the exact turn-on steps for the token-exchange broker (register app →
set CORTEX_BROKER_<PROVIDER>_CLIENT_ID/SECRET → register loopback redirect → deploy → give me the
broker base URL for CORTEX_OAUTH_BROKER_URL), plus the no-broker PKCE path for Google/Microsoft/
Slack. deploy/cortex.env.example gains the broker env template.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deep per-provider research (5 providers researched against current 2026 consoles + synthesized):
numbered, click-by-click setup for Notion, Google (Calendar/Docs/Drive/Gmail), Microsoft/Outlook,
Slack, GitHub — console URL, client type, every field + value, exact loopback redirect registration
(incl. Microsoft manifest replyUrlsWithType quirk + Google Web-application-client answer for the
fixed callback path), minimal read-only scopes, secret handling / broker, verification submission
(sensitive vs restricted/CASA), gotchas, and sources. Includes recommended order, a "what to hand
back to Cortex" list, and a comparison table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ESTION_SURVEY.md)

Deep per-group research (AI chats, PM/dev tools, notes, comms) synthesized into a strategy doc:
three ingestion tiers (local-file > export > OAuth = "meet users where their data already is"),
a master comparison table (~40 services: popularity, best path, auth/effort, one-line how), grouped
detail with exact on-disk paths / export click-paths / API endpoints + scopes, and a phased build
order. Key finding: consumer AI-chat history (ChatGPT/Claude/Gemini/etc.) has NO read API — export
is the only path, so frictionless export auto-detect is first-class. Many high-value sources
(iMessage, Apple Notes/Calendar/Contacts, WhatsApp, Bear, Chrome/Safari/Arc, Obsidian) are pure
local-file wins needing only macOS permissions — no OAuth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add secretless 'Sign in with GitHub' via the OAuth Device Flow (RFC 8628),
so users connect GitHub by authorizing in the browser instead of pasting a
personal access token. Public client ID only — no client secret, no broker.

Backend (standalone server):
- connectors/github.py: github_device_start/github_device_poll helpers with an
  injectable requester; +10 unit tests covering start, poll statuses (ok/
  authorization_pending/slow_down/expired_token/access_denied/error) and guards.
- standalone_server.py: POST /v1/connectors/github/device/{start,poll}, reading
  CORTEX_GITHUB_OAUTH_CLIENT_ID (+ optional CORTEX_GITHUB_OAUTH_SCOPE); 503 when
  unconfigured, 502 when GitHub is unreachable.
- storage.py: github blueprint advertises device_flow_{provider,start,poll}
  endpoints; _connector_connection_setup serializes them into the catalog.

macOS app:
- Info.plist: CortexGitHubOAuthClientID = Ov23liwB0zkC2Qac88ig; AppDelegate exports
  it as CORTEX_GITHUB_OAUTH_CLIENT_ID to the backend.
- AppState.startGitHubDeviceFlow: start -> show code + open verification URL ->
  poll at interval (honors slow_down/expiry, cancellable) -> on token, discover
  repos and run the existing direct-connector sync (token persisted like paste).
- GitHubDeviceCodeView sheet: shows the user code, copy button, live progress
  (waiting -> syncing -> done/failed), and cancel; GitHub tile CTA reads 'Sign in'.

Verified: build.sh (direct) compiles; 173 backend tests pass (device + broker +
catalog + standalone + storage); live routing smoke on a spare port (401 without
token, env resolution, GitHub error normalized to 502).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Swap site downloads to Cortex-0.2.0-12 (DMG + app.zip + checksums + Obsidian
plugin), refresh latest.json / distribution.json update feeds and the download
links in index.html / app.js / privacy.html. Release notes call out browser
'Sign in with GitHub'. Artifacts checksum-verified; feed sha256 matches files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…orters + /v1/tools REST surface

Phase 1 (tool metadata): every tool now carries MCP annotations (readOnly/
destructive/idempotent/openWorld, single-sourced from the scope sets so hints
can't drift), a title, and output schemas on the high-value tools.

Phase 2 (universal reach): the same TOOLS list projects to OpenAI + Anthropic
function-calling schemas and an OpenAPI 3.1 doc (export_openai_tools /
export_anthropic_tools / export_openapi / export_tool_schema). New local REST
surface so any function-calling app can drive Cortex over plain HTTP:
  - GET  /v1/tools/schema?format=openai|anthropic|openapi|mcp
  - POST /v1/tools/call            {name, arguments}   (generic)
  - POST /v1/tools/{name}          arguments as body   (matches OpenAPI operationIds)
All authed like /mcp (Bearer -> scoped context) and enforced per-tool by
call_tool, so a read-only token gets a read-only surface; scope filtering flows
through tools_for_scopes.

Verified: 11 new unit tests (annotations correctness, output schemas, exporter
shape + scope filtering + dispatch), 74 standalone-server tests green, live
smoke on a spare port (63 OpenAI tools, OpenAPI 3.1 with a path per tool,
generic + per-tool dispatch, 401/422 error paths).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… by default)

storage.py: add _rerank_rows — an optional reranking stage between fusion and
diversification. model2vec query<->candidate cosine blended with the fused-rank
prior (0.7/0.3), optional MMR (lambda 0.3) to suppress near-duplicates. Wired
into search()'s main + fallback paths. Fully guarded: no-ops when CORTEX_RERANK
is unset/off (default), when the embedder is the hash fallback, on empty query,
<2 rows, or embed failure. Only reorders eligible rows — citation/review gates
downstream untouched. Stdlib/CPU only (reuses the bundled embedder).

scripts/tool_routing_eval.py + scripts/adapter_contract_eval.py: new CI-blocking,
deterministic, in-process gates. tool_routing: cap-compliance (core fits every
client cap), scope-safety property test (all 63 tools x insufficient scopes ->
PermissionError), core-surface snapshot, annotation correctness. adapter_contract:
OpenAI/Anthropic/OpenAPI round-trip + name parity + scope projection + dispatch
parity + export_tool_schema dispatch. Both wired into ci.yml.

Verified: retrieval_eval green with rerank OFF (default, hash provider: 163 cases,
top1 1.0). Confirmed the reranker cannot affect the CI gate (hash-guarded; the
gate runs under hash). model2vec-seeded rerank validation is deferred to the
context_pack_eval / rerank_reorder cases (the eval's expectations are hash-tuned
today). Both new harnesses exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…re exposure)

use_cortex: one natural-language entry point (added to CORE + READ). A
deterministic router (_route_use_cortex) classifies the task and dispatches to
ask_memory (questions), get_entity_context (entity/person briefings),
search_memory (explicit searches), or get_context (default working pack),
returning {routed_to, alternatives, result}. Dispatch goes through call_tool so
every target keeps its own scope enforcement — all targets are read-only, so the
router can never escalate. Task-awareness happens INSIDE the call, keeping the
advertised tools/list cache-stable for clients.

Surface presets: MCP_TOOL_SURFACES (core/coding/chat) + tools_for_scopes now
resolves named presets so each client gets a stable, cap-appropriate list (all
presets stay under Cursor's 40-tool cap); unknown names fall back to core.

Verified: 17 metadata/router/preset unit tests + 80 standalone-server tests green;
tool_routing + adapter_contract gates exit 0; live routing through /v1/tools/call
(ask_memory / get_entity_context / get_context) returns cited results.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dependency-light clients over the local Context API + /v1/tools surface so any
app integrates in a few lines. Python (stdlib-only urllib, pip: cortex-client)
and TypeScript (fetch, npm: @cortex/client) both expose tools_schema/call_tool/
context/search/ask + openai_tools/anthropic_tools convenience, unwrap the
{schema}/{tool,result} envelopes, and raise CortexError(status, detail).
Loopback default + Bearer token. READMEs show OpenAI + Anthropic function-calling
wiring (client.openai_tools() -> tools=, route calls back through call_tool).

Verified: 19 network-free python unit tests green (URL/method/headers/body +
parsing + error paths); py_compile clean; TypeScript tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
use_cortex was intentionally added to CORE_TOOL_NAMES (Phase 6 router); update
the two curated-surface snapshot assertions accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g-gated)

New self-contained, stdlib-only backend/app/query_plan.py -> build_query_plan()
returns a QueryPlan: intent (keyword fast-path + optional model2vec
nearest-prototype semantic override, no-op under hash), soft layer-routing hints,
proper-noun entities, and sub-queries for compound questions. Memoized prototype
embeddings; no storage import (avoids a cycle).

Wired into storage.py behind CORTEX_QUERY_PLAN (default off): assemble_context
upgrades intent via the plan when enabled; answer_query decomposes compound
questions and unions the extra candidates BEFORE the citation gate (purely
additive to recall — single-query behavior unchanged).

Verified: 12 query_plan unit tests green; retrieval gate green with the flag both
OFF (default) and ON under hash (163 cases, top1 1.0, recall@3 1.0) — proving the
planner never regresses the gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…; +2 eval gates

mcp_tools.py: cortex:// RESOURCES (person-map, personal profile, agent-adaptation,
schema/capabilities, daily review) + entity/{name} template, and PROMPTS
(summarize_recent_decisions, extract_action_items, brief_me_on) that embed cited
context. read_resource/get_prompt run the same read-scope + trust gate + redaction
as read tools (never a scope bypass).

standalone_server.py: wire resources/list|read + prompts/list|get into /mcp;
advertise resources+prompts capabilities; MCP_PROTOCOL_VERSIONS now offers
2025-06-18 (newest-first, echoes older clients unchanged).

Eval: scripts/context_pack_eval.py (scores assemble_context: nDCG@5 0.89, MRR 1.0,
citation-coverage/no-leak/budget = 1.0) and scripts/agent_task_eval.py (scripted
end-to-end: task_completion 1.0, injection-resistance 1.0, read-only discipline
1.0) — both wired into CI.

Verified: 11 real-store resource/prompt unit tests + 75 standalone-server tests
green; both new gates exit 0; live /mcp smoke (initialize->2025-06-18 + all three
capabilities, resources/read valid JSON, prompts/get grounded).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…riefs

New backend/app/delivery.py: is_safe_webhook_url (SSRF guard — refuses loopback/
private/link-local/reserved/multicast + credential URLs; public targets must be
https; CORTEX_DELIVERY_ALLOW_INTERNAL for local dev), build_delivery_payload
(reuses assemble_context so the pushed brief is cited-only, sector-isolated, and
identity-omitted), deliver_webhook (injectable sender, SSRF-guarded).

standalone_server: POST /v1/delivery/preview (read — 'what will be shared',
nothing leaves) and POST /v1/delivery/send (egress: export scope +
allow_agent_exports trust toggle + SSRF guard + audit via record_agent_event).

Gate: scripts/delivery_eval.py (14 checks: SSRF matrix, deliver success/failure/
blocked, over-share no-cross-sector + cited-only + identity-omitted) wired into CI;
+ 5 pytest unit tests.

Verified: delivery_eval exit 0; test_delivery 5 green; live /v1/delivery/preview
200 and /v1/delivery/send to an internal URL correctly 422 (SSRF refused).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…entity-aware rerank

- _contextualized_text: prepend layer/source/date to a memory before embedding so a bare
  fragment carries its situating context (contextual retrieval). Used by the reranker.
- Per-layer exponential temporal decay in _recency_boost behind CORTEX_TEMPORAL_DECAY (default
  off): episodic ages fast (~14d half-life), semantic/decisions slow (~365d), identity-ish layers
  near-flat; amplitude capped at the current max (0.004) so decay only breaks ties.
- Entity-aware ranking: the reranker adds an entity-overlap term from the query plan's entities
  (weights rebalanced 0.6 sem / 0.25 rank / 0.15 entity).

Verified: 5 signal unit tests green; retrieval gate green with the flag OFF (default) and ON
(163 cases, top1 1.0); context_pack gate still green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CORS now echoes browser-extension origins (chrome-/moz-/safari-web-extension://)
  behind CORTEX_ALLOW_EXTENSION_CORS (default on) — the paired Bearer token, not
  CORS, is the auth boundary, so the local extension can call the loopback API.
- POST /v1/pair (maintenance scope): generate a fresh read-scoped MCP token +
  return connection details (base_url, mcp/tools/context endpoints) for pairing a
  browser extension or any local client. Read-only by default.

Verified live: /v1/pair mints a cxm_ read token; that token runs read tools (200)
but is refused write (403); chrome-extension:// CORS preflight is honored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…+ guardrailed)

- Reranker now loads its {sem,rank,ent} blend from CORTEX_RERANK_WEIGHTS_PATH
  (cached once, out of the request path); defaults to the tuned constants when unset.
- scripts/learn_rerank_weights.py: pure-Python pairwise logistic ranking that learns the
  blend from usage feedback (memory_events retrieval_feedback, or a JSON fixture). A held-out
  guardrail refuses to emit unless the learned weights beat the tuned baseline, so a sparse/bad
  signal can never regress ranking. Reads gracefully report 'insufficient_data' -> baseline.

Verified: 7 unit tests (learner upweights the discriminative feature, guardrail emits only on
improvement, insufficient-data fallback, weights-file loading) green; CLI on a synthetic fixture
emits sem-weighted {0.78/0.11/0.11} beating baseline and writes the weights file.

Note: live retrieval_feedback event logging in the request path is a documented follow-up; the
learner + pluggable weights + guardrail are in place and inert until that data exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Manifest V3 WebExtension (Chrome/Edge/Firefox, vanilla JS, no build step, loadable unpacked) under
extension/. Pairs with the local Cortex via a read-only token, pulls a cited context pack
(/v1/context markdown, with use_cortex fallback) through a background service worker, and injects it
into the input box of ChatGPT / Claude.ai / Notion with a '--- Cortex context (cited) ---'
delimiter. Options page (base_url + token + Test connection), popup status, defensive per-site input
resolution with clipboard+toast fallback. This is how web apps get Cortex context without Cortex
exposing a public endpoint.

Verified: manifest.json valid JSON; node --check passes on all four JS files; all referenced files
present.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s guide

- AppState.pairBrowserExtension(): mints a fresh read-only token via /v1/pair and surfaces the
  connection info (BrowserExtensionPairing) for the user to paste into the extension. Compile-verified
  via macos/build.sh; visual surfacing is the founder-facing finish.
- docs/EXTERNAL_INTEGRATIONS.md: complete guide to every outbound surface — MCP (Claude/Cursor/
  VS Code), the universal HTTP + OpenAI/Anthropic/OpenAPI tool API, the Python/TS SDK, the browser
  extension, and delivery/push — with the privacy invariants (cited-or-abstain, treat_as_data,
  sector isolation, scope+trust gates).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…an/temporal-decay ON

- scripts/rerank_eval.py: model2vec-seeded retrieval gate. Forces the real embedder (skips clean
  if unavailable so bare CI doesn't misjudge), drains embed jobs so the vector index is populated,
  and asserts the enabled flags never regress recall vs off (+ a floor), with the full reranker as
  an informational diagnostic. Wired into CI + a pytest wrapper (skips w/o model2vec).
- App backend launch now sets CORTEX_QUERY_PLAN=1 + CORTEX_TEMPORAL_DECAY=1 so shipped users get
  semantic intent + compound-question decomposition + per-layer temporal decay. Both proved
  no-regression under model2vec (recall 1.0 off and on, compound case recalls both facts). The
  reorder-heavy reranker (CORTEX_RERANK) stays off pending a larger graded corpus.

Verified: rerank_eval exit 0 under model2vec (off 1.0, on 1.0, no_regression true); pytest wrapper
passes; retrieval_eval still green under hash (CI default); macOS app compiles with the flags wired.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…instructions)

Status menu gains 'Connect browser extension…' → pairBrowserExtension() mints a read-only token,
copies it to the clipboard, and shows an alert with the base URL + steps. Makes the extension bridge
usable from the app.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nections + extension)

Carries the full outbound pipeline: task-aware retrieval ON (query planning + temporal decay),
the universal /v1/tools + OpenAI/Anthropic/OpenAPI surface, MCP resources/prompts, use_cortex
router + surface presets, SSRF-guarded delivery, browser-extension pairing, and the SDK/extension
artifacts. Docs in docs/EXTERNAL_INTEGRATIONS.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sarptandoven and others added 29 commits July 20, 2026 11:56
…xes in the bundle)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rimary canon, honest states, a11y, copy

Live-screenshot + code audit (10 auditors, 3-vote adversarial verification per finding) of the running
app surfaced 22 confirmed defects across Home, Review, and Ask; all applied and visually re-verified
on the rebuilt app:

Fit/layout: Review's reading column centers in the window (was pinned left with a dead right half);
the default window size clamps to the screen's visible frame with a min floor (fits a 13" Air and a
default-scaled 14"); section-card titles and counts each get a full line with grouped numbers
("1,196 notes · 11,867 memories"); the needs-attention hero drops the decorative portrait.

Simplicity/hierarchy: one wax-red primary per surface. Home's hero card and Wrapped's "Connect an AI"
drop to paper secondary, the detected-tools nudge to ghost; Review's per-card and per-item Approve
drop to secondary (the batch approve or the fix-source action carries the primary); the triple
preamble above Review's sections collapses; Ask loses its redundant third in-flight indicator.

Correctness/safety: the red error icon no longer captions the positive "Memory ready for Ask" line
(attention outranks ready in title/detail, matching icon priority); "Not helpful" becomes an honest
"Forget" with a confirmation dialog; approving a >25-item section arms the same wax-seal confirm
Archive uses; the connected-state "no recall yet" card stops prescribing "Connect an app" when apps
are already connected and says what actually starts recall (asking them questions).

Copy: Ask suggestions keep the leading noun phrase (NLTagger) instead of jamming a verb clause into
"about X?", with a curly-quoted-topic fallback; "Ask about this" quotes its seeded subject; the
health-strip fix button is a short verb phrase ("Fix Cortex Notes") not a duplicated sentence; chips
clamp at 99+ and render "synced Jul 6" instead of a raw ISO date.

Accessibility: the answer prose is ONE selectable Text (continuous VoiceOver read, native selection)
with non-interactive superscript markers; the Review tab badge is announced via accessibilityValue;
every repeatForever loop (ghost stamp/card pulses, loading sweep, refresh spin) now honors
accessibilityReduceMotion and pauses when the scene is inactive; recents "Clear" gets readable ink.

Consistency: the header Connections button joins the design system (CortexButton secondary); stale
doc comments that contradicted the new button roles are corrected.

Compile clean; all screens re-captured live after rebuild and verified against the before shots.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nnection (both directions)

The connect wizard forced up to 7 clicks (Connect another → pick a tile → Continue → run connect →
Next: verify → run test → Finish → Close) even for tools Cortex can wire up with zero user steps.
Now clicking a tool tile IS the connection:

OUTBOUND (ConnectAppWizard, ConnectionsPrivacySheet.swift):
- New activateTool(): for tools Cortex can connect autonomously (mcpConfig with supportsInstall +
  app installed, or mcpDeeplink) a single tile click runs connectIntegration (config-write +
  relaunch, or the native install deeplink) right there, then auto-runs testToolConnection, with the
  lifecycle rendered ON the tile: "Connecting…" → checkmark + "Connected. <tool> restarted with your
  memory" (or an "open it once to finish" note for deeplink tools it can't probe).
- Tools that genuinely need the user in the loop (remote-connector key, CLI paste, sign-in) fall
  through to their existing single connect screen — no Pick/Continue detour.
- The PICK/CONNECT/VERIFY/DONE step rail is hidden on the pick grid (it only appears once a manual
  flow starts); the pick-step "Continue" primary is removed; step-1 title is now "Click a tool to
  connect it". The manual connect/verify steps and the honesty gate (no Done without a real
  connect/pass) are unchanged.

INBOUND (Home, ModelTab.swift):
- New ImportChatsQuickCard on Home, right under the outbound hero: one chip per service (ChatGPT,
  Claude, Perplexity, Notion). A single click opens that service's secure sign-in window via the
  same AIChatSessionImportView the Connections sheet hosts, and the import runs itself (chat vendors
  auto-start on sign-in; Notion exports the workspace). Direct builds only (sandbox can't host the
  embedded importer), same gate used elsewhere. Previously this one-click importer existed but was
  buried four levels deep in the Connections sheet and absent from Home entirely.

Compiles clean; the redesigned no-stepper grid + the Home import chips verified live on the running
app. The connect action reuses the existing, tested connectIntegration/testToolConnection path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ncl. ChatGPT/Claude web)

activateTool previously only auto-ran config + deeplink tools; clicking a browser connector
(ChatGPT, Claude web, Perplexity, Grok, Gemini) or a CLI/httpAPI tool fell through to a manual
screen. Now one tile click runs the maximal live action for EVERY kind via connectIntegration and
shows the outcome on the tile:
- mcpConfig: write config + relaunch + probe -> "Connected. <tool> restarted with your memory."
- mcpDeeplink: fire install URL -> "Opened <tool>. Approve the prompt there."
- remoteMCP (ChatGPT/Claude web/...): mint hosted token, copy link+key, open the tool's connector
  settings -> "Link and key copied, and <tool>'s connector settings opened. Paste them there and
  Save." The paste in the browser is physically unavoidable (the web app can't reach your local
  Mac), so Cortex does everything IT can in one click and states the one remaining step honestly.
- cliCommand: copy the connect command; httpAPI: copy endpoint + key.
- remoteMCP when not signed in: the hosted connector genuinely needs a Cortex account, so the tile
  says so ("Sign in to Cortex first, then click <tool>") instead of a silent no-op.
- referenceOnly: opens the tool, honest that no live path exists yet.

Compiles clean. Reuses the existing tested connectRemoteMCP/copyCLICommand/copyHTTPAPIDetails paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… near the top

- The connect wizard's step-1 subtitle said "Pick the tool you want to give access to your reviewed
  memory" — the old pick-then-continue model. Now: "Click a tool to connect it. Your memory stays
  here; the tool reads it on demand." matching the one-click tiles.
- Reordered IntegrationCategory so "Browser assistants" (ChatGPT, Claude web, Perplexity, Grok,
  Gemini) renders right after the fully-automatic "One-click tools", before the coding tools — so
  the services most people ask for are visible without scrolling. allCases order drives display
  order consistently everywhere it's iterated.

Compiles clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y check on launch

CortexUpdateFeedURL was an empty string and checkForUpdates() only ran from a manual button in
Advanced settings, so shipped users could never learn a new build exists (fatal for weekly
iteration). Now:
- Info.plist CortexUpdateFeedURL points at the live feed (https://api.signindoppl.com/downloads/
  latest.json, deployed and serving the current release manifest).
- bootstrap() runs scheduleAutomaticUpdateCheck(): at most once per 20h (persisted timestamp),
  never blocks launch, silent unless a newer build exists; updateStatus then surfaces the update in
  Connections & Privacy with the existing one-click download.
UserDefaults override still wins over the bundled URL, so self-hosted/dev feeds keep working.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tiles, unstick restore, motion gates

From the elevation audits (agent-verified where the session limit allowed; personally verified
against the code otherwise):

Correctness:
- Quick capture OCRs the display the user is ON (screen under the mouse), not CGMainDisplayID:
  on multi-monitor setups the hotkey captured the wrong screen entirely. [agent-confirmed x2]
- The live-activity pill re-arms its auto-dismiss when the timer fires mid-hover (was: hover at
  the wrong moment made the pill permanent). [agent-confirmed]
- Restore flow's "Welcome back" gains its missing exit: onFinish was declared but never called,
  dead-ending returning users on the sheet. New "Start using Cortex" primary calls it.

Honesty (canon: never claim success before the async result), both in today's new tile code:
- Deeplink tiles (Cursor/VS Code) no longer mark .connected before the user approves inside the
  tool; they show an attention note "Approve the prompt there to finish."
- ChatGPT/Claude-web tiles no longer claim "Link and key copied and settings opened" synchronously
  while connectRemoteMCP's Task is still minting; they describe the in-progress action instead.

Simplicity/consistency:
- The Connections sheet's duplicate wax-red "Connect an app" demotes to paper secondary (the
  START HERE hero keeps the surface's one primary); two stale wizard help texts now describe the
  one-click grid instead of "copy its connection and test".
- Quick-panel footer buttons stop clipping "Capture screen" (min-width instead of fixed 72pt).

Accessibility:
- Quick-panel ask shimmer and the live-ticker breathing dot honor Reduce Motion.
- Em dash removed from the Downloads auto-import caption.

Compiles clean; braces verified per file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…o-update + 31 UI fixes)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ight + polish backlog)

Findings from a fan-out audit (copy-sweep + delight-motion + heavy-views + onboarding-restore),
each adversarially verified against the current source before applying. One finding was verifier-
rejected (a low-visibility .help tooltip) but applied anyway for jargon consistency.

Onboarding / accounts (App Store 4.8 + honesty):
- Restore "Welcome back" sign-in no longer paints normal progress/success ("Signing in…", "Signed
  in.", "Sign-in cancelled.") as a gold WARNING banner — a new severity classifier keyed on
  cloudAuthBusy/isSignedIn maps in-flight→info, live session→success, real failures→warning.
- Sign in with Apple now appears in the FIRST-RUN restore step, not just Settings: an Apple-origin
  account could not be restored during onboarding (the browser provider list excludes Apple by
  design). Extracted a shared CortexAppleSignInButton + AppleSignInSupport used by both surfaces, so
  Apple is offered wherever GitHub/Google are and the two can never drift. Self-hides on ad-hoc builds.
- 'Use it' step no longer shows two wax primaries: once a tool is connected the footer Continue takes
  the primary, so the in-content "Connect another tool" relaxes to secondary.

Honesty:
- ImportDiff "Save PNG" now confirms success ("Saved") and surfaces write failures via NSAlert instead
  of swallowing them with try? (a failed save looked identical to a successful one).

Copy (developer jargon out of user-facing strings):
- "Reset Token"→"Reset access", "N active MCP tokens"→"N active AI tool connections", "Token
  copied"→"Key copied", "Paste this token"→"Paste this key", and the reset help text de-jargoned.

Delight / energy:
- Tab content cross-fades (0.18s) in step with the sliding indicator instead of hard-cutting.
- Reduce Motion now honored by the edge-glow breathe, HUD progress sweep/shimmer, onboarding hero
  mark, distill mark, and ambient mote field (static frames, no repeatForever / display link).
- Live-activity surfaces settle (glow off, HUD hidden) when the app resigns active, so no repeating
  animation keeps drawing in the background.

Scale:
- ImportDiff fact + cortex-only card lists use LazyVStack so a large export builds rows on scroll.
- Memory Wrapped recall counts use grouped formatting (1,234 not 1234), matching Home.

Also: FOUNDER_GO_LIVE.md refreshed to true current state (backend already live; remaining founder
actions are the OAuth-app registrations + GitHub Device Flow enable).

Compiles clean; braces verified per file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-in, honesty, delight, scale)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…able soon" dead tiles)

A dead-control sweep across all 11 UI surfaces (adversarially verified) found ZERO no-op / broken
buttons — every control already does real work. The only user-visible "not functional right now"
elements were the "Available soon" tiles on the sign-in-sources shelf: managed-OAuth sources whose
provider isn't configured on the broker and that have no pasted-token fallback (Gmail, Google Drive,
Outlook in this build). They can't be connected, so they showed a calm-but-dead "Available soon".

The Connections LIBRARY grid already excludes these (browsableConnectors drops isUnconfiguredOAuth)
and names them in its footnote pointing to file/export import. The sign-in shelf was the last place
they still appeared. Filter signInConnectors to isConnectable || isConnected so the two surfaces are
consistent: every visible sign-in tile now leads to a real connection (Notion, GitHub, Linear,
Readwise, Limitless stay; the unconfigured Google/Microsoft OAuth sources are hidden until wired).

Verified: full-pipeline e2e 27/27 PASS (entrance -> organization -> cited retrieval); compiles clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prepare the public repo for open download + use. No product code changed.

Removed (internal-only, not for a public repo):
- Founder ops + launch playbooks and runbooks (SSH/server steps, token shopping
  lists, notarization/incident/KEK runbooks, OAuth setup walkthroughs).
- Business strategy + roadmap decks (YC plans, vision/strategy, 10k roadmaps,
  moonshots, expansion phases).
- Internal work logs (session/resume checkpoints, holistic audits, launch-readiness
  findings, copy reviews, first-100 beta packets and issue templates).
- Stale App Store build artifacts (a committed .pkg, entitlements, build report).

Redacted PII/infra from everything that remains:
- Personal emails -> support@trydoppl.com (site) / example.com (test fixtures).
- Production server IP removed with the ops docs that carried it.
- Founder local username path -> generic in the eval fixtures.

Kept: all product code, the technical docs (architecture, CMP protocol, vault
format, MCP integration, trust controls, imports, encryption design), and the
CI gates. Updated check_docs_current.py / ops_readiness_check.py to match the
trimmed doc set; retrieval + adaptation eval gates still pass; no live secrets
were present (the only key-shaped strings are fake redaction-test fixtures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… green the doc-currency gate

- New public-facing README: icon/badge banners (download, macOS 13+, notarized, SwiftUI, FastAPI,
  SQLite+sqlite-vec, MCP, local-first), a hero, the Connect/Review/Ask/Control loop, a mermaid
  architecture diagram, what's-inside, install, build-from-source, a docs index (kept docs only),
  privacy, and an honest source-available license note.
- Cleared two long-standing check_docs_current forbidden-phrase errors (comment-only rewordings:
  "open loops" -> "still to do" in ReviewTab; "memory layer" -> "memory source" in OnboardingView),
  so the doc-currency CI gate is green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Download links, checksums, and latest.json now serve build 51 (one-click connect both
directions, auto-update, UI elevation, only-functional connect surfaces).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sor/affiliation row

- Reworked the README badge banner into the University of Waterloo black + gold theme (official
  Waterloo gold #FDD54F), replacing the generic tech badges with the affiliation/sponsor/license row
  the project should lead with: University of Waterloo, Faculty of Engineering, Academic Research,
  Sponsored by Composio, and License: MIT. Kept a themed product/tech row (Download, macOS 13+,
  Notarized, MCP, Works with Claude, Local-first) beneath it.
- Added a real MIT LICENSE file (© 2026 Doppl) and switched the README License section from
  "source-available" to MIT.
- Added an Affiliations & sponsor block with the real Composio logo (their GitHub org mark).

All badge + logo URLs verified 200. Doc-currency gate green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… together

Each affiliation/sponsor/license badge now carries a distinct hue on a shared black label rail
(Waterloo gold / teal / magenta / indigo / green), and the product row uses each tool's brand color
(Swift orange, FastAPI teal, SQLite blue, MCP violet, Claude clay, local-first green) instead of a
uniform black+gold that ran together side by side.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d, spaced)

shields.io can't do borders or a chosen corner radius, so the affiliation/sponsor/license row now
uses custom SVG "button" badges under .github/badges/: each has rounded corners (r=10), a 1.5px
border in a darker shade of its own accent, padded/isolated text, and baked-in margins so the
buttons sit apart instead of running together. Distinct fills kept (Waterloo gold, Engineering teal,
Research magenta, Composio indigo, MIT green); text auto-contrasts (black on gold, white elsewhere).
Rendered + eyeballed before commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…op shadow)

Rebuilt the custom SVG button badges with real depth: a vertical gradient (light top -> base ->
darker bottom), a glossy top highlight, a beveled 1px top-edge, and a soft drop shadow so each button
lifts off the page instead of sitting flat. Bumped size (46px tall, 15.5px bold text, more padding)
and the README render height to match. Rendered + eyeballed before commit; distinct accents kept.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Researched how premium README badges look in 2026: the modern standard is shadcn/ui-styled buttons
(shieldcn.dev / jal-co/shieldcn — Vercel OSS), not glossy Web-2.0 gradients. Regenerated the
affiliation/sponsor/license row as shieldcn "branded" buttons — a clean dark button with a distinct
colored tag per item, Inter font, proper radius, subtle and flat. Each SVG is self-contained (text
vectorized to paths, no external refs, no runtime dependency) and renders identically on light and
dark GitHub themes, so they're committed under .github/badges/ and served locally.

Waterloo gold · Engineering teal · Research magenta · Composio indigo · MIT green. Rendered on both
backgrounds and eyeballed before commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ar-style, retina PNG)

The vectorized-SVG badge text read as fake. Rebuilt the row as Linear/Vercel-style status pills
rendered with REAL Helvetica Neue Medium (rasterized @3x via rsvg + system fonts, referenced at 1x
so they're retina-crisp): dark #161B22 capsule, #30363D hairline border, full-pill radius, a glowing
accent dot per item (Waterloo gold, Engineering teal, Research magenta, Composio indigo, MIT green),
0.1px tracking. Transparent background; verified on both GitHub themes before commit. PNGs are
self-contained in .github/badges/ (no external service).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nd lockup)

Replaced the pill badges with the older NVIDIA corporate-badge lockup the founder asked for:
sharp-cornered black block carrying the name in heavy white Helvetica Neue Bold caps, a solid
NVIDIA-green (#76B900) band beneath with a black bold caps descriptor (EST. 1957 / WATERLOO /
PROJECT / SPONSOR / MIT LICENSE), and a thin green frame so the black block separates from
GitHub's dark background. One uniform green across the set (the NVIDIA way), real system-font
typography rasterized @3x for retina. Verified on both GitHub themes before commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ext)

Focused pass on the NVIDIA-style set: all five badges now share IDENTICAL dimensions (256x53) like a
real partner-badge program, instead of five different widths. Text widths were measured pixel-exact
(rendered @4x, alpha-bbox) rather than estimated, the green band slimmed to a 13px strip with a
darker #5E9400 bottom edge, a 10% white top hairline gives the black block a machined edge, and
tracking tightened (14px/1.2 main, 8.5px/2.2 band). Rendered on both GitHub themes and eyeballed
before commit; @3x retina PNGs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rtical stack)

The stacked black-block-over-green-band lockup read as two disconnected slabs and, at 53px, was far
too tall for a README badge row. Rebuilt as ONE intact unit: a compact 30px horizontal capsule where
the black label segment and the NVIDIA-green tag segment sit side by side under a single 4px-radius
clip (classic badge structure, premium execution) — real Helvetica Neue Bold caps, pixel-measured
segment widths, a subtle top hairline, and a soft outline so the capsule holds together on both
GitHub themes. Rendered + eyeballed on dark and light before commit; @3x retina PNGs referenced at
their native 30px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main carried a reduced "beta release" cut made from an older snapshot; it removed live product code
the shipping app requires (the CMP/SMP envelope, OAuth broker, query planner, Constellation and E2EE
sources, bundled sample notes, the privacy manifest) alongside its docs cleanup. This branch is the
notarized, shipping v0.2.0 product line and has already had its own public cleanup pass (verified:
no internal runbooks, no server addresses, no secrets; all README links resolve). Taking this
branch's content wholesale and joining histories so main fast-forwards without a force push.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sarptandoven
sarptandoven merged commit 89c2c6a into main Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants