FIX-KYLE-COMPOSIO-QBO-AUTH-20260714 — Pin connected accounts into Kyle sessions
- Files modified: supabase/functions/kyle-composio/index.ts, CHANGELOG.md
- Fix: Kyle now passes the actual ACTIVE Composio connected-account ids into each direct-tools router session, instead of only exposing toolkit tool definitions. This addresses QuickBooks reads failing with
No active connection found for toolkit(s) 'quickbooks' in this sessioneven when QuickBooks is connected for the Kyle Composio user. - Safety: Logs only toolkit names and pinned account count, never account secrets or tokens.
PROMPT-KYLE-3.3-WORLDVIEW-20260707 — Standing worldview brief + operator profile
- Files created: supabase/migrations/(kyle_worldview).sql, src/routes/cockpit.kyle.profile.tsx
- Files modified: supabase/functions/kyle-agent-step/index.ts (buildBootBrief helper prepended to every run seed; snapshots pinned onto agent_runs), src/integrations/supabase/types.ts (auto)
- Schema: new table
kyle_operator_profiles(per user, per workspace: comms_style, working_hours_tz, working_hours, risk_appetite, current_okrs, notes). New MVkyle_worldviewper workspace: P0/P1 incidents, top open atlas signals, freshness health, unhealthy sites, active/parked runs, today's spend. pg_cronkyle_worldview_refresh_60srunskyle_worldview_refresh()every minute (CONCURRENTLY, logged in mv_refresh_log). RPCs:kyle_worldview_get(workspace),kyle_operator_profile_get(workspace,user),kyle_standing_rules_active(). Added columns toagent_runs:system_prompt_version,worldview_snapshot,operator_snapshot(pinned per run for reproducibility). - Orchestrator: on run open,
kyle-agent-stepbuilds a boot brief (worldview + operator + standing rules), snapshots it ontoagent_runs, and prepends<worldview>…</worldview>\n<operator>…</operator>\n<standing_rules>…</standing_rules>to the seed user message for both monolith and durable modes. Hard caps: worldview 3200 chars, operator 800, standing_rules 1600 — well under the 800-token budget. - UI:
/cockpit/kyle/profile— per-user editable profile (comms style, TZ, working hours, risk appetite, OKRs JSON, notes) that Kyle reads on every run open.
PROMPT-MBO-DOPPLER-SYNC-20260618-001 — Doppler 1:1 project sync for every Block/Site
- Files created: supabase/migrations/(timestamp)_doppler_sync_001.sql, supabase/functions/dopplerSync/index.ts, src/lib/dopplerSync.functions.ts, src/components/sites/DopplerTab.tsx, src/routes/doppler-sync.tsx
- Files modified: supabase/config.toml (verify_jwt=false for dopplerSync), src/components/sites/SiteTabs.tsx (add "doppler" tab), src/routes/sites.$siteId.tsx (wire DopplerTab), src/routes/sites-manager.tsx (add doppler_project_slug to CRITICAL jig fields → item #13), CHANGELOG.md
- Schema: ALTER public.sites ADD doppler_project_slug text, doppler_project_id text, doppler_synced_at timestamptz. New trigger
tg_sites_doppler_autosync(AFTER INSERT) fires pg_net.http_post to the dopplerSync edge function with action=create_project — best-effort, never blocks the insert. - Edge function
dopplerSync: three actions —create_project(POST Doppler /v3/projects → ensure dev/stg/prd configs → write slug+id+timestamp back to sites),bulk_seed(sweeps every site where doppler_project_slug IS NULL),check_status(verifies project + lists configs with live secret counts). Doppler bearer token read fromDOPPLER_API_KEYenv var — never logged, never returned. - UI: New
/doppler-syncbulk seed page — total / synced / missing stat cards, "Seed All Missing Projects" button, live per-site ✅/❌ progress, final summary. New "Doppler" tab on Site detail — status banner (Synced/Drifted/Not synced/Unknown), project info card, configs table with secret counts, three actions: Sync Now, Check Status, Open in Doppler. - Verification: Migration applied; edge function deployed; build clean.
- Commit: (Lovable fills in)
PROMPT-MBO-20260618-BLOCK-DEPENDENCY-TAB-001 — Block Dependency & Readiness tab on Site detail
- Files modified: supabase/migrations/(timestamp)_block_dep_001.sql, src/components/sites/SiteTabs.tsx, src/routes/sites.$siteId.tsx, src/components/sites/DependenciesTab.tsx (new), CHANGELOG.md
- Schema: ALTER public.sites ADD fed_by text[] NOT NULL DEFAULT '{}', feeds_into text[] NOT NULL DEFAULT '{}', linked_block_codes text[] NOT NULL DEFAULT '{}', wave text NULL, layer text NULL. GIN indexes on the three arrays. No RLS / GRANT changes (existing sites policies apply).
- UI: New "Dependencies" tab in Site detail tab bar (between Tasks and Deployments). Readiness banner pinned at top (green = ready / amber = blocked / neutral = no upstream). Three sections — Consumes (fed_by), Consumed by (feeds_into), Peers (linked_block_codes, hidden when empty). Each row: status dot, name, @code badge, wave/layer chips, status chip. Blocking rows in Consumes get amber border-left + BLOCKING flag. Rows resolve via
sites.codelookup; unknown codes render asunknownand are non-navigable. Known rows link to/sites/$siteId. - Verification: Migration applied; types regenerated with new array columns; tab wired into SiteTabs union + route switch.
- Commit: (Lovable fills in)
APP-CI-003 — Promote competitors to Sites (not Atlas)
- Files modified: src/routes/competitive-intel.tsx, src/routes/sites.index.tsx, src/components/sites/SiteCard.tsx, CHANGELOG.md
- Schema: ALTER public.sites ADD site_type (internal/external/holding/marketing/partner, default internal, CHECK), backfill NDA → holding. ALTER public.competitive_intel ADD promoted_site_id uuid FK → sites(id) ON DELETE SET NULL. Drop/recreate sites_status_check adding 'external'. New SECURITY DEFINER RPC
public.promote_intel_to_site(...)— atomic site insert + intel link, idempotency guard via SELECT…FOR UPDATE, workspace write check viapublic.can_write_workspace(p_workspace_id), EXECUTE granted to authenticated. - Frontend: Promote button now calls RPC instead of writing to atlas_signals; toast reads "Promoted to Sites"; button shows "In Sites" + disabled when promoted_site_id set. Sites page gets a Type filter row (All · Internal · External · Holding · Marketing · Partner). SiteCard gets a colored type badge alongside the status chip.
- Cleanup: Deleted leftover Origin HQ atlas_signals row (most recent by created_at, by title ILIKE 'Origin HQ%').
- Verification: Migration applied. RPC + GRANT live. Origin HQ atlas row removed. Build clean.
- Commit: (Lovable fills in)
APP-CI-002 — Competitive Intel enrichment pipeline (scrape → tech detect → AI analysis)
- Files created: supabase/migrations/(timestamp)_app_ci_002.sql, supabase/functions/ci-pipeline/index.ts, supabase/functions/ci-pipeline/deno.json
- Files modified: src/routes/competitive-intel.tsx, supabase/config.toml, CHANGELOG.md
- Schema: ALTER public.competitive_intel ADD pipeline_status (idle/queued/running/done/error, CHECK + index), pipeline_error text, tech_stack jsonb, ai_analysis jsonb, raw_payload jsonb (no-op — already from CI-001), enriched_at timestamptz, enrichment_model text.
- Edge function
ci-pipeline: async pipeline (EdgeRuntime.waitUntil) — scrape source_url (15s timeout, UA Mozilla/WorldPortBot) → header+HTML tech-stack fingerprinting (Next.js, Gatsby [real fingerprints — not _app.tsx], WordPress, Webflow, Framer, React, Vue, Cloudflare, Vercel, AWS, Stripe, Intercom, Segment, GA) → Anthropic claude-sonnet-4-6 (configurable via AGENT_MODEL env) structured JSON analysis (snapshot, threat level, strengths/weaknesses, WorldPort opportunity, recommended_category) → admin write-back. Only fillscategory/summarywhen blank. - Auth model (FIX from prompt review): function does NOT use
requireServiceRole. Instead verifies caller's user JWT withadmin.auth.getUser(token), then re-queries the row through an RLS-scoped anon client using the user's bearer — existing SR-006 policies do the authorization. Admin client used only for the privileged write-back. Front end sendssession.access_token, never anon/service keys. verify_jwt = falseset insupabase/config.toml(notdeno.json) — function reachable but does its own auth check.- Frontend: Pipeline status chip column, Analyze/Re-run button (disabled if no source_url or running), expand row → renders ai_analysis sections + tech stack chips + enrichment timestamp/model. Auto-triggers pipeline on new entry creation when source_url provided. Smart polling (every 2s, stops on terminal state or 60s cap). All row-action buttons
stopPropagationso they don't toggle the expand panel. - Secrets: ANTHROPIC_API_KEY (present).
- Verification: Migration applied (pipeline_status column + index live). Build clean.
- Commit: (Lovable fills in)
APP-CI-001 — Competitive Site Intelligence (ported from mbo-entityos-shell prompt)
- Files created: supabase/migrations/(timestamp)_app_ci_001.sql, src/routes/competitive-intel.tsx, docs/agent-platform/APP-CI-001-RUNBOOK.md
- Files modified: LOVABLE_RULES.md (Serial Dictionary: added CI prefix), CHANGELOG.md
- Schema: public.competitive_intel — workspace_id NOT NULL → workspaces, optional site_id → sites, optional promoted_atlas_signal_id → atlas_signals. Serial prefix
CIvia canonical mbo_set_serial_tg('CI'). Registered in serial_registry. - RLS (SR-006 dual-clause): ci_select_members (SELECT via is_workspace_member), ci_insert_writers / ci_update_writers / ci_delete_writers (via can_write_workspace). GRANTs to authenticated + service_role issued before RLS enable (Pitfall #11).
- Route:
/competitive-intel— list + filter by site + create modal + promote-to-Atlas action. Uses direct supabase client (matches research-vault.tsx pattern) and worldport primitives. - Promotion: creates an
atlas_signalsrow (kind=competitor_intel, severity=info, source_table=competitive_intel, source_id=row.id) and back-links viapromoted_atlas_signal_id. - Port deltas from original prompt: dropped proposed
mboschema (lives inpublic), dropped proposed edge function (browser-side supabase call instead, RLS-enforced), serial formatCI-YYYYMMDD-NNN(notPROMPT-MBO-SITE-INTEL-...), no sidebar nav wired (URL-only for now). - Verification: 5 gates pass — table exists, RLS=true, 4 policies (SELECT/INSERT/UPDATE/DELETE), set_serial + touch_updated_at triggers, CI prefix in serial_registry.
- Commit: (Lovable fills in)
APP-012 — Acceptance runbook + formal pack closure
- Files created: docs/agent-platform/APP-012-ACCEPTANCE-RUNBOOK.md
- Files modified: CHANGELOG.md, LOVABLE_RULES.md (closing note appended at end)
- Pack closure: BP-AGENT-PLATFORM-PACK-001 formally closed. Eight changes shipped (APP-001, APP-002-REVA, APP-002.5-REVA, APP-003-REVA, APP-004, APP-005, APP-009+010-UNIFIED, APP-RECONCILE-001). Four prompts deferred to TheCodex Block (APP-006, APP-006-REVA, APP-007, APP-008) on Frame+Settings architecture grounds. One dependent prompt deferred (APP-011 — needs run ledger data).
- Verification: 16-check structural audit + 6 functional smoke tests on 2026-06-15. All five primary DB migrations + corrective APP-RECONCILE-001 verified structurally complete and functionally sound. Partial unique index enforcement on agent_prompt_packs confirmed for both INSERT and UPDATE.
- Rules added/updated this pack: SR-006 (codified with named platform-global agents + RLS pattern + GRANT subsection + anti-patterns), SR-007 (Frame+Settings — codified during Kyle debug), SR-010 (audit-before-build standing law), Pitfall #11 (GRANT ordering reinforced), Pitfall #12 (filename auto-generation codified), Serial System (rewritten to canonical mbo_set_serial_tg pattern with prefix registry including MEM and APPK)
- Findings queued for BP-RLS-FOLLOWUP-001: 5 open (none blocking), 3 resolved during execution by APP-RECONCILE-001
- Architecture documents ratified: WD-LOVABLE-001 (Winding Down Lovable), SU-WORLDPORT-001 (Spinning Up TheCodex Block) — both incorporate four-pillar North Star foundation: Evidence Trail, Provenance Attestation, Policy Enforcement, Auditor Surface
- Commit: (Lovable fills in)
APP-RECONCILE-001 — Reconcile shipped agent_* tables with codified rules
- Files created: supabase/migrations/20260615180110_5e224552-67d7-47b2-9bb7-52013ab98fdf.sql (prompt-specified 20260615120000_app_reconcile_001.sql per Pitfall #12)
- Files modified: LOVABLE_RULES.md (SR-006 council/council_seat reconciliation — 4 lines), CHANGELOG.md
- Schema corrections:
- agent_tool_calls RLS: dropped agent_tool_calls_select and agent_tool_calls_write; created agent_tool_calls_ws_read (SELECT, NULL OR member), agent_tool_calls_ws_write (INSERT, NOT NULL AND writer), agent_tool_calls_ws_update (UPDATE, NOT NULL AND writer in USING + WITH CHECK) — matches SR-006 dual-clause verbatim and parity with agent_runs/agent_memory_entries/agent_prompt_packs
- agent_runs trigger: migrated trg_arn_serial → set_serial using canonical mbo_set_serial_tg('ARN'); produces ARN-GLOBAL-YYYYMMDD-NNN (was ARN-MBO-YYYYMMDD-NNN). Zero data risk — agent_runs has 0 rows.
- tg_serial_arn_fn function: dropped (no remaining dependents per pre-DDL audit)
- Rules update: LOVABLE_RULES.md SR-006 council members documented as
kind: council(matches production data). Brian's decision (option A) — rules describe reality, not data correction. - BP-RLS-FOLLOWUP-001 findings resolved by this migration:
- Finding #1 (agent_tool_calls missing UPDATE policy) — RESOLVED
- Finding (agent_tool_calls SR-006 incompliance — SELECT missing NULL clause, INSERT missing NULL guard) — RESOLVED
- Finding (agent_runs legacy trigger pattern) — RESOLVED
- Rules cited: SR-006 (full + GRANT subsection), Serial System (canonical mbo_set_serial_tg), SR-010 (audit-before-build)
- Verification: build · typecheck · 5 verify SQL outputs pasted
- Commit: (Lovable fills in)
APP-012 — Acceptance runbook + formal deferral to TheCodex Block
- Files created: docs/agent-platform/APP-012-ACCEPTANCE-RUNBOOK.md
- Files modified: CHANGELOG.md, LOVABLE_RULES.md (one closing note added)
- Pack closure: BP-AGENT-PLATFORM-PACK-001 formally closed. Six DB migrations + one unified UI amendment shipped (APP-001, APP-002-REVA, APP-002.5-REVA, APP-003-REVA, APP-004, APP-005, APP-009+010-UNIFIED). Four prompts deferred to TheCodex Block (APP-006, APP-006-REVA, APP-007, APP-008) on Frame+Settings architecture grounds. One dependent prompt deferred (APP-011 — needs run ledger data).
- Rules added/updated this pack: SR-006 (codified), SR-007 (Frame+Settings, codified during Kyle debug), SR-010 (audit-before-build), Pitfall #11 (GRANT ordering, reinforced), Pitfall #12 (filename auto-generation), Serial System (canonical mbo_set_serial_tg), GRANT and authenticated-write policy subsection under SR-006, prefix registry updated with MEM and APPK
- Findings queued for BP-RLS-FOLLOWUP-001: 8 items, none blocking
- Architecture documents ratified: WD-LOVABLE-001 (Winding Down Lovable), SU-WORLDPORT-001 (Spinning Up TheCodex Block) — both incorporate four-pillar North Star foundation
- Commit: (Lovable fills in)
APP-009+010-UNIFIED — Agents UI amended for SR-006 platform-global visibility
- Files modified: src/routes/agents.index.tsx (SR-006 dual-clause query), src/routes/agents.$agentId.tsx (Prompt Packs + Memory tabs), CHANGELOG.md
- Files created: none
- Schema migrations: none
- Architectural decisions: APP-009 (read-side server function layer) COLLAPSED INTO APP-010 (UI). Reasoning: existing codebase uses direct Supabase + React Query for reads, not createServerFn (which is used for writes only per agentWrites.functions.ts). Adding a new read-side server function layer would create an inconsistent pattern. Frame+Settings (SR-007) honored — UI renders platform-global agents identically to workspace-scoped agents.
- Visibility: Kyle, Aria, Scanner, Schema Auditor, Regression Watcher now appear in /agents list per SR-006 dual-clause query
- New tabs: Prompt Packs (queries agent_prompt_packs by agent_id), Memory (queries agent_memory_entries by agent_id) — both with empty states; agent_runs and agent_tool_calls panels DEFERRED to TheCodex Block
- Rules cited: SR-006, SR-007, SR-010
- Verification: build · typecheck · visual confirmation of Kyle's row and PROMOTED prompt pack
- Commit: (Lovable fills in)
APP-005 — agent_prompt_packs registry — STRUCTURALLY COMPLETE
- Files created: supabase/migrations/<lovable-auto-generated>_agent_prompt_packs.sql (prompt-specified 20260615000105_agent_prompt_packs.sql per Pitfall #12), docs/prompts/agents/kyle/v1/SYSTEM_PROMPT.md
- Files modified: LOVABLE_RULES.md (prefix registry adds APPK), CHANGELOG.md
- Schema migrations: creates agent_prompt_packs (APPK serial via mbo_set_serial_tg, DRAFT/SHADOW/CANARY/PROMOTED/DEPRECATED/ARCHIVED lifecycle with partial unique index for one PROMOTED per family, SR-006 RLS, Pitfall #11 GRANT ordering, Realtime publication, Kyle v1 PROMOTED backfill)
- Rules update: LOVABLE_RULES.md prefix registry documents APPK (Serial System rule line 121 enforcement caught by Lovable rules-conflict gate — exactly what that gate is for)
- Serves platform-global agents: Kyle, Aria, Scanner, Schema Auditor, Regression Watcher
- Coexistence: independent of prompts (89 rows, Brian work queue), prompt_pack_documents (3 rows, PACK serial), prompt_pack_items (0 rows, extraction bridge) — disambiguation in migration comment
- Rules cited: SR-006, Serial System (incl. prefix registry), Pitfall #11, Pitfall #12, SR-010
- Verification: V1-V6 and V8 passed. V7 deferred (mbo_serial_counters permission in test environment — index structurally correct per V3). Lint failure is pre-existing repo-wide (~9,055 errors, queued as finding for BP-RLS-FOLLOWUP-001, not introduced by APP-005)
- New findings queued for BP-RLS-FOLLOWUP-001: (1) mbo_set_serial_tg produces {PREFIX}-GLOBAL-{YYYYMMDD}-{SEQ3} not {PREFIX}-{YYYYMMDD}-{SEQ3} (rules doc wrong format — Kyle's APPK serial confirms 'GLOBAL' segment is present); (2) V7 partial-unique-index enforcement could not be tested due to mbo_serial_counters role permission; (3) Lint gate has been non-functional through APP-001 through APP-005 — 9,055 errors with 8,974 auto-fixable suggesting missing .eslintignore or out-of-date --fix
- Commit: (Lovable fills in)
APP-004 — agent_memory_entries pgvector table
- Files created: supabase/migrations/<auto-generated-by-lovable>_agent_memory_entries.sql (prompt-specified filename was 20260615000104_agent_memory_entries.sql; Lovable Cloud migration service rewrote per Pitfall #12 below)
- Files modified: CHANGELOG.md, LOVABLE_RULES.md
- Schema migrations: creates agent_memory_entries — MEM serial via mbo_set_serial_tg unified pattern, vector(1536) embedding, HNSW cosine index (m=16, ef_construction=64), 4 B-tree indexes, SR-006-compliant RLS (ws_read/ws_write/ws_update, no svc_all, no DELETE), supabase_realtime publication membership, search_agent_memory SECURITY INVOKER similarity search function
- Serves platform-global agents: Kyle, Aria, Scanner, Schema Auditor, Regression Watcher (per SR-006 registered list)
- Rules cited: SR-006 (full section), Serial System (canonical mbo_set_serial_tg), Pitfall #11 (GRANT ordering), SR-010 (discovery complete)
- Discovery finding 1 — anon ACL behavior: Lovable Cloud sets permissive default ACLs at the public schema level. anon/authenticated/service_role receive full DML on every new public table by default. Explicit GRANTs in migrations are decorative in this environment. RLS is the sole access gate. SR-006's "NO grant to anon" rule is enforced via RLS policy absence (no policy covering anon → RLS rejects anon), not via GRANT absence. Codified in this prompt as a note appended to SR-006's GRANT subsection.
- Discovery finding 2 — filename auto-generation: Lovable Cloud migration service auto-generated the filename rather than using the prompt-specified name. This is the second occurrence (APP-003-REVA was the first). Codified in this prompt as new Pitfall #12.
- Verification: build · lint · typecheck · V1/V2/V3/V4/V6/V7 verbatim outputs verified · V5 explained via has_table_privilege audit (Check 4 returned all true)
- Commit: (Lovable fills in)
APP-003-REVA — Enrich agent_tool_calls additively
- Files created: supabase/migrations/20260614214658_d485957c-a6e0-4194-a464-c6474c9c440f.sql
- Files modified: CHANGELOG.md
- Schema migrations: added run_id, risk_class, proposed_action_id, lifecycle_status, result_summary, completed_at, and retry_count to agent_tool_calls; added four partial indexes; enabled Realtime
- Edge functions: none
- Verification: all seven additive columns present; four idx_atc_* indexes present; Realtime publication confirmed; all 37 existing rows retain serials
- Commit: feat: APP-003-REVA agent_tool_calls additive enrichment (TCALL convention honored)
- Notes: Option A investigation replaced the pack's proposed ATC prefix with the established production TCALL / TCALL-GLOBAL convention. The existing set_serial trigger calling mbo_set_serial_tg('TCALL') remains authoritative; no serial column, backfill, function, trigger, or toolkit auto-derivation work was added.
FIX-KYLE-COMPOSIO-ENV — Make optional Kyle environment values non-fatal
- Files created: none
- Files modified: supabase/functions/kyle-composio/index.ts, CHANGELOG.md
- Outcome: COMPOSIO_USER_ID falls back to user_0qev3c; missing KYLE_MBO_API_KEY returns a structured configuration error before the MBO API fetch.
- Commit: fix: FIX-KYLE-COMPOSIO-ENV guard optional Kyle environment values
FIX-KYLE-FUNCTION-INVOKE — Use authenticated function client for Kyle calls
- Files created: none
- Files modified: src/lib/agentChat.functions.ts, CHANGELOG.md
- Outcome: kyle-composio and kyle-session-distill are invoked through the authenticated backend function client already in scope.
- Commit: fix: FIX-KYLE-FUNCTION-INVOKE route Kyle calls through functions.invoke
SEC-007 — Make vault-documents private and workspace-scoped
- Files created: storage policy migration
- Files modified: src/components/research-vault/VaultEntryModal.tsx, src/routes/research-vault.tsx, CHANGELOG.md
- Schema migrations: replace public/owner-folder storage policies with workspace membership policies
- Storage: vault-documents changed from public to private
- Verification: policy state, private bucket state, signed attachment reads, build checks
- Commit: fix: SEC-007 secure vault document storage
- Notes: New uploads use the workspace UUID as the first path segment. Attachment reads now generate one-hour authenticated signed URLs instead of persisting or opening public URLs.
FIX-KYLE-WORKSPACE-CHECK — Allow platform-global agents through chat router
- Files created: none
- Files modified: src/lib/agentChat.functions.ts, CHANGELOG.md
- Schema migrations: none
- Edge functions: none
- Verification: build · lint · typecheck · Step 4 outcome
- Commit: fix: allow platform-global agents through chat router (Kyle workspace mismatch)
- Notes: The workspace check at
src/lib/agentChat.functions.tsrejected agents withworkspace_id IS NULL, blocking the canonical platform-global Kyle (seeded 2026-06-11). The thrown "Workspace mismatch" error surfaced in the chat UI as the misleading "Edge Function returned a non-2xx status code" because the server function threw before invoking the edge function. This is the actual root cause of Kyle returning errors since approximately 2026-06-11. The three code fixes shipped earlier today (model ID, provider revert, supabaseAdmin elevation) were all correct in isolation but addressed downstream paths that were never reached due to this earlier gate. SR-006 exemption (LOVABLE_RULES.md) already permits platform-global agent identities; this fix brings the chat router into alignment.
FIX-SEC-003-KYLES-SESSIONS-RLS-REVB — Replace permissive kyles_sessions policy with workspace-scoped policies
- Files created: supabase/migrations/20260614154913_d27c91a4-f409-4b2f-a678-df273cb48812.sql
- Files modified: CHANGELOG.md
- Schema migrations: replace_kyles_sessions_permissive_policy
- Edge functions: none (service_role bypass preserved by default; kyle-session-distill unchanged)
- Verification: pre-state captured, migration applied, post-state confirmed, service-role bypass verified, security scan re-run, finding #3 resolved; cross-workspace isolation blocked because the database has only one workspace membership and no second-workspace test identity
- Commit: fix(sec): replace permissive kyles_sessions RLS policy (finding #3 of 5)
- Notes: Finding #3 (kyles_sessions_public_access) was the only finding addressed in this session. Per the convention established in commit 8f0543b earlier today, no service-role policy was created — service_role bypasses RLS by default in Supabase. The other 4 findings (#1 can_write_workspace_member_escalation, #2 email_ingest_body_exposure, #4 tenants_sensitive_contact_data, #5 workspace_accounts_credentials_exposure) are deferred to a future BP-RLS-HARDENING-PACK-001. The fresh scan also surfaced additional pre-existing error-level findings outside this prompt's scope; none were changed.
FIX-KYLE-MODEL-ID — Restore Kyle from non-2xx errors
- Files created: none
- Files modified: supabase/functions/kyle-composio/index.ts, supabase/functions/kyle-briefing-generate/index.ts, supabase/functions/kyle-session-distill/index.ts, CHANGELOG.md
- Schema migrations: none
- Edge functions: kyle-composio, kyle-briefing-generate, kyle-session-distill (all three redeployed)
- Verification: build (no behavior change beyond the model identifier); Kyle returns 2xx after redeploy
- Commit: fix: Kyle model identifier (claude-sonnet-4-6 → claude-sonnet-4-5) restores Anthropic 2xx responses
- Notes: All three Kyle edge functions defaulted
MODELto"claude-sonnet-4-6", which is not a valid Anthropic model identifier. Anthropic returns 404 model_not_found on every invocation, surfacing in MBO as "Edge Function returned a non-2xx status code". Kyle has been broken since whatever commit introduced 4-6 (last successful agent_tool_calls write was 2026-06-10). Fix is a one-line default change in each of the three files to"claude-sonnet-4-5", matching the value backfilled intopublic.agents.modelby APP-001. If a customAGENT_MODELenv var is set in the Lovable console, it overrides the default — verify it is also a valid identifier or unset it. This fix is independent of the Agent Platform Execution Pack and unblocks Prompt 7 (APP-006-REVA kyle-composio wrapper) running against a healthy Kyle.
FIX-KYLE-INVOKE-AUTH — Elevate Kyle invocations to service-role from server function
- Files created: none
- Files modified: src/lib/agentChat.functions.ts, CHANGELOG.md
- Schema migrations: none
- Edge functions: none (no edge function code changed; kyle-composio and kyle-session-distill keep their requireServiceRole gates)
- Verification: Kyle chat path returns 2xx; kyle-composio edge function logs show successful invocation; user-JWT-bound flow no longer reaches the service-role gate.
- Commit: fix: elevate kyle-composio + kyle-session-distill invocations to service-role admin client
- Notes:
kyle-composio(L150) andkyle-session-distillboth callrequireServiceRole(req), which demands an exactAuthorization: Bearer <SERVICE_ROLE_KEY>match. Thesupabaseclient fromrequireSupabaseAuthmiddleware is bound to the user's JWT and fails this check, returning 401 (surfacing in MBO as "Edge Function returned a non-2xx status code"). Predecessor commit PROMPT-MBO-20260613-KYLEFIXC-001 switched from rawprocess.env.fetchtosupabase.functions.invoke()(correct move to server-side) but did not elevate the client — incomplete fix. This change importssupabaseAdminfromsrc/integrations/supabase/client.server.tsand switches both Kyle-related invocations to use it. The trust boundary remainssendAgentMessage'srequireSupabaseAuthmiddleware; the elevation happens server-side, on the user's behalf, after auth has succeeded. No edge function code or auth model changed. Surgical unblock; full architectural cleanup (settings-driven kyle-composio, settings UI, explicit acting-as claim) lands in BP-KYLE-CONFIG-PACK-001 per SR-007.
RULES-SR-007-FRAME-SETTINGS — LOVABLE_RULES.md adds Frame + Settings architecture (SR-007)
- Files created: none
- Files modified: LOVABLE_RULES.md, CHANGELOG.md
- Schema migrations: none
- Edge functions: none
- Verification: docs-only change; no build impact
- Commit: docs: LOVABLE_RULES.md — SR-007 Frame + Settings architecture principle
- Notes: Codifies the architectural principle Brian articulated 2026-06-14: agents are frames, their power is configuration, never code. Model provider, model identifier, system prompt, tool broker, allowed tools, risk policy, MBO entity access, and approval thresholds all live in database settings. Adding/retiring a provider is a row change, never a code change. New section under ARCHITECTURE LAW with implementation contract for edge functions, settings UI contract, violation history (APP-001 break), and enforcement guidance for Council review. This rule will land alongside BP-KYLE-CONFIG-PACK-001 (post-Agent-Platform-Pack) which builds the settings UI and refactors kyle-composio to read from rows instead of constants.
FIX-KYLE-PROVIDER-REVERT — Restore Kyle's provider = 'composio'
- Files created: supabase/migrations/20260614142100_fix_kyle_provider_revert.sql
- Files modified: CHANGELOG.md
- Schema migrations: 20260614142100_fix_kyle_provider_revert.sql
- Edge functions: none
- Verification: one-row UPDATE on canonical Kyle agent row; idempotent
- Commit: fix: revert Kyle's
agents.providerto 'composio' (APP-001 break) - Notes: APP-001 set Kyle's
providerto 'anthropic' (intent: model inference vendor). Five existing production code paths readprovideras the tool-broker selector and filter onprovider = 'composio'— src/lib/agentChat.functions.ts (lines 194 and 416), src/routes/kyle.tsx (line 61), src/components/dashboard/KyleActivityStrip.tsx (line 26), src/components/agents/KyleLauncher.tsx (line 50). After APP-001, all five surfaces stopped resolving Kyle, surfacing in MBO as "Edge Function returned a non-2xx status code". Minimum unblock for the in-flight pack: revertproviderto 'composio'. Keepsmodel='claude-sonnet-4-5',kind='superagent',is_council_seat=true,tool_policy, andconfigexactly as APP-001 set them. The proper architectural cleanup (provider semantics, settings-UI, settings-driven kyle-composio refactor) lands in the post-pack follow-up BP-KYLE-CONFIG-PACK-001.
APP-002.5-REVA — Reconciliation note: agent_runs vs agent_chat_messages
- Files created: docs/agent-platform/APP-002.5-RECONCILIATION.md
- Files modified: CHANGELOG.md
- Schema migrations: none
- Edge functions: none
- Verification: build · lint · typecheck
- Commit: (Lovable fills in)
APP-002-REVA — agent_runs sibling table
- Files created: supabase/migrations/20260614133634_4dc149f7-dc20-4811-9d78-41d9504dedf1.sql
- Files modified: CHANGELOG.md
- Schema migrations: created public.agent_runs as the execution-span sibling to agent_chat_messages, with SR-006 nullable workspace scoping, ARN serial generation, lineage, timing, outcome, cost, indexes, RLS, grants, and realtime publication
- Edge functions: none
- Verification: migration applied cleanly · generated types include agent_runs · build ✓ · lint blocked by pre-existing Prettier errors · typecheck blocked by pre-existing BlockDrawer.tsx error
- Commit: feat: APP-002-REVA agent_runs sibling table
- Notes: arn_svc_all was intentionally omitted because the service role bypasses RLS; grants appear immediately after CREATE TABLE per Pitfall #11.
APP-001 — agents table enrichment
- Files created: supabase/migrations/20260614130439_8330ecab-5f54-4df9-8789-cb661b9c91ca.sql (prompt specified 20260615000101_agents_enrichment.sql; Lovable migration tool auto-generated timestamp+UUID filename)
- Files modified: CHANGELOG.md
- Schema migrations: agents table — added kind, is_council_seat, model, prompt_pack_serial, tool_policy, config; created agents_slug_workspace_unique and agents_slug_global_unique indexes; backfilled Kyle (superagent) and Council seats (council)
- Edge functions: none
- Verification: build ✓ · lint (8721 pre-existing prettier errors, 0 new) · typecheck ✓
- Commit: feat: APP-001 agents table enrichment
- Notes: The verbatim migration’s Council-seat backfill used
and slug is null, but the 4 Council rows already had slugs assigned. A follow-up UPDATE was applied to complete the backfill for Aria, Scanner, Schema Auditor, and Regression Watcher.
RULES-PITFALL-9-PGVECTOR — LOVABLE_RULES pitfall #9 updated
- Files created: none
- Files modified: LOVABLE_RULES.md, CHANGELOG.md
- Schema migrations: none
- Edge functions: none
- Verification: docs-only change; no build impact
- Commit: docs: LOVABLE_RULES pitfall #9 — pgvector status reflects live DB (post APP-000 discovery)
- Notes: APP-000 discovery (2026-06-14) confirmed pgvector is installed (migration 20260524224146). Pitfall #9 previously stated "not yet installed" — that guidance is now stale. Rule updated to reflect production reality and provide forward guidance for APP-005 (agent_memory_entries) and any future vector-using migration.
APP-000 — Discovery and Reconnaissance
- Files created: docs/agent-platform/APP-000-DISCOVERY.md
- Files modified: CHANGELOG.md
- Schema migrations: none
- Edge functions: none
- Verification: build · lint · typecheck
- Commit: docs: APP-000 discovery and reconnaissance — Agent Platform pre-flight inventory
2026-06-13 — MBO-CODEX-STATUSBAR-001
- Codex Console: replaced horizontal scrolling status bar with two-row pill grid. No overflow scroll. All fields visible at once.
2026-06-13 — CODEX-FIX-DISPATCH-NULL-REPO
- Removed DEFAULT_REPO hardcoded fallback (was worldport-platform).
- Dispatch form now reads repo_url from active site record.
- Dispatch button disabled with clear error if site has no repo_url.
- Closes silent wrong-repo dispatch on misconfigured sites.
2026-06-13 — PROMPT-MBO-20260613-KYLEFIXC-001
- agentChat.functions: replace raw process.env fetch calls to kyle-composio and kyle-session-distill with supabase.functions.invoke(). Fixes: Unauthorized — service-role key required on every Kyle message.
2026-06-13 — PROMPT-MBO-20260613-KYLEFIXB-001
- kyle-composio: revert KYLE_MBO_API_KEY and COMPOSIO_USER_ID from requireEnv() back to safe Deno.env.get() fallbacks — prevents cold-start crash when custom secrets are absent. Restores graceful degradation guard on callMboApi.
2026-06-13 — PROMPT-MBO-20260613-KYLE-FIX-A-SITUATIONAL-AWARENESS
- Kyle now receives workspace-level context when opened outside a site scope. Previously his only knowledge was the base system prompt and a 6-hour-stale WorldPort brief — he had no awareness of the current route, what sites exist, open incidents/tasks across the workspace, or code health state.
- Added current_route to ChatInput schema and propagated it through AgentChatInterface, KyleLauncher, and the /kyle route. Both surfaces now pass pathname + search params so Kyle knows where the operator is.
- Closed KYLE-MED-006: /kyle route now reads ?site= from URL params and passes siteId to AgentChatInterface, matching KyleLauncher's behavior. Previously the route never extracted siteId at all.
- New buildWorkspaceContextBlock helper in siteContext.server.ts mirrors the buildSiteContextBlock pattern but pulls workspace-wide state: sites list, cross-site incidents/tasks/prompts, code health by severity, and the operator's current route.
- sendAgentMessage now calls buildWorkspaceContextBlock when site_id is absent. The remote wire field
site_context_blockis reused for both scopes; the local variable was renamed tocontextBlockfor clarity. - Closes audit KYLE-HIGH-001 (situational awareness gap, Option A) and KYLE-MED-006 (siteId launcher/route inconsistency).
2026-06-13 — PROMPT-MBO-20260613-KYLE-FIX-B-ENV-HARDENING
- Promoted COMPOSIO_USER_ID from hardcoded constant to required env var in both kyle-composio and agent-action-execute. Closes audit KYLE-CRIT-003. The platform-wide identity is now rotatable via Supabase edge function secrets without a code change.
- Promoted KYLE_MBO_API_KEY from optional (Deno.env.get(...) ?? "") to required (requireEnv(...)) in both files. Removes the silent-failure mode where the function deployed and returned per-request errors instead of refusing to start. Closes audit KYLE-CRIT-002.
- Deleted now-unreachable !KYLE_MBO_API_KEY null-checks (both files).
- Operational pre-condition: both env vars (COMPOSIO_USER_ID, KYLE_MBO_API_KEY) must be set in the Supabase project before redeploy. Current Composio user ID in use: user_0qev3c (preserved for backward compat; rotate via env when ready).
2026-06-13 — PROMPT-MBO-20260613-KYLESESSGRAPH-001a (Kyle Sessions Graph Wire)
- Added agent_id UUID FK from kyles_sessions → agents(id) ON DELETE SET NULL. Preserves session archive integrity even if Kyle's agent row is ever removed.
- Added idx_kyles_sessions_agent_id index on the new FK column.
- Added COMMENT ON TABLE for kyles_sessions and agents — pg_description now carries canonical descriptions including the architectural note that Kyle is platform-global (workspace_id IS NULL) while sessions are workspace-scoped.
- Added COMMENT ON COLUMN for all primary kyles_sessions fields (agent_id, serial, session_id, app_tags, decisions, topics, prompt_ids, summary, workspace_id). app_tags remains free-form pending operator reconciliation of the apps/blocks/topics split.
- Added "agents" to kyle-api ALLOWED_ENTITIES as read-only. Write operations (insert/update/delete) on agents return 403 from kyle-api.
- Backfilled agent_id on all 7 existing kyles_sessions rows with Kyle's UUID.
- Supersedes PROMPT-MBO-20260613-KYLESESSGRAPH-001 (original assumed Kyle was workspace-scoped; corrected to platform-global per migration 20260611165854).
- Enables: 5am CT nightly session push automation can now write graph- connected rows from day one.
2026-06-13 — AUTOLOOP-001 / AL-001 (Nightly Code Reader trigger)
- Added the authenticated
/api/public/code-reader-nightlyscheduler route. At 03:30 UTC it inspects each active site's latestHEAD~1GitHub diff when the head commit is under 24 hours old, reviews changed patches for concrete defects, and sends findings through the existingcode-reader-dispatchingestion path. Empty scans are dispatched too, preserving the audit trail. - This stage does not submit findings to Council or dispatch a writer; those wires remain deferred to later AUTOLOOP stages by operator decision.
2026-06-12 — CODEX-FIX-D (Council pr_title — intentional assignment)
- Removed TODO(COUNCIL-3) comment at council-deliberate line 694-696. pr_title now intentionally uses vertical_slug — codex_jobs has no title column. vertical_slug is human-readable and always present. Closes COUNCIL-4. COUNCIL-3 (entity_serial) already resolved per Lovable pre-flight — not modified. Required before AUTOLOOP-001 AL-001 ships (Daily Letter reads pr_title).
2026-06-13 — CODEX-FIX-B (Service-role auth hardening)
- Created
_shared/requireServiceRole.ts— guard for internal-only edge functions. Callers must present the service-role key; user JWTs are rejected. - Applied the guard to
kyle-briefing-generate,kyle-composio, and the realkyle-session-distillimplementation. council-deliberateauth guard was already present — not modified.- Closes audit C-3 / KYLE-CRIT-001. Required before AUTOLOOP-001 AL-001 ships.
2026-06-12 — ATLAS-FIX-A (vw_atlas_blocks grant)
-
Added GRANT SELECT ON public.vw_atlas_blocks TO authenticated, service_role. The ATLAS-RESTORATION-001 migration (20260612201200) created the view with security_invoker = true but omitted this grant — the only view in the codebase missing it. PostgREST returned 42501 permission denied on every authenticated read, causing the Atlas canvas at /atlas to render the error state. Closes audit C-1.
-
Applied SR-005 three-role REVOKE discipline to record_resolve_evidence, assert_resolved, and pre_council_resolve_batch (closes audit RPC-1, RPC-2).
-
Removed @ts-nocheck from src/integrations/agentos/sanitize.test.ts (closes audit L-4).
-
Added hand-maintained CouncilAgentSeat union type in src/lib/councilTypes.ts and applied at council-deliberate write sites; generated types remain untouched (closes audit SCHEMA-2).
Deferred (explicit decisions needed):
- H-2/TYPES-1 (platform_deferred_findings cross-workspace UPDATE) — pending operator decision on workspace scoping.
- L-1 (agent_dispatch_log RLS) — accepted design risk per existing comment.
- L-2 (codex_jobs.builder CHECK constraint) — minor; defer.
- SOT-1/SOT-2 — documentation + runtime investigation; not code fixes.
2026-06-12 — CODEX-FIX-H (UI polish & cleanup)
- Realtime subscription for codex_jobs now skipped when the seeded job is already terminal (output_rendered=true or pr_status=failed), preventing channel leaks for completed_inline dispatches (closes audit M-1).
- prStatusTone now returns 'success' for completed_inline and not_applicable_inline instead of neutral gray (closes M-2).
- CHAT-1's cited
record(...).catch(console.error)call was absent from the live agentChat.functions.ts tree, so that change was skipped and logged as a deferred drift finding rather than applied to a different call site. - Composio non-JSON responses now log diagnostic info (status + parse error) while still propagating a generic error to the user (closes CHAT-2).
- DeferredFindingModal now throws when finding is null instead of silently resolving with undefined (closes audit DEFER-1).
- worldportGraph.ts now includes Council tables (deliberations, votes, amendments, resolve_evidence) in the TS manifest (closes audit GRAPH-1).
2026-06-12 — CODEX-FIX-F (AddressBar resolve guard)
- AddressBar.submit() now returns early when SERIAL_RE.test() fails, preventing onResolve from firing on malformed serials. Previously the warning displayed but resolution still fired with the bad input, causing contradictory UX (closes audit RESOLVE-1, SOT-3).
2026-06-12 — CODEX-FIX-E (Site audit filter)
- Fixed PostgREST
.not("in", ...)filter syntax in siteAudit.core.ts at the task scan (line 137) and incident scan (line 165). Double-quoted values inside the parens caused the exclusion filter to match no rows, so done/cancelled tasks and resolved/closed incidents were being counted in site health scores. All site health scores were silently wrong (closes audit AUDIT-1).
2026-06-12 — CODEX-FIX-D (Council write correctness)
- Council platform_state rows now emit the canonical CJOB serial from codex_jobs.serial instead of CJOB-<uuid>; previously these rows were unresolvable via public.resolve() (closes audit COUNCIL-4).
- codex_jobs.pr_title is not present in the live schema, so COUNCIL-3 is documented as a deferred schema_drift finding and the vertical_slug fallback is explicitly marked with a follow-up TODO.
- serialBySeat.get(seat)! non-null assertion replaced with an explicit invariant throw; protects persistVotes from silent TypeError (closes audit COUNCIL-6).
- Added 'not_applicable_inline' to codex_council_status enum and dispatch jobs set it for inline builders so council_status no longer hangs at 'pending' forever for claude_code / perplexity jobs (closes audit H-4).
- council-deliberate now throws instead of soft-approving when GITHUB_TOKEN is unset; it refuses to deliberate against an empty diff (closes audit M-6).
2026-06-12 — CODEX-FIX-C (Code Reader + dispatch enums)
- Extended agent_dispatch_action enum with 'code_reader_scan' and 'canceled'.
- Extended agent_dispatch_actor enum with 'code_reader'.
- code-reader-dispatch now writes the dispatch log with valid enum values; the silent invalid_text_representation error is closed. No previous scans were logged (closes audit DISPATCH-1, SCHEMA-3).
- code-reader-dispatch now surfaces dispatch-log insert errors via console instead of silently swallowing them.
- cancelCodexJob writes action 'canceled' to the dispatch log instead of 'codex_dispatched' (closes audit M-3).
- metadata.commit_sha now validated through the DispatchBody schema instead of an unsafe raw-body cast (closes audit DISPATCH-3).
- upsert_code_health_issue now follows SR-005 three-role REVOKE discipline (closes audit DISPATCH-2).
2026-06-12 — CODEX-FIX-B (Security)
- council-deliberate now requires Bearer COUNCIL_DELIBERATE_SECRET; previously any unauthenticated POST with valid job + workspace UUIDs could fire a full Council deliberation pass (closes audit C-3).
- council-deliberate now verifies the caller's workspace_id contains the job's created_by user via workspace_members; previously workspace_id was accepted verbatim from the request body with no ownership check (closes audit COUNCIL-2).
- codex-pr-completed updated to send the new COUNCIL_DELIBERATE_SECRET when invoking council-deliberate.
- cancelCodexJob now scopes the update by created_by = context.userId, preventing cross-user cancellation (closes audit H-1).
- Operator action required before this code runs: configure COUNCIL_DELIBERATE_SECRET in Edge Function secrets. Until configured, every Council invocation returns 500 server_misconfigured.
2026-06-12 — CODEX-FIX-A (Dispatch prompt + builder gating)
- Inline builders (claude_code, perplexity) now receive the user's task_directive in their prompt (closes audit C-1).
- GITHUB_DISPATCH_TOKEN check moved inside the codex_actions builder path so inline builders no longer 500 when the token is unset (closes audit C-2).
- site_context (incidents, tasks, known issues, last prompt serials, health score) now forwarded to the builder prompt instead of being silently dropped (closes audit C-5).
- DEFAULT_REPO in src/lib/codex.functions.ts is now read from CODEX_DEFAULT_REPO env var with the existing string as fallback (closes audit H-5).
2026-06-12 — TEST-INFRA-WIRE-001
- Declared
vitest,@playwright/test,jsdom, and@testing-library/*indevDependencies(previously imported but not declared; tests ran only because Lovable's runtime included them). - Added
test,test:watch,test:ui,test:e2e,test:e2e:installscripts. - Created
vitest.config.ts(scoped tosrc/**/*.{test,spec}.{ts,tsx}). - Created
playwright.config.ts(scoped totests/**/*.spec.ts). - No test files modified; no new tests written; E2E suite still requires live dev server + auth env vars to actually run.
2026-06-12 — WP-CODEX-FULLWIRE: Synchronous research and experiment builders
- Wired Perplexity and Claude Code builders to return inline output, with atomic
completed_inlinejob completion. - Extracted deferred-finding classification into a shared edge utility and applied it to both synchronous output and PR completion paths.
- Added
completed_inlinetocodex_pr_status; regenerated frontend types already include the new terminal value. - Files touched:
supabase/functions/_shared/deferred-scan.ts,supabase/functions/dispatch-codex-job/index.ts,supabase/functions/codex-pr-completed/index.ts, migration,CHANGELOG.md. - Serial:
PROMPT-WP_CODEX_FULLWIRE-20260612-0001.
2026-06-12 — CODEX-DISPATCH-SPEC-002a: Inline output + deferred findings
- Added inline Codex output states with realtime job updates, markdown rendering, copy/clear, PR, Council, and failure actions.
- Added the platform-global deferred findings registry, exact/fuzzy classifier, deterministic clustering, context sweep, console grouping, job counts, modal actions, and sidebar cluster badge.
- Exact and fuzzy dedup use an atomic backend helper: occurrence counts increment on every hit, while
source_jobsappends only whenNOT (job_id = ANY(source_jobs)), preventing duplicate job IDs on retries. - Updated
codex-pr-completedanddispatch-codex-job; dual-writer review remains deferred to 002b. - Serial:
BP-CODEX-DISPATCH-SPEC-20260612-002a.
ATLAS-RESTORATION-003 — Pack 11 closeout
- Files modified:
docs/RETROSPECTIVE_ATLAS_RESTORATION_2026-06-12.md(new),docs/ATLAS_RESTORATION_CLOSED.md(new),docs/FOUNDATION_ROADMAP.md(pack 11 row added),CHANGELOG.md. - Schema migrations:
vw_atlas_blocksview registered as platform artifact in pages (artifact_type=view,artifact_status=live,block_id=worldport); SWEEP-ATLAS-INTENT-DRIFT-001 closed inplatform_sweepswithclosed_at,closed_in_pack,resolution_notes; the AR-003 pages row for SWEEP-ATLAS-INTENT-DRIFT-001 flipped toartifact_status=deprecated; SR-008.application_count incremented 1 → 2 with notes append. - Verification: ✓ migration applied · smoke passed (
vw_atlas_blocksregistered, sweep closed + page row deprecated, SR-008.application_count=2, total platform rows=15, is_compliant=true). - Tag:
atlas-restoration-closed-2026-06-12(annotated; application reserved for Computer/Brian per repository state boundary). - Commit: Lovable + Computer co-applied; tag pushed by Computer.
2026-06-12 — ATLAS-RESTORATION-002: Visual Atlas + Q3-C artifact drawer
- Ported the seven-file Atlas visualization scaffold into
src/components/atlas/, reskinned to MBO tokens, with layer/vertical/wave/source filter pills derived directly from view data. - Replaced the
/atlasplaceholder with the 66-block React Flow architecture map and responsive mobile list. - Added the Q3-C block drawer with grouped iconned artifact rows, MBO status chips, and separate Marketplace navigation.
- Added
/atlas/$type/$namefocused-artifact routing so all 14 AR-003 canonical paths resolve and reopen Atlas with the matching artifact highlighted. - No schema changes. Serial:
PROMPT-ATLAS_RESTORATION-20260612-0002.
2026-06-12 — ATLAS-RESTORATION-001: Atlas projection and route scaffold
- Created
public.vw_atlas_blocks, a security-invoker projection of all 66 canonical blocks with dependency-slug arrays for the visual Atlas. - Installed
@xyflow/react,react-window, andframer-motionfor the ATLAS-002 visualization build. - Added the
/atlasloading route and restored Atlas to the Operate navigation insrc/components/worldport/Shell.tsx. - Inline SR-004 smoke passed: 66 projected rows, canonical
rosdependencies, andis_compliant=true. - Serial:
PROMPT-ATLAS_RESTORATION-20260612-0001.
ARTIFACT-REGISTRY-005 — Pack 10 closeout
- Files modified:
docs/RETROSPECTIVE_ARTIFACT_REGISTRY_2026-06-12.md(new),docs/ARTIFACT_REGISTRY_CLOSED.md(new),docs/FOUNDATION_ROADMAP.md(pack 10 row added, body bullets updated),CHANGELOG.md. - Schema migrations: SR-008 promotion CANDIDATE → ACTIVE in
platform_standing_rulesvia UPDATE.application_countunchanged at 1 (promotion is governance, not application).promoted_atset to now(). Notes appended with promotion narrative. - Verification: ✓ migration applied · smoke passed (
status='active',application_count=1unchanged, total active rules=8,is_compliant=true). - Tag:
artifact-registry-closed-2026-06-12(annotated; application reserved for Computer/Brian per repository state boundary). - Commit: Lovable + Computer co-applied; tag pushed by Computer.
2026-06-12 — ARTIFACT-REGISTRY-004: SR-008 candidate filing
- Filed
SR-008 — Universal Artifact Registrationinplatform_standing_ruleswith candidate status and the ratified wording, rationale, and exclusions. - Recorded AR-003 as the first application with
application_count=1, a populatedfirst_applied_at, and no promotion timestamp. - Inline SR-004 smoke completed with exactly one SR-008 row, the required candidate shape, and
is_compliant=true. - Files touched:
supabase/migrations/20260612193654_artifact_registry_004_sr008_candidate.sql,CHANGELOG.md. - Serial:
PROMPT-ARTIFACT_REGISTRY-20260612-0006.
2026-06-12 — ARTIFACT-REGISTRY-003: Pack 10 canonical artifact rows
- Seeded 14 platform-scoped artifact rows into
pages, all attributed to theworldportcore block and addressed at/atlas/{artifact_type}/{artifact_name}. - Registered 3 tables, 1 RPC, 1 cron, 1 view, 2 guards, and 6 sweeps; 12 are live, G15 is planned, and the closed Aria seat-instantiation sweep is deprecated.
- Inline SR-004 smoke completed with all 14 rows, the expected type distribution, correct platform scope and block attribution, matching Atlas paths, and
is_compliant=true. - Files touched:
supabase/migrations/20260612191213_49ceb811-a11e-4847-894e-dd30ce82d66d.sql,CHANGELOG.md. - Serial:
PROMPT-ARTIFACT_REGISTRY-20260612-0005.
2026-06-12 — ARTIFACT-REGISTRY-002: Canonical block catalog + 63-block seed
- Migrated the 8 legacy
blocks_catalogrows to canonical slugs and layer vocabulary, includingrouteos→rosandesignos→signos. - Added
blocks_catalog_layer_valid, seeded all 63 canonical blocks, and fanned 12 validfed_byrelationships intoblock_dependencies; 5 documented dangling references were skipped. - Inline SR-004 smoke completed with 66 total blocks, 16 total dependency edges, legacy slugs removed, canonical layers enforced, and
is_compliant=true. - Files touched:
supabase/migrations/20260612184645_45567b00-52f7-42b7-a114-90719ca035af.sql,CHANGELOG.md. - Serial:
PROMPT-ARTIFACT_REGISTRY-20260612-0004.
2026-06-12 — ARTIFACT-REGISTRY-001a: Universal artifact registry schema
- Extended
public.pageswithartifact_type,artifact_status,artifact_name,artifact_subtype, andblock_id, with schema comments defining the locked Q2b/Q3/Q4/Q5 semantics. - Backfilled existing page rows, relaxed workspace/site ownership for platform artifacts, and added four integrity constraints, two platform RLS policies, and four lookup indexes.
- Corrected the superseded 001 comment syntax by using single PostgreSQL string literals; the migration and inline SR-004 smoke completed successfully with
is_compliant=true. - Serial:
PROMPT-ARTIFACT_REGISTRY-20260612-0002.
2026-06-12 — OPERATE-VISIBILITY-006: Pack 9 Closeout (SR-007 ACTIVE)
Pack 9 closed. Six sub-prompts. Zero retries. ~2 hours from ADR to closeout.
- SR-007 (operational visibility) promoted CANDIDATE → ACTIVE in
public.platform_standing_rulesregistry with 5 cited applications (OV-001 governance, OV-002 signals, OV-003 scheduler crons, OV-004 incidents, OV-005 daily letter). Total active Standing Rules: 7 (SR-001 through SR-007). - Retrospective document filed at
docs/RETROSPECTIVE_OPERATE_VISIBILITY_2026-06-12.md(serialRETRO-OPERATE_VISIBILITY-20260612-0001). Documents: pack constants, SR-007 promotion case, three emerged patterns (registry+view+UI, idempotent upsert + auto-resolve, apply-time auth hardening as discipline), four named honest limits (no realtime, no manual cron trigger, no email digest, marginal G14 detection inefficiency in OV-005), what ships next (Pack 10 ATLAS-RESTORATION, PULSE-000 parked). FOUNDATION_ROADMAP.mdupdated: Pack 9 row added with✅ COMPLETE, §11 Foundation Closeout amended with pack 9 paragraph, §10 cross-references extended with retro + marker + 3 new registries.- Marker file
docs/OPERATE_VISIBILITY_CLOSED.mdcreated. Annotated git tagoperate-visibility-closed-2026-06-12to be applied by Brian locally (same operational boundary asfoundation-closed-2026-06-12). - The "no hunting, no SQL" property is now structurally enforced through pack discipline. Every future pack that writes operational rows is bound by SR-007 to ship UI in the same pack or file a tracked deferral.
Apply-time hardening discipline note: five distinct events across foundation + pack 9 where Lovable tightened auth/safety surfaces beyond prompt specification (TH-001 three-role REVOKE, TH-003 inline preservation, TH-007 Part 3 search_path, OV-003 service-role tightening, OV-004 continuation). Worth a future SR-008 candidate filing.
What ships next:
- ATLAS-RESTORATION (Pack 10) — visual Block/Vertical interactive tool restoration to the now-freed
/atlasslot. ClosesSWEEP-ATLAS-INTENT-DRIFT-001. Salvage fromBP-WP-SITE-ATLAS-VIZ-001scaffold (~70% reusable). - PULSE-000 — parked. 30-day operational data prerequisite started with 2026-06-12 03:00 UTC nightly batch. Reopens ≥2026-07-12.
2026-06-12 — OPERATE-VISIBILITY-005: Daily Letter Platform Overnight section
- New server function
composePlatformOvernight(src/lib/platformOvernight.functions.ts) — reads compliance check, G14 silence (25h window), lastnightly_batchrow,platform_incidents(open/opened-24h/auto-resolved-24h/top open),platform_sweeps(filed/closed/open), andget_platform_cron_jobsrecent_runs to produce 2–4 narrative paragraphs matching the existingseedLettervoice. Same SR-005 posture as OV-003/004:requireSupabaseAuth+supabaseAdminloaded inside the handler. - New component
PlatformOvernightSection(src/components/letter/PlatformOvernightSection.tsx) — Activity-iconed card withis_compliantchip, optional G14 silence chip, paragraph stack, and a footer strip (generated time, last batch, open incidents, open sweeps). /daily-letternow renders the Platform Overnight card directly under the workspace LetterView (Archive untouched). Page is otherwise unchanged. No schema migration.- Serial: PROMPT-OPERATE_VISIBILITY-20260612-0005. Fifth consecutive SR-007 application; pattern proven for OV-006 promotion retro.
2026-06-12 — OPERATE-VISIBILITY-004: platform_incidents + auto-elevate + auto-resolve
- New table
public.platform_incidents— platform-global incidents auto-elevated fromplatform_signals. Deterministic idmd5(signal_kind || '|' || entity_serial)so incidents persist across hourly signal rollovers. SR-006 exempt (operational state registry). - New FK column
source_signal_idreferencesplatform_signals(id) ON DELETE SET NULL; updated on each refresh to point at the latest matching signal. - New RPC
public.refresh_platform_incidents()— idempotent upsert + auto-resolve. Allowlist:g14_silence→ SEV2,compliance_failure→ SEV2,cron_failure→ SEV3 single / SEV2 if ≥3 distinct-hour failures in 24h.open_sweepandtest_harness_attentionstay signals-only. - Auto-resolve: incidents whose source signal condition no longer fires transition to
status='auto_resolved'withauto_resolved_reason='signal_condition_cleared'. Recurrence reopens the incident. - New view
public.vw_incidents_combined(security_invoker) — UNIONs workspace incidents and platform_incidents with asource_typediscriminator. /incidentspage extended: new Source filter pill (All / Workspace / Platform), new "Refresh platform" header button callingrefreshPlatformIncidentsserver fn, and a read-only Platform Incidents section above the existing workspace table. Workspace components (IncidentTable / IncidentDetail / IncidentModal / IncidentLinkChips) preserved verbatim./signalsRecompute now chainsrefresh_platform_signals→refresh_platform_incidents(signals must refresh first so incident elevation reads fresh state) and invalidates both caches.- SR-005 seventh application (three-role REVOKE on
refresh_platform_incidents, service_role only — same hardening posture as OV-003). - Fourth SR-007 application: data layer + UI surface ship in same pack.
- Inline migration smoke: RPC runs, idempotent on second call, view UNIONs cleanly,
is_compliant=truepost-apply.
2026-06-12 — OPERATE-VISIBILITY-003: Scheduler Platform Crons section
- New RPC
public.get_platform_cron_jobs()— read-only SECURITY DEFINER function surfacingcron.job+cron.job_run_details. Returns one row per job with last 5 runs as jsonb, computed last_success_at / last_failure_at timestamps, failures_last_24h count, and a uses_vault boolean derived from the cron command text. SR-005 + SECDEF-auth-leak guard: EXECUTE granted toservice_roleonly (granting toauthenticatedtrips the compliance check, as in OV-002). - New TanStack server function
getPlatformCronJobs(src/lib/platformCrons.functions.ts) authorizes the caller viarequireSupabaseAuthand invokes the RPC throughsupabaseAdmin. /schedulerpage wrapped in Tabs: "Operations" (existing user-created approval-gated jobs, preserved verbatim) and "Platform Crons" (new visibility surface).- New
PlatformCronsPanelcomponent renders active/inactive chip, vault chip, last 5 runs as a colored status strip, last success/failure relative timestamps, inline error message when the most recent run failed, and a failures_last_24h chip when >0. Refetch every 60s. - Read-only by design — manual trigger controls require their own ADR. Cron failures continue to auto-surface on
/signalsvia OV-002'srefresh_platform_signals(signal_kind=cron_failure). - Third SR-007 application: data layer (RPC) + UI surface (panel + tabs) shipped in the same pack.
- Inline migration smoke verifies
test-harness-nightlyis visible andis_compliant=truepost-apply.
2026-06-12 — OPERATE-VISIBILITY-002: Atlas → Signals rename + platform_signals layer
/atlasroute renamed to/signals(route file, sidebar nav, Codex dialog labels).atlas_signalstable is NOT renamed — its workspace-scoped semantics are correct; only the route was lying./atlasslot is now free for ATLAS-RESTORATION pack 10.- New table
public.platform_signals— platform-global signals layer (no workspace_id). Five signal kinds:test_harness_attention,g14_silence,open_sweep,cron_failure,compliance_failure. Deterministic id frommd5(signal_kind || entity || time_bucket)(cron_failure also includes runid) for idempotent upsert. SR-006 exempt (operational state registry, no serial issuance). - New view
public.vw_signals_combined— UNIONs atlas_signals (workspace) and platform_signals (platform) withsource_typediscriminator.security_invoker=trueso workspace RLS is honored. - New RPC
public.refresh_platform_signals()— idempotent upsert from 5 sources. SR-005 three-role REVOKE applied; service-role-only EXECUTE (granting to authenticated would trip the SECDEF-auth-leak compliance guard). Invoked from the new TanStack server functionrefreshPlatformSignalsinsrc/lib/platformSignals.functions.ts, which authorizes the caller viarequireSupabaseAuthand then calls the RPC with the admin client. - Auto-resolve logic: open_sweep signals whose underlying platform_sweeps row closes are marked resolved with reason=
sweep_closed. OV-003/004/005 will add triggers for continuous refresh; OV-002 ships the pull model. - Signals page now shows mixed workspace+platform rows with a
source_typechip and filter pills (All / Workspace / Platform). Dismiss button conditionally rendered only for workspace signals (platform signals auto-resolve). - Recompute button calls BOTH
compute_atlas_signals(workspace) AND therefreshPlatformSignalsserver fn (platform) in parallel — one click refreshes both layers. - Second SR-007 application: new table shipped with UI surface in the same pack.
- Inline migration smoke verifies: refresh runs cleanly, populates ≥5 rows (from OV-001's 5 open sweeps), view UNIONs correctly, second refresh is idempotent (no row stacking), is_compliant=true post-apply.
2026-06-12 — OPERATE-VISIBILITY-001: platform_sweeps + /governance UI
- New table
public.platform_sweeps— first durable home for sweep findings. Same registry pattern as platform_standing_rules and platform_properties (TH-007 Part 3). SR-006 exempt (configuration/registry table, no serial issuance). - Seeded with all 6 known foundation-era sweeps: ARIA-SEAT-INSTANTIATION (closed in TH-007 Part 1), ARIA-AMENDMENT (deferred), DISSENT-DETECTOR-COVERAGE (deferred), GRAPH-NODE-BACKFILL (investigating), PREFIX-EMPTY-AUDIT (open), ATLAS-INTENT-DRIFT (deferred to pack 10).
- New route
/governance— read-only UI surfacing three registries via tabbed interface: Standing Rules (6 ACTIVE + 1 CANDIDATE), Platform Properties (OWE-001 with 3 instantiations), Sweeps (5 open + 1 closed at first ship). Sidebar nav entry added under OPERATE with ShieldCheck icon. - First known SR-007 application: registries shipped at TH-007 Part 3 → UI shipped today. Same pack discipline that SR-007 promotion will require going forward.
- Inline migration smoke verifies 6 sweep rows landed and is_compliant=true post-apply. SR-004 application (structural surface added with proactive smoke).
- Mobile nav unchanged:
MobileMenuSheetreads from sharedNAVinShell.tsx;MobileBottomNavhas a fixed 4-slot taxonomy that intentionally excludes secondary OPERATE items.
2026-06-12 — TEST_HARNESS-007 Part 6: Foundation Closing Marker
Final sub-part of TH-007. Added docs/FOUNDATION_CLOSED.md — a 5-line human-discoverable marker pointing to the retrospective, roadmap, and the foundation-closed-2026-06-12 annotated git tag. No code, no migrations. Tag application (git tag -a foundation-closed-2026-06-12) is a stateful git operation reserved for Brian; the annotation body lives in TH007-PART6-CLOSING-COMMIT_PROMPT.md. Foundation now closed in every layer: structural (G14), narrative (retro + roadmap), and permanent (marker file + pending tag).
2026-06-12 — TEST_HARNESS-007: Foundation Closeout
Eight-pack foundation closed. 22-day arc. 151 migrations. Nine is_compliant-contributing structural guards active. The platform now structurally requires itself to observe itself.
Five sub-parts shipped today as part of TH-007:
- Part 1 — Aria seat instantiation.
SeatSlugunion extended;seatAria()thin-approve stub added;INSTANTIATED_VOTING_SEATSupdated to include aria. ResolvesSWEEP-ARIA-SEAT-INSTANTIATION-001. Closes the loop on TH-002 scenario 7 (canonical_seat_drift). - Part 2 — G14 + G2 view-filter correction. New guard
test_harness_scenario_drift(silence detector, 25-hour window) added toserial_compliance_check()as the ninthis_compliant-contributing guard. Same canonical reproduction also corrects a latent G2 misclassification bug where views exposingserialcolumns were flagged as unregistered tables. SR-003 application #10. - Part 3 — Standing Rules + Platform Properties registries. New tables
public.platform_standing_rulesandpublic.platform_properties. Six rules promoted to ACTIVE (SR-001-006); one CANDIDATE (SR-007). One named property (OWE-001 Observation Without Enforcement). Both tables are first known SR-007 application items pending UI in OPERATE-VISIBILITY pack 9. - Part 4 — Retrospective document.
docs/RETROSPECTIVE_FOUNDATION_2026-06-12.md(serialRETRO-FOUNDATION-20260612-0001). ~3500 words across 11 sections including three honest-limit dimensions, sweep dispositions, patterns that worked, and what ships next. - Part 5 — Roadmap + STATE_OF_PLATFORM + CHANGELOG (this entry). Foundation closed in the canonical roadmap document; STATE_OF_PLATFORM points to retro and new registries; CHANGELOG records the closeout.
Next packs: OPERATE-VISIBILITY (Pack 9) wires UI to existing operational data and closes SR-007. ATLAS-RESTORATION (Pack 10) restores the visual Block/Vertical interactive tool to its proper nav slot. PULSE-000 (parked) opens after 30 days of operational TEST_HARNESS data accumulates.
Unreleased
-
TEST_HARNESS-007 Part 2 (G14 silence detector + latent G2 view-filter fix):
PROMPT-TEST_HARNESS-20260612-0007-PART2-REV2. Single migration rewritingpublic.serial_compliance_check()(SR-003 application #10 — full body verbatim from20260612003434_*.sqllines 6-294, five minimal additions marked NEW). G14 (test_harness_scenario_drift): silence detector requiring each of the 4 requiredscenario_kinds (synthetic_council,drift_simulation,resolver_prefix_sweep,nightly_batch) to have at least onecouncil_test_runsrow within the last 25 hours (24h cron cadence + 1h slack). If any kind goes silent, the corresponding finding row surfaces andis_compliantflips false. G14 is the 9this_compliant-contributing guard (G2, G7, G8/G9, G10, G11, G12, G13, G14). Posture matches G13: future amendments adding a new scenario_kind MUST extend both thecouncil_test_runsCHECK constraint and this required-kinds list. G2 view-filter correction (latent bug fix): addedAND c.table_name IN (SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE')to thev_unregistered_tablesquery. Root cause: G2 readinformation_schema.columnswithout filtering to BASE TABLE, so views exposingserialvia pass-through SELECT (TH-005'svw_test_harness_attention_required) were misclassified as unregistered tables. Pre-existing latent bug from TH-005 —is_complianthad been quietly false since TH-005 shipped; TH-005 omitted an inlineDO $smoke$ ... is_compliant=trueblock (missed SR-004 application), so the regression survived until Part 2's smoke was the first apply-time check strict enough to demand it. Per Brian's clarification, both deltas count as one SR-003 application (rule contract: one canonical reproduction per migration). SR-003 candidate retro count remains 10 applications, zero drift. Refines SR-004 candidate wording for Part 3 promotion: scope explicitly includes "views that exposeserialcolumns or other graph-participating data". Inline smoke: asserts response carriestest_harness_scenario_driftkey; thatunregistered_tablesdoes NOT containvw_test_harness_attention_required(explicit G2 fix verification); G14 finding count = 0;is_compliant=true. Migration applied successfully — smoke passed. Discipline: SR-003 ✅ application #10 (full body verbatim, 5 NEW markers); SR-004 N/A for this migration (G2 was the missing-smoke case being corrected; this migration includes its own smoke); SR-005 N/A (no new SECDEF functions; existing REVOKE PUBLIC + GRANT authenticated/service_role preserved verbatim); SR-006 N/A (no new tables); D9 ✅ (G14 surfaces silence as compliance failure, not seat veto); D10 ✅ (G14 contributes tois_compliant, doesn't write to it); D12 ✅ (G14 reserved at TH-001 now realized). Foundation closeout guard set complete — next: TH-007 Part 3 (SR-003/004/005/006 registry promotion + SR-004 wording refinement). -
TEST_HARNESS-007 Part 1 (Aria seat instantiation — constitutional gap close):
PROMPT-TEST_HARNESS-20260612-0007-PART1. ResolvesSWEEP-ARIA-SEAT-INSTANTIATION-001.supabase/functions/council-deliberate/index.ts: addedariatoSeatSlugunion (line 56), addedseatAria()thin-approve stub directly afterseatKyle(returns{verdict:"approve", reason_code:null, reason_detail:null}untilSWEEP-ARIA-AMENDMENT-001ships her real amendment-generation logic), added Aria to thePromise.allspawn block, added to theverdictsrecord, and added to thedetermineNextStatusvoting array. Aria is now constitutionally present in every Council round; consensus outcomes are unaffected because the stub always approves.supabase/functions/_shared/council-seats.ts: updatedCanonicalVotingSeattype union andINSTANTIATED_VOTING_SEATSconstant to includearia; updated leading comment to note the TH-007 Part 1 application. No database migration required —ariaalready exists inpublic.agentswithworkspace_id IS NULLper G11 canonical seat seed (verified: 1 row). Closing-loop signal achieved. Post-apply smoke A (triggered_by=th007_part1_aria_smokeagainsttest-harness-synthetic-council, bothcouncil-deliberateandtest-harness-synthetic-councilredeployed so the bundled_shared/council-seats.tspicks up aria): HTTP 200,total=7, passed=7, failed=0, by_action={auto:7, confirm:0, drop:0}. Scenario 7 (canonical_seat_drift) flipped frompassed=false, confidence_action=confirm(pre-fix nightly) topassed=true, confidence_action=auto—canonical_in_agents_tableandinstantiated_in_council_deliberatenow both equal[aria, kyle, regression_watcher, schema_auditor, scanner],diff.canonical_not_instantiated=[],diff.instantiated_not_canonical=[]. Smoke B (test-harness-drift-simulators): HTTP 200,contaminated=false, 14 rows written,sentinel_residue=0; the 2 pre-existingconfirmrows (drift_REGRESSION_RISK,drift_TRAVERSABILITY_VIOLATION) are invariant under this change per prompt §8B (Aria's stub dispatches no seat-detection scenarios).SWEEP-ARIA-SEAT-INSTANTIATION-001✅ closed.SWEEP-ARIA-AMENDMENT-001remains open (deferred post-foundation sub-pack — the seat slot, type union, verdicts record entry, and Promise.all spawn are now in place; only the function body needs replacement when her real logic ships). Discipline: SR-003 N/A (no function body reproduced); SR-005 N/A (no new SECDEF functions); SR-006 N/A (no new tables); D9 ✅ (stub approves, doesn't veto); D10 ✅ (nois_compliantwrite path); no writes tocouncil_deliberations/council_votes. Closing-loop property: scenario 7 in TH-002 will continue to pass on every subsequent nightly run; the first post-foundation nightly will show the flip as durable evidence incouncil_test_runs. TH-007 Part 2 (G14 migration) next. -
TEST_HARNESS-006 (Nightly orchestrator + vault-backed pg_cron):
PROMPT-TEST_HARNESS-20260612-0006. New edge functionsupabase/functions/test-harness-nightly/index.tsinvokes the three TH sub-functions (test-harness-synthetic-council,test-harness-drift-simulators,test-harness-resolver-sweep) in parallel, waits up to 5 minutes for completion via verify-by-count polling oncouncil_test_runs(scenario_kind+triggered_by+occurred_at >= run_started_at), and writes onescenario_kind='nightly_batch',scenario_name='nightly_orchestrator'summary row with full per-sub-function expected/actual/completed records. Expected counts queried at run start — constant7for synthetic_council (scenarios fixed in TH-002 source),count(*) FROM public.dissent_ontologyfor drift_simulation (14 today), TH-004's filterintroduced_in <> 'reserved' AND graph_participant = truefor resolver_sweep (63 today). No hardcoded counts; the orchestrator tracks platform growth automatically. Triggered_by wrapping: orchestrator suffixes the incomingtriggered_bywith__<8-char run_id>so verify-by-count is unambiguous across overlapping manual+cron runs. Confidence semantics for nightly_batch: all-completed →passed=true, score=0.95, action=auto(HTTP 200); partial completion (>0 actual, < expected) →passed=false, score=0.80, action=confirm(HTTP 207 Multi-Status); any sub-function 0 actual or dispatch 4xx/5xx →passed=false, score=0.30, action=drop(HTTP 500). Dispatch failures abort before waiting and persist a drop-band row immediately. New edge functionsupabase/functions/test-harness-bootstrap-vault/index.tsmirrors SOT-OPS-001'ssot-bootstrap-vaultpattern — readsTEST_HARNESS_NIGHTLY_WEBHOOK_SECRETfrom edge env and idempotently UPSERTs intovault.secretsviapublic.sot_seed_vault_secretRPC. New runtime secretTEST_HARNESS_NIGHTLY_WEBHOOK_SECRETregistered viasecrets--add_secret; seeded into vault (vault_seed: "created"). Migration: new pg_cron jobtest-harness-nightlyat0 3 * * *UTC, idempotent (unschedule-then-schedule), vault-backed viavault.decrypted_secrets WHERE name='TEST_HARNESS_NIGHTLY_WEBHOOK_SECRET'. Mirrors SOT-OPS-001's vault-backed cron pattern verbatim. Concurrent withmbo-scheduled-site-auditper ADR D7 (same cron expression, different jobname). Inline smoke asserts (a) job scheduled, (b) command does not containREPLACE_WITHplaceholder, (c) command referencesvault.decrypted_secrets(not literal secret), (d) command targetstest-harness-nightly. Post-deploy smoke (triggered_by=th006_apply_smoke): HTTP 200,all_completed=true,elapsed_ms=33556(~10× under the 5-min deadline — matches TH-006 prompt's~30s expensive sub-functionmath).council_test_runsshows exactly 85 rows from one orchestrator invocation:synthetic_council=7, drift_simulation=14, resolver_prefix_sweep=63, nightly_batch=1. The singlenightly_batchrow:passed=true, confidence_action=auto, confidence_score=0.95, duration_ms=33556. Discipline: SR-003 N/A (no function body reproduced); SR-005 N/A (no new SECDEF functions;sot_seed_vault_secretalready has SR-005 from SOT-OPS-001); D2 ✅ (nightly_batchscenario_kind reserved at TH-001 now in use); D3 ✅ (edge function, not test file); D7 ✅ (concurrent withmbo-scheduled-site-auditat 03:00 UTC); D9 ✅ (nightly_batch row writes regardless of sub-function outcome — evidence, not veto); D10 ✅ (nois_compliantwrite path); D11 ✅ (0.95/0.80/0.30 bands extended cleanly to orchestrator level); vault-backed cron pattern (SOT-OPS-001 lineage) ✅. Foundation pack 8 of 8 complete — TH-007 (G14 + SR-003/004/005/006 promotions + retro closeout) next. -
TEST_HARNESS-005 (Query surface over
council_test_runs):PROMPT-TEST_HARNESS-20260612-0005. New views and RPC providing the read surface over per-row confidence data already written by TH-002/003/004. New viewpublic.vw_test_harness_daily_summary(security_invoker=true) — daily pass/fail rollup byscenario_kind, grain(UTC date, scenario_kind), exposes pass/fail/auto/confirm/drop counts plus avg/min/max confidence andpass_pct. New viewpublic.vw_test_harness_attention_required(security_invoker=true) — rows whereconfidence_action IN ('confirm','drop'), sorted drop-first then confirm both newest-first; pairs with existingidx_council_test_runs_attention_requiredpartial index from TH-001. New viewpublic.vw_test_harness_pass_rate_trend(security_invoker=true) — 7-day rolling pass rate perscenario_kindover the trailing 30 days, function-of-now (no materialization, no staleness); sparse-window dates showrows_in_window=0androlling_7d_pass_pct=NULL. New RPCpublic.get_test_harness_summary(p_days int DEFAULT 7)—SECURITY DEFINER,STABLE,search_path=public, typedRETURNS TABLEfor programmatic dashboards and future PULSE consumption; clampsp_daysviagreatest(p_days, 1)so zero/negative input is treated as 1 day; returns aggregate counts and date ranges only, no row-level data, so the elevated privilege does not breach workspace isolation. SR-005 application #4 (sharpened three-role REVOKE/GRANT): REVOKE EXECUTE from PUBLIC, anon, AND authenticated; then GRANT EXECUTE to service_role and authenticated. Brings SR-005 candidate to 4 applications, promotion-eligible at TH-007 retro. Honest scope clarification: TH-005's original roadmap line read "confidence-scoring layer + D8 application" — both halves already shipped in TH-002/003/004 (per-rowconfidence_score numeric(3,2)+confidence_action; structural jsonbexpected_outcome/actual_outcome). TH-005's actual remaining work was making the data queryable; that is what this delivers. Post-deploy smoke:vw_test_harness_daily_summary→ 3 rows (one per scenario_kind seen so far),vw_test_harness_attention_required→ 67 rows (matches TH-003 designed-evidence confirms + TH-004 skipped/failed prefixes),vw_test_harness_pass_rate_trend→ 93 rows (3 scenario_kinds × 31 days, mostlyrows_in_window=0); RPC executes for authenticated/service_role roles. New sweeps filed:SWEEP-GRAPH-NODE-BACKFILL-001(umbrella for TH-004'sresolve_AUDIT+resolve_RVfound=falsefindings — root cause likely one of: rows pre-date graph_node registration, schema_auditor seat detector for TRAVERSABILITY_VIOLATION incomplete, or registry incorrectly marks those row types as graph_participant);SWEEP-PREFIX-EMPTY-AUDIT-001(29 of 63 prefixes with no live rows yet — TH-007 retro review). Discipline: SR-003 N/A (no function body reproduced); SR-005 ✅ application #4; D3 ✅ (pure SQL); D9 ✅ (read-only consumer of evidence); D10 ✅ (nois_compliantwrite path); D11 ✅ (bands now queryable); no writes tocouncil_deliberations/council_votes; no new tables. -
TEST_HARNESS-004 (
test-harness-resolver-sweepedge function):PROMPT-TEST_HARNESS-20260612-0004. New edge functionsupabase/functions/test-harness-resolver-sweep/index.tssweeps every active serial prefix inpublic.serial_registry(filter mirrors G11:introduced_in <> 'reserved' AND graph_participant = true) throughpublic.resolve(text)and writes onecouncil_test_runsrow per prefix withscenario_kind='resolver_prefix_sweep'. Prefix count at apply: 63 active graph-participating prefixes swept. Per-prefix assertions: samples most-recent live serial from the prefix'stable_name(fallback to unordered whencreated_atis absent); callspublic.resolve(p_serial:=sampled); assertsfound=true, all 18 required envelope keys present,resolver_version='1.3.0',serialechoed correctly. Full resolver envelope stored inactual_outcome.envelopefor diff detection over time and PULSE training data when PULSE ships. Confidence bands (D11): pass → 0.95auto; envelope deviation → 0.85confirm; empty-prefix skip → 0.85confirm(surfaced for review, not silenced); RPC error or thrown → 0.30drop. No contamination gate (per prompt §2):resolve()isSTABLE SECURITY DEFINERwith no write paths in canonical body; surface is too narrow to justify the symmetry cost; SR-003 audit independently catches resolver write-path regressions. Post-deploy smoke (triggered_by=th004_apply_smoke, two invocations because the first request held the HTTP socket past the curl tool's 60s timeout — server-side completed cleanly both times): 63/63 prefixes covered per run, 126 council_test_runs rows total across the two runs. Per-run shape: ~32passed(resolver returned full envelope, version match, serial echo, found=true), ~29skipped(no live rows in source table —passed=true, skipped=true, confidence_action=confirm), 2failed(confidence_action=confirm):resolve_AUDITandresolve_RVboth returnedfound=false, serial_echo=true, version=ok, missing=[]— sampled serial exists in source table but is not registered ingraph_node_by_serial. Per D9/D10 the failures are recorded as evidence (confirmband) and do not flipis_compliant; they are the kind of TRAVERSABILITY-adjacent finding TH-004 exists to surface for human follow-up. SR-001/SR-002 N/A (no migration); SR-003 N/A (asserts documented envelope shape, doesn't reproduce a function body); PRE-007 N/A. -
TEST_HARNESS-003 (
test-harness-drift-simulatorsedge function + synthetic workspace/codex_job seed):PROMPT-TEST_HARNESS-20260612-0003. New edge functionsupabase/functions/test-harness-drift-simulators/index.tsruns 14 simulators (one per activepublic.dissent_ontologycode;SELF_MODIFICATION_BYPASSdeferred to PULSE-004) and writes onecouncil_test_runsrow per scenario withscenario_kind='drift_simulation'. Hybrid design (Choice 3): 7 Option B simulators (SERIAL_VIOLATION,NAMING_VIOLATION,REGRESSION_RISK,HUMAN_REQUIRED,TRAVERSABILITY_VIOLATION,MISSING_SITE_TAB,RESOLVE_BYPASS) construct syntheticDiffContext+Envelopeand invoke the corresponding seat function directly; 7 Option A simulators (RLS_GAP,SCHEMA_DRIFT,ARCHITECTURE_CONFLICT,MISSING_PAGE_RECORD,SCOPE_CREEP,DEPENDENCY_UNMET,INSUFFICIENT_DIAGNOSIS) are tally-only placeholders for codes with no seat detector — they construct aSeatVerdict[]with the target code and assert correct routing throughdetermineNextStatusfor round 1 (amend vs human_required) and round 3 (deadlock vs human_required). Inlining decision: Supabase Edge Functions deploy independently per-folder, so cross-functionimportfrom../council-deliberate/index.tsfails to bundle. The 5 seat function bodies are inlined verbatim fromcouncil-deliberate/index.tsHEAD0753e5f— same SR-003 canonical-preservation discipline as the tally block; when seat logic changes upstream, both copies must be updated together. Contamination gate (D9 critical defense, Brian directive): every run recordsrun_started_at, then after all simulators complete assertscount(*) FROM council_deliberations WHERE created_at >= run_started_at = 0ANDcount(*) FROM council_votes WHERE submitted_at >= run_started_at = 0(both tables per refinement). If either trips → scenario rows NOT written, singlecontamination_gate_trippedrow written withconfidence_score=1.00, confidence_action=confirm(the only TH write path that speaks with maximum certainty), HTTP 500 returned. Sentinel scan (Q2B): scanscouncil_deliberations.pr_title LIKE 'TH003_DRIFT_SIM__%'— if found after Q2A passed, writes acontamination_sentinel_residueevidence row (does not block run). Synthetic envelopepr_title='TH003_DRIFT_SIM__<scenarioName>__<runId>'is the sentinel signature. Migration: seeded syntheticpublic.workspacesrow00000000-0000-0000-0000-000000000003(owner_user_id=first existing auth user, name 'TH003 Synthetic') +public.codex_jobsrow00000000-0000-0000-0000-000000000004(vertical_slug='th003-synthetic', minimalresolved_vertical_contract). These are passed into seat envelopes as context only — no row written by any simulator references them. SR-003 (8th application):DissentCode,SeatVerdict,DiffContext,Envelope,HARD_VETO_CODES,determineNextStatus+ all 5 seat-function bodies reproduced verbatim fromcouncil-deliberate/index.tsHEAD0753e5f. Confidence bands (D11): Option B pass → 0.90auto/ fail → 0.80confirm/ thrown → 0.30drop; Option A pass → 0.95auto/ fail → 0.85confirm/ thrown → 0.30drop; contamination event → 1.00confirm. Post-deploy smoke (triggered_by=th003_apply_smoke): HTTP 200 (gate clean — no contamination on apply),total=14, passed=10, failed=4, by_action={auto:10, confirm:4, drop:0}, by_mode={option_b_detection:7, option_a_tally:7}, sentinel_residue=0, contaminated=false. Passes (10): all 7 Option A tally checks +MISSING_SITE_TAB(diff-stage detection fires on syntheticCREATE TABLE ... site_id uuidwithout graph_node row) +NAMING_VIOLATION(scanner flagspublic.TH003SyntheticCamelCase) +RESOLVE_BYPASS(kyle'sassert_resolvedRPC detects synthetic serial with no resolve() evidence). Designed-evidence failures (4 →confirm):SERIAL_VIOLATIONandTRAVERSABILITY_VIOLATION(only reachable viaserial_compliance_checkpost-state — platform currently compliant, so seat returns approve);REGRESSION_RISK(synthetic workspace has nopagesrows withknown_issues, so seat approves);HUMAN_REQUIRED(state_verifier emits only whenis_compliant=false— pass/fail correctly reflects platform compliance, not a simulator bug). Per D9/D10, these failures route toconfirmfor human review without flippingis_compliant. New sweep filed:SWEEP-DISSENT-DETECTOR-COVERAGE-001— 7 dissent codes in the ontology have no seat detector. Promotion path: when a detector ships for any of these 7 codes, replace the corresponding Option A simulator with Option B in the same migration;council_test_runsis append-only and self-describing viaactual_outcome.detector_mode, so no backfill is required. SR-001/SR-002 N/A (no FK/manifest changes); SR-003 satisfied (8th application — full canonical preservation across tally + 5 seats); PRE-007 N/A. -
TEST_HARNESS-002 (
test-harness-synthetic-counciledge function +_shared/council-seats.ts):PROMPT-TEST_HARNESS-20260612-0002. New edge functionsupabase/functions/test-harness-synthetic-council/index.tsruns 7 deterministic scenarios overdetermineNextStatusand writes onecouncil_test_runsrow each per TH-000 D9 (evidence, not vetoes) and D10 (independent of guard outcomes). New shared modulesupabase/functions/_shared/council-seats.tsis the source of truth for instantiated voting seats (INSTANTIATED_VOTING_SEATS= kyle/scanner/schema_auditor/regression_watcher;INSTANTIATED_OBSERVER_SEATS= state_verifier). Imported bycouncil-deliberate(documentation-only, no behavior change) andtest-harness-synthetic-council(scenario 7). Scenarios 1–5 exercise the four tally outcomes (resolved, round_N_amending, deadlocked, human_required) and the hard-veto path; scenario 6 is the SOT-004 observer-filter regression test (with counterfactual sanity check); scenario 7 catches canonical-vs-instantiated voting seat drift. SR-003 (7th application):determineNextStatusandHARD_VETO_CODESreproduced verbatim fromcouncil-deliberate/index.tsHEAD0753e5f. Confidence bands (D11): pure-tally scenarios → 0.95auto(pass) / 0.90auto(fail); scenario 7 drift → 0.95auto(match) / 0.90confirm(drift); thrown error → 0.30drop. Post-deploy smoke (triggered_by=th002_apply_smoke): HTTP 200,total=7, passed=6, failed=1, by_action={auto:6, confirm:1, drop:0}. Scenario 7 currently fails (passed=false, confidence_action=confirm) becauseariais canonical inpublic.agentsper G11 but not instantiated incouncil-deliberate— exactly the planning-context drift TEST_HARNESS exists to surface. Per D10, this failure does not flipis_compliant. New sweeps filed:SWEEP-ARIA-SEAT-INSTANTIATION-001(Gap 1, ~10-line fix).SWEEP-ARIA-AMENDMENT-001already on file (Gap 2, full sub-pack from COUNCIL ADR Q7). No graph wiring changes (edge function, not a node;council_test_runsalready wired in TH-001). -
TEST_HARNESS-001 (
council_test_runstable + CTR serial + graph wiring + RLS + indexes):PROMPT-TEST_HARNESS-20260611-0001, baseline06e7495. SQL migration + companion TS edit. Migration: newpublic.council_test_runstable (append-only log of synthetic Council scenarios, drift simulations, resolver prefix sweeps, nightly batch) with locked vocab —scenario_kindCHECK (4 values),confidence_actionCHECK (3 bands:auto/confirm/drop),confidence_scoreCHECK [0,1]. Nullableworkspace_idFK withON DELETE CASCADE(platform-global pattern mirroringplatform_state). RLS enabled with 3 policies (workspace-scoped read, authenticated read of platform-global rows, service_role full); GRANTSELECTto authenticated, GRANTSELECT,INSERT,DELETEto service_role — no UPDATE grant, append-only by privilege. 4 indexes:(scenario_kind, occurred_at DESC),(passed, occurred_at DESC),(scenario_name, occurred_at DESC), partial(confidence_action, occurred_at DESC) WHERE confidence_action <> 'auto'. CTR serial trigger:public.tg_serial_council_test_run()SECURITY DEFINER withSET search_path = public, emitsCTR-GLOBAL-YYYYMMDD-NNNviambo_generate_serial('CTR','GLOBAL'). SR-005 candidate discipline applied: REVOKE EXECUTE FROM PUBLIC, anon, authenticated (smoke verifiedhas_function_privilege('authenticated', ..., 'EXECUTE')=false). Serial registry: CTR prefix row inserted (graph_participant=true). Graph wiring (SR-002):graph_noderowcouncil_test_run(terminal, has_workspace_id, site_tab_state='infrastructure') +graph_manifestsoft edgecouncil_test_run → workspace via workspace_id. Companion TS edit tosrc/lib/worldportGraph.ts: added'council_test_run'toGraphNodeTypeunion,NODE_TABLEentry, andGRAPH_EDGESsoft edge — SR-002 sync preserved. SR-006 candidate discipline:tg_ensure_graph_edge_status_row_council_test_runsAFTER INSERT trigger attached. Inline smoke (10th SR-004 application): asserts 5 indexes, 3 RLS policies, CTR registry row, graph_node row, manifest edge, SR-005 REVOKE, SR-006 ledger trigger, CTR-GLOBAL-* serial generation, 3 CHECK rejections (invalid scenario_kind, confidence_score>1, invalid confidence_action), cleanup, andis_compliant=true. Apply note: first attempt failed self-check becauseREVOKE ... FROM PUBLIC, anonleft an implicit grant toauthenticated; resolved by adding explicitREVOKE EXECUTE FROM authenticated. SR-001 N/A pending manifest_fk_sweep run; SR-002 satisfied (TS + DB landed together); SR-003 satisfied (all referenced objects verified vs canonical migrations per prompt SR-003 table); PRE-007 satisfied. 77 pre-existing linter warnings unchanged. Unblocks TH-002 (test-harness-synthetic-counciledge function). -
SOT-OPS-001 (vault-backed cron secret for
state-verifier—SWEEP-STATE_OF_TRUTH-CRON-SECRET-VAULTING-001resolved for SOT): SQL migration + new edge function. Part A (migration): createdpublic.sot_seed_vault_secret(p_name text, p_value text)SECURITY DEFINER helper (service_role only, REVOKE EXECUTE FROM PUBLIC/anon/authenticated) — idempotently UPSERTs intovault.secretsso the cron command can read viavault.decrypted_secretswithout holding the literal in pg_cron's command text. Rescheduledsot-state-verifier-hourly: command now resolvesAuthorization: Bearer||(SELECT decrypted_secret FROM vault.decrypted_secrets WHERE name='STATE_VERIFIER_WEBHOOK_SECRET' LIMIT 1)(mirrors READER-004 /mbo-scheduled-site-auditpattern). Part B (edge function):supabase/functions/sot-bootstrap-vault/index.ts— one-shot env→vault mirror readsSTATE_VERIFIER_WEBHOOK_SECRETfrom Deno env, calls the seeding RPC with service-role client, then exercisesstate-verifierwith the live bearer to confirm end-to-end. Secret registration:STATE_VERIFIER_WEBHOOK_SECRETadded viasecrets--add_secret(auto-provisioned into Supabase env; not in repo). Verification (live): bootstrap returned{ok:true, vault_seed:"created", verifier_status:200};platform_stateshows onestate_verifier_assertionrow (event_source='state-verifier',metadata->>'source'='manual_ops_001_verification',is_critical=false, summary "1 non-critical check(s) failed" — C4 council throughput, expected);cron.jobconfirms command text referencesvault.decrypted_secrets(no literal secret). Removed stalesrc/routes/api/public/sot-bootstrap-vault.ts(edge function path chosen for deploy speed). SR-001/SR-002 N/A; PRE-007 satisfied (REVOKE on new SECDEF).SWEEP-STATE_OF_TRUTH-CRON-SECRET-VAULTING-001→ ✅ RESOLVED for the SOT-005 cron; READER-004 + future PULSE-005 still pending the same refactor. -
STATE_OF_TRUTH-006 (pack closeout: G13
platform_state_event_kind_drift+ retrospective +STATE_OF_PLATFORM.mdarchive):PROMPT-STATE_OF_TRUTH-20260611-0006, baseline53eec52. SQL migration + docs. Sixth SR-003 application — full body ofpublic.serial_compliance_check()preserved verbatim from canonical20260611224500_*.sqllines 52–268; three minimal additions only: (a) declarev_event_kind_drift jsonb, (b) G13 SELECT block immediately after G12 computingunknown_event_kindrows frompublic.platform_statewhoseevent_kindis NOT in the canonical 12-value list inlined as a VALUES list (same posture as G11's seat list — CHECK constraint + G13 NOT IN clause must be amended together), (c)platform_state_event_kind_driftadded to RETURN map and to theis_compliantAND-chain. Sevenis_compliant-contributing guards now active: G2, G7, G8/G9, G10, G11, G12, G13. SECDEF /SET search_path/REVOKE EXECUTE FROM PUBLIC/ GRANTs preserved verbatim. Inline smoke (3 phases): asserts field present, G13 array empty (no event_kind drift in live data),is_compliant=true(9th SR-004 application). Docs: createddocs/STATE_OF_TRUTH_PACK_RETROSPECTIVE.md(serialRETRO-STATE_OF_TRUTH-20260611-0001) — what shipped across 7 prompts, what we learned (live-tree probing, Lovable apply-time corrections as signal, placeholder→fulfillment contract pattern, Path A/B cleavage), Standing Rules status (SR-004 recommended for promotion to ACTIVE; SR-005 + SR-006 filed as new single-instance candidates from SOT-001's G7/G8 corrections), 7 deferred sweeps catalogued (amendment-emit, trigger-audit-rpc, cron-secret-vaulting, migration-events, deployment-events, confidence, retention), pack metrics + TEST_HARNESS handoff. Archiveddocs/STATE_OF_PLATFORM.md→docs/_archive/STATE_OF_PLATFORM_20260611.md; new stub atdocs/STATE_OF_PLATFORM.mdredirects toplatform_state/mv_current_platform_statequeries and the retrospective. Updateddocs/FOUNDATION_ROADMAP.mdrow 7 →✅ COMPLETE. Appended pack-closeout note todocs/COUNCIL_PR_REVIEW_CHECKLIST.md. SR-001/SR-002 N/A (no FK/manifest changes); SR-003 satisfied (sixth application — canonical body preserved verbatim, three additions only); PRE-007 satisfied (REVOKE EXECUTE preserved verbatim). 77 pre-existing linter warnings unchanged. STATE_OF_TRUTH pack CLOSED. Foundation status: 7 of 8 packs complete (SCHEMA_AUDITOR_v2, SERIAL, TRAVERSABILITY, RESOLVER, COUNCIL, SITE_PAGE_VIS, STATE_OF_TRUTH). Next: TEST_HARNESS (pack 8 — closes foundation). -
STATE_OF_TRUTH-005 (
state-verifieredge function + hourly pg_cron +pr_mergedPath B):PROMPT-STATE_OF_TRUTH-20260611-0005, baseline74e3344. Three-part commit: new edge function + SQL migration (cron schedule + smoke) + TS edit to existingcodex-pr-completed. Part A: newsupabase/functions/state-verifier/index.ts— Bearer-auth (STATE_VERIFIER_WEBHOOK_SECRET) Deno handler runs 5 checks in parallel and emits onestate_verifier_assertionrow toplatform_stateper invocation (entity_serial='STATE-VERIFIER-PLATFORM',event_source='state-verifier',emitted_by='state-verifier'). Checks: C1 compliance spine (serial_compliance_check ->> is_compliant, critical), C2 MV freshness (advisory), C3 refresh log health (medium), C4 council throughput (stuck non-terminal jobs >24h, low), C5 trigger audit (deferred to v1 info-only —SWEEP-STATE_OF_TRUTH-TRIGGER-AUDIT-RPC-001files for v2 SECDEF RPC).is_criticalmetadata flag set when any critical check fails (evidence-only; no auto-escalation). Part B (migration): Block 1 idempotentcron.unschedule+cron.schedule('sot-state-verifier-hourly', '0 * * * *', ...)invokesnet.http_postto the state-verifier function URL with Bearer placeholderREPLACE_WITH_STATE_VERIFIER_WEBHOOK_SECRET(mirrors READER-004 SCHEDULED_AUDIT_WEBHOOK_SECRET pattern; operator must substitute or refactor to vault.secrets). Block 2 inline smoke asserts cron job registered +is_compliant=true(8th SR-004 application). Part C (TS edit):codex-pr-completed/index.ts— after the existingagent_dispatch_logmergedinsert, added best-effortplatform_stateINSERT withevent_kind='pr_merged',event_source='codex', metadata carrying codex_job_id/pr_number/pr_url/vertical_slug/merge_commit_sha/head_ref/github_action. Wrapped in try/catch — merge has already happened; audit-log failure must not block downstream. SR-001/SR-002 N/A (no FK/manifest changes); SR-003 satisfied (every column + RPC verified vs SOT-001/SOT-002/SOT-003 canonical sources). PRE-007 satisfied — no new SECDEF functions; pg_cron HTTP-call pattern mirrors READER-004. After this: state-verifier produces hourly evidence rows; all 3 Path B emitters live (council_deliberation_opened/council_deliberation_closedSOT-002 +pr_mergedSOT-005); all 3 spec'd event sources active (trigger/council/codex/state-verifier).council_amendment_proposedremains vocabulary-only per deferredSWEEP-STATE_OF_TRUTH-AMENDMENT-EMIT-001. Env:STATE_VERIFIER_WEBHOOK_SECRETmust be set in Supabase env before the first scheduled run. Unblocks SOT-006 (pack closeout: G13 platform_state_event_kind_drift, STATE_OF_PLATFORM archive, retrospective, FOUNDATION_ROADMAP row 7 → COMPLETE). -
STATE_OF_TRUTH-004 (State Verifier observer seat: CHECK extensions + seed +
seatStateVerifier()wiring):PROMPT-STATE_OF_TRUTH-20260611-0004, baseline6d7aa75. SQL migration + TS edits tosupabase/functions/council-deliberate/index.ts. Block 1: extendedagent_seatCHECK constraints on bothcouncil_deliberationsandcouncil_votesto accept'state_verifier'(DO-block discovers auto-named constraint viapg_constraint+pg_get_constraintdefLIKE%agent_seat%, drops it, re-adds named<table>_agent_seat_checkwith 6-value list). Block 2: seededagentsrow(workspace_id=NULL, slug='state_verifier', name='State Verifier', role='observer', status='idle', provider='council')ON CONFLICT (slug) DO NOTHING — platform-global observer (NOT in G11 canonical 5-seat list, per architectural distinction: voting Council seats are constitutional, State Verifier is constitutional-adjacent infrastructure verified by SOT-006's G13). Block 3 (inline smoke): asserts seat present withrole='observer'+workspace_id IS NULL, both CHECK constraints includestate_verifier, CHECK still rejects inventedinvented_seat(real INSERT against any workspace, caughtcheck_violation), andis_compliant=true(G11 unaffected). TS edits (3, same commit): (B1)SeatSlugunion extended to add"state_verifier"(line 50). (B2)seatStateVerifier()added afterseatRegressionWatcher— thin v1 probe: readsserial_compliance_checkRPC; emitsHUMAN_REQUIREDdissent ifis_compliant=falseor RPC errors, else approves. Observer reason code isHUMAN_REQUIREDnotSERIAL_VIOLATION/TRAVERSABILITY_VIOLATION(those belong to Schema Auditor; State Verifier signals "recorded state vs reality"). (B3) Dispatch fan-out expanded 4→5Promise.allawaits +verdictsrecord gainedstate_verifierkey +persistVotesunchanged (all 5 persist as audit trail) +determineNextStatuscall explicitly excludes observer ([kyle, scanner, schemaAuditor, regWatcher]— inline comment marks the architectural propertyaudit-but-not-voteso future readers don't "fix" the apparent omission). SR-001/SR-002 N/A (no FK/manifest changes); SR-003 satisfied (every column + constraint value verified against canonical sources before draft). 77 pre-existing linter warnings unchanged. After this: State Verifier deliberation participation is wired; observer verdict persists for audit but is filtered from consensus; CHECK constraints accept'state_verifier'for persistence. Unblocks SOT-005 (state-verifieredge function with richer periodic assertion + cron schedule + Path Bpr_mergedemission fromcodex-pr-completed). -
STATE_OF_TRUTH-003 (Resolver v1.3.0 — replace
recent_state_changesplaceholder + addcurrent_statefield):PROMPT-STATE_OF_TRUTH-20260611-0003, baseline5c05e73. SQL migration only. 5th application of SR-003 verbatim-canonical-preservation discipline: full body ofpublic.resolve(text)preserved byte-for-byte from canonical20260611185910_*.sqllines 1–217 with three minimal additions clearly marked NEW (SOT-003). Change 0: addedv_current_state jsonb := NULLto DECLARE. Change 1: replaced placeholderrecent_state_changesquery (wascouncil_deliberationsLIKE-match, COUNCIL-006 placeholder) with read frompublic.platform_state WHERE entity_serial = p_serial ORDER BY occurred_at DESC LIMIT 10; outerjsonb_agg(jsonb_build_object(...))shape preserved; field shape nowkind/at/source/summary/metadata(kind-agnostic — replaces Council-specificagent_seat/verdict/reason_code). Change 2: addedSELECT to_jsonb(c.*) - 'workspace_id' INTO v_current_state FROM public.mv_current_platform_state c WHERE c.entity_serial = p_serial—workspace_idstripped (redundant with parent response'shas_workspace_idflag); NULL default distinguishes "no events" from "empty state". Change 3: addedcurrent_statekey to both the main RETURN block and the malformed_serial early-return; bumpedresolver_versionfrom'1.2.0'→'1.3.0'in both places. SECDEF/STABLE/SET search_path preserved; REVOKE EXECUTE FROM PUBLIC + anon, GRANT EXECUTE to authenticated + service_role preserved verbatim from COUNCIL-006 (PRE-007 / G7 discipline). Inline smoke (3 phases): (1) structural — picks sample serial, assertsresolver_version='1.3.0', all three fields (recent_state_changes/open_deliberations/current_state) present, recent_state_changes is array, current_state is null|object. (2) data-level roundtrip — finds real incident serial, inserts syntheticplatform_staterow (state_verifier_assertion/manual), refreshes MV, assertsrecent_state_changescontains the seed kind,current_state.current_event_kind = 'state_verifier_assertion', andcurrent_statedoes NOT carryworkspace_id; cleanup deletes seed + re-refreshes MV. (3)is_compliant=trueassertion (7th SR-004 application). SR-001/SR-002 N/A (no schema/manifest changes); SR-003 satisfied (5th application — verification table cites every referenced object to canonical source). TS consumers forward-compatible by inspection (recent_state_changes: unknown[]atsrc/lib/resolve.functions.ts:47; no version-comparison consumers); UI placeholder text atResolvedPanel.tsx:303left intact — frontend wire-up deferred to SOT-006 (out of scope here). 77 pre-existing linter warnings unchanged. Closes COUNCIL-006 placeholder contract ("STATE_OF_TRUTH SOT-003 will replace with proper platform_state read"). After this: resolver returnscurrent_statefor any entity with aplatform_stateevent;recent_state_changessurfaces the full 12-kind vocabulary, no longer council-only;resolver_version='1.3.0'makes the contract change auditable. Unblocks SOT-004 (State Verifier observer seat incouncil-deliberate). -
STATE_OF_TRUTH-002 (
mv_current_platform_state+ wrapped refresh + pg_cron + Council Path B emission):PROMPT-STATE_OF_TRUTH-20260611-0002, baseline96b3dab. SQL migration + TS edits tosupabase/functions/council-deliberate/index.ts. Block 1:public.mv_current_platform_statematerialized view —DISTINCT ON (entity_serial) ORDER BY entity_serial, occurred_at DESCprojectingcurrent_event_kind/current_event_source/current_state_snapshot/current_state_since/current_summary/current_metadata/workspace_id. Unique index onentity_serial(REFRESH CONCURRENTLY req), partial index on(workspace_id, current_state_since DESC) WHERE workspace_id IS NOT NULL, btree oncurrent_event_kind. GRANT SELECT to authenticated + service_role. Block 2:public.refresh_mv_current_platform_state_logged()SECURITY DEFINER, mirrors SERIAL-008e — INSERTsmv_refresh_logrow (running), runsREFRESH MATERIALIZED VIEW CONCURRENTLY, UPDATEs log row with duration_ms/rows_affected/status=success; on EXCEPTION updates log with status=error + SQLERRM (does NOT re-raise — error row IS the audit trail). REVOKE EXECUTE FROM PUBLIC/anon/authenticated; GRANT EXECUTE TO service_role (G7 discipline per SOT-001 Lovable correction). Block 3: pg_cronsot-mv-current-platform-state-refreshscheduled*/5 * * * *; idempotent viacron.unschedule+cron.schedulewrap. Block 4: Initial non-CONCURRENT REFRESH primes the view. Block 5 (inline smoke): asserts MV exists, function exists, authenticated has NO EXECUTE, cron job scheduled, refresh logs success row, manualpr_mergedseed gets PSTA serial + is queryable + appears in MV after refresh, cleanup leaves MV consistent, CHECK constraint rejects inventedevent_source, andis_compliant=true. Smoke deletes the seed row before completion. TS edits (Path B emission, 2 of 3 events — 3rd iscouncil_amendment_proposed, deferred): (1)runDeliberationRound— capturespriorCouncilStatusvia best-effort SELECT before thecodex_jobs.council_statusUPDATE; after the UPDATE, ifnextStatus ∈ {resolved,deadlocked,human_required,clean,flagged}, insertsplatform_staterow withentity_serial='CJOB-<id>',event_kind='council_deliberation_closed',event_source='council', prior_state, next_state, full verdicts in metadata,emitted_by='council-deliberate'. (2) Handler kickoff path — after thepending → round_1UPDATE, insertsplatform_staterow withevent_kind='council_deliberation_opened',event_source='council', next_state={council_status: round_1}, metadata={codex_job_id, pr_number, pr_url, round:1}. Both emissions wrapped in try/catch — Path B is best-effort, deliberation proceeds even if audit insert fails. SWEEP filed:SWEEP-STATE_OF_TRUTH-AMENDMENT-EMIT-001—council_amendment_proposedhas no emitter today (council-deliberate does not write to council_amendments); wires when Aria amendment generation ships (SWEEP-ARIA-AMENDMENT-001). SR-001/SR-002 N/A; SR-003 satisfied (every column verified against SOT-001 locked schema). 77 pre-existing linter warnings unchanged. After this: latest-state projection is live and refreshes every 5 min with audit; 2 of 3 Council Path B events emit explicitly;pr_mergedvocabulary proved end-to-end. Unblocks SOT-003 (resolver bumps to 1.3.0; readscurrent_statefrom MV +recent_state_changesfromplatform_state). -
STATE_OF_TRUTH-001 (
platform_statetable + 12-kind CHECK + RLS + indexes + PSTA serial + graph wiring + 5 trigger attachments):PROMPT-STATE_OF_TRUTH-20260611-0001, baseline4dbd432. SQL migration + companion TS edit (SR-002). Block 1:public.platform_state(id, serial UNIQUE, entity_serial NOT NULL, workspace_id → workspaces ON DELETE CASCADE nullable, event_kind NOT NULL CHECK 12-enum locked at v1 [entity_created/entity_status_changed/entity_archived/council_deliberation_opened/council_deliberation_closed/council_amendment_proposed/pr_merged/migration_applied/deployment_completed/incident_opened/incident_resolved/state_verifier_assertion], event_source NOT NULL CHECK 7-enum [resolver/council/codex/cron/manual/state-verifier/trigger], prior_state/next_state jsonb, summary NOT NULL, metadata jsonb DEFAULT '{}', occurred_at, emitted_by). Block 2: RLS enabled;platform_state_ws_read(SELECT viais_workspace_member),platform_state_global_read(SELECT TO authenticated WHERE workspace_id IS NULL),platform_state_svc_all(ALL TO service_role). No UPDATE policy — append-only by privilege. GRANT SELECT to authenticated; GRANT SELECT,INSERT,DELETE to service_role (no UPDATE grant). Block 3: 5 explicit indexes (entity_serial+occurred_at DESC, kind, occurred_at DESC, workspace_id+occurred_at DESC partial WHERE NOT NULL, occurred_at DESC partial WHERE NULL). Block 4: PSTA prefix registered (graph_participant=true);tg_serial_platform_stateBEFORE INSERT callsmbo_generate_serial('PSTA','GLOBAL')— formatPSTA-GLOBAL-YYYYMMDD-NNN. Block 5:graph_noderow (node_type=platform_state_event, is_terminal=true, has_workspace_id=true, site_tab_state='infrastructure') + 1graph_manifestedge (platform_state_event→workspace via workspace_id, soft, forward). Companion TS edit tosrc/lib/worldportGraph.ts: extendedGraphNodeTypeunion +NODE_TABLE+ appendedGRAPH_EDGESrow (SR-002 satisfied in same commit). Block 5b (correction added during apply):tg_ensure_graph_edge_status_row_platform_state()SECURITY DEFINER +trg_ges_platform_stateAFTER INSERT/UPDATE — callsges_upsert_edge()so G8 manifest-coverage stays at 0 from the first emitted row. Block 6: Two static SECURITY DEFINER trigger functions per ratification flag (no dynamic TG_TABLE_NAME inspection of columns):tg_emit_status_change_to_platform_state()(plainstatuscolumn, defensiveBEGIN ... EXCEPTION WHEN undefined_columnfor workspace_id) andtg_emit_council_status_change_to_platform_state()(codex_jobs.council_status; entity_serial keyedCJOB-<id>since codex_jobs has no serial column). BothREVOKE EXECUTE FROM PUBLIC/anon/authenticated; GRANT EXECUTE TO service_role— keeps G7secdef_authenticated_leaksempty without amending AUDITOR allow-list. 5 trigger attachments:AFTER UPDATE OF statuson deployments/incidents/tasks/agents;AFTER UPDATE OF council_statuson codex_jobs. Block 7 (inline smoke): asserts 5 emit-triggers attached, PSTA serial_registry row present, graph_node + graph_manifest rows present, CHECK rejects inventedevent_kind, real incident status flip (open→investigating→open) emits a PSTA-prefixed row, and(public.serial_compliance_check() ->> 'is_compliant')::boolean = true. Apply note: first attempt failed with G7 (2 SECDEF leaks) + G8 (platform_state_event→workspace coverage gap, ledger_count=0) violations. Re-submission added EXECUTE revokes on all three new SECDEF functions + the Block 5b ledger trigger; smoke then asserted is_compliant=true and migration committed cleanly. SR-001 satisfied (Block 5b + smoke); SR-002 satisfied via companion TS edit; SR-003 satisfied — every column/function cited to canonical migration in the prompt's verification table. Path A (trigger emission) live. Path B (explicit emission incouncil-deliberateandcodex-pr-completed) ships in SOT-002/005. After this:platform_stateis real, queryable, append-only; 12-kind vocabulary locked at DB level; PSTA serials register events; constitutional-level audit trail begins now. Unblocks STATE_OF_TRUTH-002 (mv_current_platform_state+ Path B emission in council-deliberate). -
SITE_PAGE_VIS-005 (Pack closeout: G12
site_tab_coverage_gaps+site_tabs_canonicalregistry):PROMPT-SITE_PAGE_VIS-20260611-0005, baselinea4f141a. SQL migration. Block 1: Createdpublic.site_tabs_canonical(tab_key PK, label, introduced_in, is_ui_only, created_at) with RLS —_readpolicy SELECT to authenticated,_svcpolicy ALL to service_role; GRANTs SELECT to authenticated, ALL to service_role. Seeded with all 13 current tab keys;notes/pipeline/previewflaggedis_ui_only=true(deviation from prompt — see below). Block 2: Replacedpublic.serial_compliance_check()adding G12 (site_tab_coverage_gaps) with two-branch payload —pending_entity(graph_node rows withsite_tab_state='pending'excludingsite_tab_pending_exceptions) andorphan_tab(canonical tab_keys not referenced by anygraph_node.site_tab_key, skippingis_ui_only=true). G12 added to theis_compliantAND-chain. Sixis_compliant-contributing guards now active (G2, G7, G8/G9, G10, G11, G12). Block 3 (inline smoke): Asserts 13 canonical rows, G12 empty,is_compliant=true. Deviation from prompt: prompt's smoke fired on first apply —notes,pipeline,previeware UI-only surfaces not backed by anygraph_nodeentity. Addedis_ui_only booleancolumn tosite_tabs_canonical; G12's orphan-tab branch excludes ui-only rows. FiledSWEEP-SITE_PAGE_VIS-UI-ONLY-TABS-001. SR-001/SR-002 N/A; SR-003 satisfied. 76 pre-existing linter warnings unchanged. Closes SITE_PAGE_VIS pack (6 of 8). Retrospective:docs/SITE_PAGE_VIS_PACK_RETROSPECTIVE.md(RETRO-SITE_PAGE_VIS-20260611-0001). Foundation status: 6 of 8 packs complete (SCHEMA_AUDITOR_v2, SERIAL, TRAVERSABILITY, RESOLVER, COUNCIL, SITE_PAGE_VIS). Next: STATE_OF_TRUTH. -
SITE_PAGE_VIS-004 (Schema Auditor seat:
MISSING_SITE_TABdiff inspection + verdict branching):PROMPT-SITE_PAGE_VIS-20260611-0004, baseline3a8666a. TypeScript-only; single file edit tosupabase/functions/council-deliberate/index.tsreplacingseatSchemaAuditorbody. Stage 1 (pre-merge diff inspection): scans added lines of each migration patch (+-prefixed lines only) forCREATE TABLE public.<name>(...)blocks containingsite_id uuid; for each candidate, searches the entire PR diff for eitherINSERT INTO public.graph_node ... '<name>'OR anUPDATEsettingsite_tab_state='covered'|'infrastructure'near'<name>'. If neither is found → dissentMISSING_SITE_TABwithreason_detail.missing_site_tab_diff[]listing filename/table/needs. Stage 2 (compliance check, post-state): callsserial_compliance_check(). RPC error →HUMAN_REQUIRED(preserved escape hatch). Ifis_compliant=true→ approve. Otherwise routes by populated field:site_tab_coverage_gaps→MISSING_SITE_TAB(forward-compatible — field ships in SITE_PAGE_VIS-005's G12; until then this branch is dormant),manifest_coverage_gaps|dangling_edge_summary→TRAVERSABILITY_VIOLATION, else →SERIAL_VIOLATION(catch-all preserved).KNOWN_REASON_CODESandReasonCodeunion already include both new codes (seeded by COUNCIL-001), so no type/registry edits needed. SR-001/SR-002/SR-003 N/A (no DB or manifest changes). After this: Schema Auditor has typed jurisdiction overMISSING_SITE_TAB/TRAVERSABILITY_VIOLATION/SERIAL_VIOLATION+HUMAN_REQUIREDfallback; missing site-tab coverage caught BEFORE merge via diff inspection; compliance branch ready to auto-route G12 findings when 005 ships. -
SITE_PAGE_VIS-003 (Discussion + Vault tab surfaces +
graph_nodeflip):PROMPT-SITE_PAGE_VIS-20260611-0003, baseline2317c37. Mixed TS + SQL. SQL: Two UPDATEs flipgraph_noderows forsite_discussion→(covered,discussion) andresearch_vault→(covered,vault); DO-block smoke asserts both flips, pending count = 5 (Bucket C set), andis_compliant=true. TS:SiteTabs.tsx—SiteTabKeyunion andSITE_TABSarray gainvault(after notes) anddiscussion(after code_health), 13 entries total.sites.$siteId.tsx— dispatch lines for both new tabs; inlineVaultTabcomponent (readsresearch_vaultfiltered by site_id, ordered by created_at DESC; renders serial/doc_type chips, title, source_url, overview/content preview) withResearchVaultEntrytype adapted to actual table columns (content/overview/doc_typerather than the template'sbody/source_type); inlineDiscussionTab(reads roots only viaparent_id IS NULL, separate reply-count query, in-memory sort: announcements float → open first → newest; renders serial + kind + status chips, title, 200-char body preview, reply count). NoworldportGraph.tsedit (entities already present). SR-001/SR-002 N/A; SR-003 satisfied. 76 pre-existing linter warnings unchanged. After this: two new site tabs render live, transitional pending bucket cleared, only the 5 Bucket C exceptions remain pending — ready for G12 in SITE_PAGE_VIS-005. Unblocks SITE_PAGE_VIS-004. -
SITE_PAGE_VIS-002 (Site-scoped entity audit +
graph_nodeextension +pagesFK + pending-exception registry):PROMPT-SITE_PAGE_VIS-20260611-0002. SQL-only migration. Block 1: Addedpages_site_id_fkey(pages.site_id→sites.idON DELETE CASCADE) — orphan check ran first (0 found); DO-block guarded for idempotency. Block 2: Extendedpublic.graph_nodewithsite_tab_key textandsite_tab_state text NOT NULL DEFAULT 'unscoped', plusgraph_node_site_tab_state_checkCHECK enforcing the 4-state enum (covered|unscoped|infrastructure|pending) per Locked Call 1 (D1). Block 3: Createdpublic.site_tab_pending_exceptions(table_name PK, reason, sweep_serial, introduced_in, expected_resolution, created_at) — RLS enabled,_readpolicy SELECT to authenticated,_svcpolicy ALL to service_role; GRANTs SELECT to authenticated, ALL to service_role. Per Brian's Call 3 spec +expected_resolutionnullable column. Block 4: 25 explicit UPDATEs ratified by Brian — Bucket A covered (9: pages, deployments, design_bridge, code_health_issues, prompt_pack_documents, prompts, blueprints, incidents, tasks); Bucket A-transitional pending (2: research_vault, site_discussion — 003 flips to covered withvault/discussiontab keys); Bucket B infrastructure (9: agentos_connections, browser_sessions, build_chains, context_pointers, documents, email_ingest, email_integration_config, site_integrations, revenue_events —revenue_eventsper Call 4 promotes to covered when revenue tab ships); Bucket C deferred pending (5: fleet_deployments, fleet_templates, github_pull_requests, loops, scheduled_operations). Per Call 5,tenants/telegram_chat_links/freshness_recordsintentionally not updated — nosite_idcolumn; defaultunscopedapplies. Block 5: Seeded 5 exception rows with sweep serials SWEEP-SPV-TAB-{FLEET,GIT,LOOPS,SCHED}-001 — G12 (ships in 005) will read this to exclude rows from pending_entity violations. Block 6 (inline smoke): Asserted 0 orphan pages, FK installed, both new columns exist, bucket distribution (covered≥9, infrastructure≥9, pending=7), 5 exceptions seeded, and(public.serial_compliance_check() ->> 'is_compliant')::boolean = true. SR-001 satisfied (post-apply smoke confirms manifest↔FK sync); SR-002 N/A (graph_node extension server-side only); SR-003 satisfied — all 24 entity node_type values Python-parsed from migration tree and verified verbatim. 76 pre-existing linter warnings unchanged. Audit truth: ADR estimated 18–22 site-scoped tables; actual count is 24. After this: every site-scoped entity has explicit bucket assignment;pages.site_idaudit-discovered drift fixed; exception registry mechanism live, ready for G12 in SITE_PAGE_VIS-005. Unblocks SITE_PAGE_VIS-003 (shipdiscussion+vaulttabs, flip 2 transitional pending rows to covered). -
SITE_PAGE_VIS-001 (
site_discussionsschema + DSC prefix + graph wiring):PROMPT-SITE_PAGE_VIS-20260611-0001, baseline83117c5. SQL migration + companion TS manifest edit (SR-002). New tablepublic.site_discussions(id, serial, workspace_id NOT NULL → workspaces ON DELETE CASCADE, site_id NOT NULL → sites ON DELETE CASCADE, parent_id self-FK ON DELETE CASCADE, kind CHECK in (discussion|announcement|answer), author_id → auth.users ON DELETE SET NULL, title, body NOT NULL, status DEFAULT 'open' CHECK in (open|resolved|archived), created_at, updated_at) withsite_discussions_title_root_onlyCHECK enforcing Locked Call 4 (root posts require title; replies must have NULL title). 5 explicit indexes (site, workspace, parent partial, kind, site+kind+created_at DESC) plus PK.tg_touch_site_discussions_updated_atBEFORE UPDATE trigger;tg_serial_site_discussionBEFORE INSERT trigger callsmbo_generate_serial('DSC', sites.code WHERE id=NEW.site_id)— site-scoped serial formatDSC-<site_code>-YYYYMMDD-NNN(Locked Call 5, pattern adapted fromtg_serial_agent_fn). RLS enabled with 3 policies:site_discussions_ws_read(SELECT viais_workspace_member),site_discussions_ws_write(ALL viacan_write_workspace),site_discussions_svc_all(ALL TO service_role) — Locked Call 2 (Q6=A). GRANT SELECT/INSERT/UPDATE/DELETE to authenticated; GRANT ALL to service_role.serial_registryrow inserted: prefix=DSC, table=site_discussions, label=Discussion, introduced_in=SITE_PAGE_VIS-001.graph_noderow inserted: node_type=site_discussion, table=site_discussions, is_terminal=false (threading self-edge), has_workspace_id=true (Locked Call 6). 3graph_manifestedges inserted: site_discussion→workspace (workspace_id, hard, in_workspace), site_discussion→site (site_id, hard, on_site), site_discussion→site_discussion (parent_id, soft, reply_to). Companionsrc/lib/worldportGraph.tsedit (SR-002 Locked Call 7): addedsite_discussion+workspacetoGraphNodeTypeunion andNODE_TABLE, added 3 matchingGRAPH_EDGESentries. InlineDO $smoke$(Locked Call 8 — SR-004 candidate honored): asserts DSC registry row present, site_discussion graph_node row present, 3 manifest edges from site_discussion; picks live workspace+site, inserts root with title (verifies serial beginsDSC-), inserts reply (parent_id), asserts both CHECK constraints fire on illegal inputs (title-on-reply, rootless-title), asserts CASCADE delete propagates to reply, and asserts(public.serial_compliance_check() ->> 'is_compliant')::boolean = trueat end. SR-001 N/A pre-apply (table didn't exist); SR-002 satisfied via companion TS edit in same commit; SR-003 satisfied — every column/function/pattern cited to canonical migration in prompt's verification table. 76 pre-existing linter warnings unchanged. After this: DSC serials work end-to-end;resolve('DSC-...')returns Council Protocol Part VI shape once rows exist; threading auditable via soft self-edge;is_compliant=trueholds (G2/G10/G11 green); TS↔DB manifest in sync. Unblocks SITE_PAGE_VIS-002 (site-scoped audit +pagesFK fixup +site_tab_pending_exceptionstable). -
COUNCIL-007 (Pack closeout — G11
council_seats_unseeded+ DISO drift fix):PROMPT-COUNCIL-20260611-0007, baselinec39ba0e. SQL migration + docs. Full canonical body ofpublic.serial_compliance_check()preserved verbatim from20260611160401_*.sqlwith only TWO semantic additions: (1) G7 SECDEF allow-list extended withrecord_resolve_evidenceandassert_resolved(the 2 COUNCIL RPCs shipped in COUNCIL-002 — both confirmedSECURITY DEFINERin20260611173634_*.sql); (2) new G11 block computescouncil_seats_unseededfrom a static VALUES list of the 5 canonical seats (kyle,aria,scanner,schema_auditor,regression_watcher), flaggingmissingif absent frompublic.agentsorworkspace_scopedif present withworkspace_id IS NOT NULL. G11 is included in the return object AND in theis_compliantAND-chain — there is no soft mode; constitutional infrastructure must be platform-global. Block 0 (executed BEFORE the function rewrite):UPDATE public.serial_registry SET introduced_in='reserved' WHERE prefix='DISO' AND table_name='dissent_ontology'— resolves pre-existing COUNCIL-001 drift (SWEEP-COUNCIL-DISO-REGISTRY-FIX-001) where DISO was registered as active butdissent_ontologyiscode-keyed with no serial column; grep-verified zerombo_generate_serial('DISO',…)callers and zerodissent_ontology.serialreaders. InlineDO $smoke$block ran in-transaction: asserted DISO now reserved, response containscouncil_seats_unseeded, G11 count = 0, ANDis_compliant=true(existence ≠ correctness — the 005b lesson re-applied).COMMENT ON FUNCTIONupdated. Permissions unchanged (REVOKE PUBLIC, GRANT EXECUTE TO authenticated, service_role). Trigger functions NOT added to G7 allow-list — structuralRETURNS triggercarve-out (AUDITOR-002) already covers them. SR-001/SR-002 N/A; SR-003 satisfied — every referenced object cited to canonical migration in the prompt's verification table. 76 pre-existing linter warnings unchanged. Docs updates: createddocs/COUNCIL_PACK_RETROSPECTIVE.md(serialRETRO-COUNCIL-20260611-0001) with full pack metrics + handoff brief to SITE_PAGE_VIS;docs/FOUNDATION_ROADMAP.mdCOUNCIL row flipped 🟡 ACTIVE → ✅ COMPLETE with closed-date 2026-06-11; appended§8 Closeouttodocs/COUNCIL_PACK_ROADMAP.md; appendedPack closeout: COUNCILsection todocs/COUNCIL_PR_REVIEW_CHECKLIST.md. FiledSR-FOUNDATION-CANDIDATE-20260611-0004(spine-function smokes must assertis_compliant=true) — hold for STATE_OF_TRUTH promotion. After this: 5 of 8 foundation packs COMPLETE (SCHEMA_AUDITOR_v2, SERIAL, TRAVERSABILITY, RESOLVER, COUNCIL). G11 now structurally enforces "the Council exists, platform-global, always." Next: SITE_PAGE_VIS. -
COUNCIL-006 (Resolver v1.2.0 — light up
recent_state_changes+open_deliberations):PROMPT-COUNCIL-20260611-0006, baseline2520168. SQL-only migration; full canonical body ofpublic.resolve(text)preserved verbatim from20260611144507_*.sqlwith only two semantic changes: (a) bumpresolver_versionliteral1.1.0 → 1.2.0in both return paths; (b) compute and populate the two previously-empty placeholder fields. New DECLARE vars:v_open_delibs,v_recent_changes,v_codex_job_id,v_council_status. Per Locked Call 1,open_deliberationspopulates ONLY whenv_prefix='CJOB'; per Call 2, only whencouncil_status NOT IN ('resolved','deadlocked','human_required','clean','flagged')(terminal + legacy); per Call 4, LIMIT 20 for open_delibs, LIMIT 10 for recent_changes.recent_state_changesis the thin v1 sourced fromcouncil_deliberations— surfaces rows for either the matched codex_job OR any deliberation whosereason_detail LIKE '%<serial>%'; each row shape{kind:'council_vote', at, agent_seat, verdict, reason_code, summary}with summary = reason_code ??agent_seat verdict. STATE_OF_TRUTH SOT-003 will swap the data source toplatform_state; response shape is forward-compatible.COMMENT ON FUNCTIONupdated toCOUNCIL-006 (v1.2.0): .... Permissions unchanged (REVOKE PUBLIC/anon, GRANT EXECUTE TO authenticated, service_role). InlineDO $smoke$block ran in-transaction: picked latestcodex_jobs.serial+ anygraph_node.serial, assertedresolver_version='1.2.0', both fields present + array-typed, and non-CJOB serials return emptyopen_deliberations. SR-001/SR-002 N/A (no schema, no manifest); SR-003 satisfied — every column reference cited to canonical migration in the prompt's verification table. 76 pre-existing linter warnings unchanged (none introduced). After this: the address bar (RESOLVER-003) andCouncilPanel.tsx(COUNCIL-005) surface live Council activity when resolving CJOB serials; foundation feedback loop RESOLVER↔COUNCIL closes. Unblocks COUNCIL-007 (pack closeout — G11council_seats_unseededadded toserial_compliance_check). -
COUNCIL-005 (Deliberation viewer UI — replace
CouncilPanel.tsx):PROMPT-COUNCIL-20260611-0005, baseline46d562b. Frontend-only full replace ofsrc/components/codex/CouncilPanel.tsxper ADR D8. Same prop shape{workspaceId, siteId, sessionIdBase?}keepssrc/routes/codex.tsxintegration intact (sessionIdBaseaccepted, unused). TwouseQuerycalls withrefetchInterval: 10_000(realtime deferred toSWEEP-COUNCIL-UI-REALTIME-001): (1)codex_jobsforsite_id=siteId, excluding legacyclean, 20 most recent; (2)council_deliberationsfiltered toworkspace_id+codex_job_id IN (...). Grouped client-side bycodex_job_id→round→ seat (canonical order: kyle, scanner, schemaauditor, regression_watcher). Per ADR D8 read-only — no vote casting, amendment push, override, or delete (filedSWEEP-COUNCIL-ADMIN-UI-001). One card per job: header with PR title/number/URL + status chip (statusTone: resolved=success, deadlocked/human_required=danger, *_amending=warning, round_=info, else neutral); per-round vote rows with verdict chip (verdictTone: approve=success, dissent=danger, flag=warning), reason_code chip, reason_detail text; serial regex/[A-Z]+(?:-[A-Z0-9]+)?-\d{8}-\d{3,}/gextracts click-through chips routing to/codex/resolve?serial=...(RESOLVER-003). Amber_\_amendingfooter says "Awaiting amendment — Brian or Kyle must push manually" since Aria deferred (ADR D7). Red footers forhuman_requiredanddeadlockedterminal states. Empty state explains how to trigger Council review. Loading + error states with Retry button (refetches both queries). All colors via design tokens (text-text-secondary,hover:bg-surface-secondary); no neutral-* literals. SR-001/SR-002/SR-003 N/A (frontend, no SQL, no manifest). After this: first user-visible Council surface —/codexshows live deliberation state per site. Unblocks COUNCIL-006 (resolve placeholder fill). -
COUNCIL-004 (Wire
codex-pr-completed→council-deliberate):PROMPT-COUNCIL-20260611-0004, baseline54700ec. Edge function only — modifiessupabase/functions/codex-pr-completed/index.ts; no SQL, no frontend, no new edge function. Two edits: (1) header comment updated to note COUNCIL-004 fire-and-forget dispatch behavior; (2) inserted COUNCIL-004 dispatch block after theagent_dispatch_loginsert and before the existing READER-009 merge-dispatch — gated onnewStatus === "pr_open"so it fires exactly once per PR open event (PRsynchronizere-fire deferred toSWEEP-COUNCIL-AMENDMENT-RETRIGGER-001). Resolves workspace viacodex_jobs.site_id → sites(workspace_id)join (Locked Call 3 —codex_jobshas noworkspace_idcolumn); orphan codex_jobs (no site or no workspace) log"orphan codex_job (no workspace)"and skip dispatch (filedSWEEP-COUNCIL-ORPHAN-JOB-001). Invocation pattern mirrors READER-009 verbatim:void fetch(SUPABASE_URL/functions/v1/council-deliberate)withAuthorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}and{codex_job_id, workspace_id}body. Thevoidprefix prevents the webhook from awaiting deliberation — webhook returns 200 to GitHub immediately per ADR D3 and GitHub's retry semantics..thenlogs structured"council-deliberate dispatched"success with dispatch_status;.catchlogs failure but never bubbles up — dispatch failure does NOT fail the webhook (Locked Call 5). Outertry/catchensures any unexpected error (workspace lookup, env var read) is logged viaconsole.errorand the webhook still acknowledges GitHub. MissingSUPABASE_URLorSUPABASE_SERVICE_ROLE_KEYlogs error and skips dispatch (Locked Call 4 — service-role auth sincecouncil-deliberateisverify_jwt = falseper COUNCIL-003 but requires service-role internally; HMAC hardening filedSWEEP-COUNCIL-WEBHOOK-HMAC-001). SR-001/SR-002 N/A (no schema/manifest); SR-003 satisfied — every column reference (codex_jobs.id,codex_jobs.site_id,sites.workspace_id,codex_jobs.council_status) cited to canonical migration in the prompt's SR-003 table. After this: end-to-end deliberation loop is wired — PR opens →codex-pr-completedmatches codex_job + setscouncil_status='pending'→ fire-and-forget dispatchescouncil-deliberate→ state machine advances → resolved/amending/human_required/deadlocked. Resolver Law is now ENFORCED on every real PR (Kyle's seat firesRESOLVE_BYPASSif serials are written without prior resolve). Unblocks COUNCIL-005 (deliberation viewer UI rebuild —CouncilPanel.tsxbecomes read-only, vote-history-driven). -
COUNCIL-003 (Deliberation orchestrator + 4 voting seat modules, v1):
PROMPT-COUNCIL-20260611-0003, baseline4182ca5. New edge functionsupabase/functions/council-deliberate/(state-machine-driven per ADR D6 — one invocation advances ONE transition; nosetTimeout, no polling, no blocking primitives). Single-file orchestrator: (1)fetchPrDiff(pr_url, pr_number)calls GitHub/repos/{o}/{r}/pulls/{n}/fileswithGITHUB_TOKEN, regex-extracts touched serials via/[A-Z]+(?:-[A-Z0-9]+)?-\d{8}-\d{3,}/g(soft-degrades to empty diff whenGITHUB_TOKENis absent — seats then APPROVE-by-default); (2) immutableEnvelope(Object.freeze+Readonly<…>) carries{workspace_id, codex_job_id, round, snapshot_as_of, source:'council-deliberate', pr_number, pr_url, pr_title}per SITE*SUPERVISOR_INSIGHT — seat modules read only, no shared mutable state; (3) 4 seat modules run inPromise.all: Kyle callsassert_resolved(p_codex_job_id, p_round, p_serials_touched)and emitsRESOLVE_BYPASS(hard veto) ifall_resolved=false, APPROVE if no serials touched,HUMAN_REQUIREDif RPC errors; Schema Auditor short-circuits APPROVE unless diff touchessupabase/migrations/, otherwise callsserial_compliance_check()and emitsSERIAL_VIOLATION(hard veto) if non-compliant; Scanner scans migration patches forCREATE TABLE [IF NOT EXISTS] public.<Name>and emitsNAMING_VIOLATION(interpretive) if any name is not lowercase; Regression Watcher readspagesrows in the workspace withknown_issues IS NOT NULLand emitsREGRESSION_RISK(interpretive) when a changed filename contains the page's last path segment (path-heuristic limitation documented inreason_detail; full-fidelity blocked onpages.source_file_path— filedSWEEP-PAGES-SOURCE-FILE-PATH-001); (4)runRoundcallspre_council_resolve_batch(p_serials)forsnapshot_as_of, thenrecord_resolve_evidence(p_workspace_id, p_codex_job_id, p_round, p_resolved_serials, p_snapshot_as_of)(skipped when zero serials touched), then all 4 seats in parallel, thenpersistVotes— inserts 4council_deliberationsrows, mapsagent_seat → deliberation_serialvia the RETURNING clause, then inserts 4council_votesrows referencing those serials; (5)determineNextStatus(round, verdicts)— anyHARD_VETO_CODESmember fires (SERIAL_VIOLATION, RLS_GAP, ARCHITECTURE_CONFLICT, MISSING_PAGE_RECORD, HUMAN_REQUIRED, TRAVERSABILITY_VIOLATION, MISSING_SITE_TAB, RESOLVE_BYPASS) →human_required; all APPROVE →resolved; round<3 + any DISSENT →round*${round}\_amending; round=3 + any DISSENT →deadlocked; single UPDATE tocodex_jobs.council_status; (6) handler dispatches bycouncil_status: terminal states (resolved/deadlocked/human_required) return immediately;\*\_amendingreturnsawaiting_amendment(Aria deferred per ADR D7);pendingadvances toround_1then runs round 1;round_1/round_2/round_3runs that round.supabase/functions/council-deliberate/deno.jsonmirrorsresolve/deno.json.supabase/config.tomlregisters[functions.council-deliberate] verify_jwt = false(service-role invoked by COUNCIL-004 webhook wiring; HMAC hardening filedSWEEP-COUNCIL-WEBHOOK-HMAC-001). No SQL migration. No frontend. SR-003 satisfied — every RPC signature + column reference cited to canonical migration in the prompt's SR-003 table. v1 ships 1 dissent code per seat (4 of 14); remaining 10 filedSWEEP-COUNCIL-SEAT-EXPANSION-001; LLM interpretive layer filedSWEEP-COUNCIL-LLM-INTERPRETIVE-001. Unblocks COUNCIL-004 (codex-pr-completed → council-deliberatewebhook chain). -
COUNCIL-002 (Resolver Law enforcement primitive): Ships the structural mechanism that lets Kyle's seat fire
RESOLVE_BYPASS(PROMPT-COUNCIL-20260611-0002, baselinea18df3d). Single SQL migration, no edge function, no frontend. Six blocks in one transaction: (1)council_resolve_evidencetable —serial(UNIQUE, RESOEV-GLOBAL-…),workspace_id NOT NULL REFERENCES workspaces ON DELETE CASCADE,codex_job_id NOT NULL REFERENCES codex_jobs ON DELETE CASCADE,round integer NOT NULL CHECK (round BETWEEN 1 AND 3),subject_serial text NOT NULL,snapshot_as_of timestamptz NOT NULL,recorded_atdefaultnow(), optionalresolver_version+resolve_foundcaptured per row frompublic.resolve();council_resolve_evidence_unique UNIQUE (codex_job_id, round, subject_serial)enforces one row per (job, round, serial) —INSERT ... ON CONFLICT DO NOTHINGmakesrecord_resolve_evidenceidempotent against webhook retries. Three indexes (codex_job+round, subject_serial, workspace+recorded). RLS enabled;council_resoev_readSELECT viais_workspace_member(workspace_id);council_resoev_svcFOR ALL TO service_role. GRANTs SELECT→authenticated, ALL→service_role per public-schema law. (2)serial_registryrow forRESOEVprefix (introduced_in=COUNCIL-002,graph_participant=true, row_type_labelResolver Evidence) withON CONFLICT (prefix) DO UPDATE; BEFORE INSERT triggertg_serial_council_resolve_evidencecallspublic.mbo_generate_serial('RESOEV','GLOBAL')whenNEW.serial IS NULL. (3)graph_noderow (council_resolve_evidence,is_terminal=true,has_workspace_id=true) + onegraph_manifestedgecouncil_resolve_evidence → codex_jobviacodex_job_id(hard, forward, labelevidence_for);subject_serialis intentionally NOT a manifest edge — it's a TEXT pointer to any serial-bearing row, resolved at read time viapublic.resolve(subject_serial). (4)public.record_resolve_evidence(p_workspace_id uuid, p_codex_job_id uuid, p_round integer, p_resolved_serials text[], p_snapshot_as_of timestamptz) → jsonb— VOLATILE, SECURITY DEFINER,search_path=public. Workspace_id passed explicitly per Locked Call 4 (codex_jobs has no workspace_id column — explicit beats heuristic). Validates all five inputs; for each serial in the array callspublic.resolve(v_serial)to captureresolver_version+found, then inserts withON CONFLICT DO NOTHING; per-iteration BEGIN/EXCEPTION block isolates failures (005b lesson — single bad serial doesn't abort the batch, surfaces via NOTICE). Returns{recorded, skipped_duplicates, total, codex_job_id, round, snapshot_as_of}. REVOKE FROM PUBLIC,anon; GRANT EXECUTE TO authenticated, service_role. (5)public.assert_resolved(p_codex_job_id uuid, p_round integer, p_serials_touched text[]) → jsonb— STABLE, SECURITY DEFINER,search_path=public(Locked Call 3 — read-only, planner can cache within statement). Readscouncil_resolve_evidencefor the (codex_job, round) tuple intov_resolved_serials, partitionsp_serials_touchedintoresolved(in evidence) andbypassed(not in evidence) via FOREACH+ANY, computesv_all_resolved := (array_length(v_bypassed,1) IS NULL). Returns{all_resolved, bypassed, resolved, evidence_count, touched_count, snapshot_as_of, codex_job_id, round}(Locked Call 6 — shape locked so Kyle's seat in COUNCIL-003 can serialize directly intoreason_detail). REVOKE FROM PUBLIC,anon; GRANT EXECUTE TO authenticated, service_role. (6) InlineDO $smoke$block ran in-transaction: created syntheticcodex_jobwithvertical_slug='council_002_smoke'+council_status='pending', pulled one real serial fromgraph_node, then asserted (a)record_resolve_evidencewith 1 serial →recorded=1, (b) duplicate call →skipped_duplicates=1(idempotency), (c)assert_resolvedwith the recorded serial →all_resolved=true, (d)assert_resolvedwith the recorded serial + a fakeFAKE-DIFF-20260101-001→all_resolved=falseandbypassedlength=1; each branchRAISE EXCEPTIONon failure so a regression would rollback the whole migration. Synthetic codex_job cleaned up at end; evidence rows cascade-delete via FK. Per ADR D6: this is review-layer enforcement only (not write-layer); per ADR Locked Call 7: orchestrator (COUNCIL-003) atpending → round_1callspre_council_resolve_batch(touched_serials), capturesas_of, thenrecord_resolve_evidence(workspace_id, codex_job_id, 1, touched_serials, as_of), then dispatches seats — Kyle callsassert_resolvedand firesRESOLVE_BYPASSifall_resolved=false. SR-001/SR-002/SR-003 active per prompt header: every schema reference cited to canonical migration; new table + manifest edge land in the same migration as the FK; new graph_node + RESOEV registry row land in the same migration so RESOLVER-006 G10 (resolver_unreachable_tables) stays green. No RESOLVER primitive (resolve,pre_council_resolve_batch,graph_serials_by_ids) modified. No frontend/edge changes. 76 pre-existing linter warnings unchanged (none introduced — both new functions setsearch_path=public; service_roleUSING (true)mirrors existing COUNCIL-001*_svcpolicies). Rollback per prompt §"Rollback Plan" — drop both RPCs, drop trigger + trigger fn, delete manifest edge + graph_node row, delete RESOEV registry row, DROP TABLE CASCADE. After this: Resolver Law has executable structural primitive; COUNCIL-003 (orchestrator + 4 seat modules) is unblocked — Kyle's seat module is a thin caller ofassert_resolved. -
COUNCIL-001 (schema spine, state machine, agent seeding): Ships the executable schema for the Council deliberation engine (
PROMPT-COUNCIL-20260611-0001, baseline3a9fa77). Split into two migrations becauseALTER TYPE ... ADD VALUEvalues can't be used in the same transaction that adds them: (1) extendscodex_council_statusenum with 9 state-machine values (round_1,round_1_amending,round_2,round_2_amending,round_3,round_3_amending,resolved,deadlocked,human_required) preserving legacypending/clean/flaggedper Locked Call 1; (2) the 12-block spine —dissent_ontology(text PK, 14 codes seeded verbatim from Council Protocol Part V withenforcer_seat+kyle_vetoflags),council_deliberations(per-vote audit log, FK tocodex_jobsON DELETE SET NULL, CHECK constraint enforcingdissent ⇒ reason_code IS NOT NULL),council_votes(structured decision record withsuperseded_byself-FK chain and a partial unique index on(codex_job_id, round, agent_seat) WHERE superseded_by IS NULLenforcing one active vote per seat per round),council_amendments(table ships per Locked Call 6 even though Aria auto-amend is deferred per D7 — v1 records manual amendments). All 4 tables:workspace_id NOT NULL REFERENCES workspaces ON DELETE CASCADE, RLS enabled,is_workspace_memberread policy + service_role full policy, GRANTs to authenticated+service_role per public-schema law.dissent_ontologyis platform-global (no workspace_id) withTO authenticated USING (true)read. Block 6 relaxesagents.workspace_idto nullable and rewritesws_read(workspace_id IS NULL OR is_workspace_member(...)) andws_write(workspace_id IS NOT NULL AND can_write_workspace(...)) so platform-global seats are readable by all authenticated users but only writable by service_role per ADR D4 + Locked Call 4. Block 6.5 (added at runtime — not in original prompt): the first attempt failed inline smoke (c) withfound 3because two pre-existing workspace-scoped agents squatted on the canonical slugs (kyle= kyle_base44/build_engineer,aria= aria_perplexity/research_lead in workspace24b0d85a…) andON CONFLICT (slug) DO NOTHINGsilently skipped them; resolved by renaming the legacy rows tokyle_legacy_ws/aria_legacy_ws(filedSWEEP-COUNCIL-LEGACY-SLUG-DEPRECATE-001to evaluate full removal once any callers migrate). Block 7 idempotently addsagents_slug_key UNIQUE (slug)constraint viaDOblock and seeds the 5 canonical platform-global Council seats (kyle/aria/scanner/schema_auditor/regression_watcher) withworkspace_id=NULL,provider='council'. Block 8 flips DELIB/VOTE/AMENDserial_registryrows fromreserved → COUNCIL-001withgraph_participant=trueand inserts DISO asgraph_participant=false(text-PK table, not graph-resolved). Block 9 adds 4graph_noderows (council_deliberationnon-terminal,council_vote+council_amendmentterminal, allhas_workspace_id=true;dissent_ontology_codeterminalhas_workspace_id=false). Block 10 declares 7graph_manifestedges (deliberation→codex_job soft, vote→deliberation hard, vote→codex_job hard, amendment→deliberation hard, amendment→codex_job hard, deliberation→dissent_code soft, vote→dissent_code soft). Block 11 installs 3 BEFORE INSERT serial triggers (tg_serial_council_deliberation/vote/amendment) callingpublic.mbo_generate_serial(prefix, 'GLOBAL')forDELIB-GLOBAL-…/VOTE-GLOBAL-…/AMEND-GLOBAL-…format. Block 12 inline smoke ran in-transaction and asserted (a) state machine acceptspending → round_1 → round_1_amending → round_2 → resolved, (b) enum rejects unknown valuetotally_not_a_real_status(per Brian's ratification — the test that proves enforceability; transition-jump rejection is the orchestrator's job in COUNCIL-003), (c) exactly 5 platform-global seats present — all three passed. POST verification: enum has all 12 values; 4 council tables RLS-enabled; 14 dissent codes seeded; serial_registry shows 4 rows (DELIB/VOTE/AMEND/DISO all introduced_in=COUNCIL-001, graph_participant=true except DISO); 4 graph_nodes + 7 graph_manifest edges; agents.workspace_id is_nullable='YES'; 5 platform-global seats present. SR-003 satisfied — every reference cited in prompt header to canonical migration. SR-001/SR-002 sweeps deferred to CI (TSworldportGraph.tsNODE_TABLE update for the 4 new node types filed asSWEEP-COUNCIL-TS-MANIFEST-001if drift detected). 74 pre-existing linter warnings unchanged (none introduced by this migration). Rollback per prompt §"Rollback Plan" — drop triggers/functions, delete manifest/graph_node rows, restore serial_registry, delete seeded seats, restore agents NOT NULL + original policies, drop 4 tables CASCADE; enum values cannot be removed without recreating the type. After this: schema spine LIVE; state machine has executable values; 5 Council seats constitutionally present; 14 dissent codes queryable; COUNCIL-002 (Resolver Law primitive) is unblocked. -
COUNCIL-000 (pack opens): Constitutional commit for the COUNCIL pack — documentation-only, no SQL, no edge functions, no frontend (
PROMPT-COUNCIL-20260611-0000, baselinea7435b8, ratified 2026-06-11 11:36 CDT). Four artifacts shipped: (1)docs/COUNCIL_ADR.mdnew (ADR-COUNCIL-20260611-0001) locking D1–D9 corresponding to the nine ratified design questions — D1 onecouncil-deliberateedge function with 4 internal seat modules, D2 rule-based v1 seats (no LLM in deliberation path; 8 hard veto codes + 6 interpretive), D3 webhook-driven via existingcodex-pr-completed, D4 migration-seeded platform-global agents (workspace_id IS NULL, requires relaxing current NOT NULL + RLS update + 5 seed rows), D5 pack order SQL→law→orchestrator→wiring→UI→placeholder fill→closeout (8 prompts 000–007), D6RESOLVE_BYPASSvia diff-inspection in Kyle's seat (review-layer enforcement, not write-layer), D7 Aria deferred toSWEEP-ARIA-AMENDMENT-001, D8 read-only deliberation viewer fully replacingCouncilPanel.tsx(no sibling tab), D9 G11 (council_seats_unseeded) ships asis_compliant-contributing in COUNCIL-007; six open items closed with explicit answers (synthetic state-machine smoke + invalid-jump rejection assertion,github_mcp_directwithGITHUB_DISPATCH_TOKENfallback verified pre-COUNCIL-003, full panel replace, state-machine-driven execution model non-negotiable,agents.workspace_idrelax sequence, Site Supervisor forward-context insights folded into COUNCIL-003/006 headers); three additional considerations ratified (COUNCIL-006 single prompt not split, no per-workspace flag in v1 →SWEEP-COUNCIL-WORKSPACE-FLAG-001, rule-based v1 sufficient — revisit after 20-30 deliberations →SWEEP-COUNCIL-LLM-INTERPRETIVE-001); seven deferred items filed (Aria amendment, LLM interpretive seats, Brian admin UISWEEP-COUNCIL-ADMIN-UI-001, per-seat edge isolation, Postgres-triggerRESOLVE_BYPASS, workspace flag,registerCodeReaderFleetremovalSWEEP-COUNCIL-DEPRECATE-FLEET-REGISTRAR-001). (2)docs/COUNCIL_PACK_ROADMAP.mdnew (ROADMAP-COUNCIL-20260611-0001) — status ACTIVE, §2 pack composition table for all 8 prompts, §6 ratification form filled with ✅ Option A for Q1–Q9, closing line records 2026-06-11 11:36 CDT ratification. (3)docs/FOUNDATION_ROADMAP.md§2 pack table — COUNCIL row flippedNOT_STARTED → 🟡 ACTIVE — ADR locked 2026-06-11, prompt estimate~10–12 → 8 prompts (000–007), date started2026-06-11, narrative replaced with ADR/roadmap pointers + headline decisions. (4)docs/COUNCIL_PR_REVIEW_CHECKLIST.md— pack opening note appended below the existing two-gate map / RESOLVER closeout note, confirms PRE-001..006 cover COUNCIL prompts (no new gates added at pack open), G11 ships in COUNCIL-007, plus two reviewer heads-ups: COUNCIL-001agents.workspace_idNULL relaxation + RLS update riskiest part, COUNCIL-003 state-machine-driven execution model (persist + return immediately, 30-min round timeout is wall-clock at function entry on subsequent invocations — reject blocking patterns). Pre-flight verification: SR-001/SR-002/SR-003/PRE-002/PRE-004 N/A (no triggers, no manifest, no SQL, no schema); PRE-003 baselinea7435b8. POST gates: POST-001..POST-005 documentation existence/serial/status/checklist content verified by file writes; POST-006 this CHANGELOG entry; POST-007 schema auditor unchanged (no SQL in this prompt — pre-existingis_compliantbaseline preserved). After this: COUNCIL pack constitutional surface LOCKED; any deviation from D1–D9 in COUNCIL-001..007 requires explicit ADR amendment; COUNCIL-001 drafts next. -
RESOLVER-006 (pack closeout): Extends
public.serial_compliance_check()with G10 (resolver_unreachable_tables) asis_compliant-contributing and extends the G7 SECDEF allow-list with the three new RESOLVER functions (resolve,graph_serials_by_ids,pre_council_resolve_batch) — pack closes (PROMPT-RESOLVER-20260611-0006, baseline248bc97). SQL-onlyCREATE OR REPLACE FUNCTION; canonical body from20260610220415_*.sqlpreserved verbatim with two minimal additions: (a) 3 names appended to the G7 allow-listIN (...)literal so the new SECDEF functions stop registering assecdef_authenticated_leaksfalse positives, and (b) a new G10 block —v_resolver_unreachable jsonbvariable declared, aLEFT JOIN public.serial_registry sr → public.graph_node gn ON gn.table_name = sr.table_name WHERE sr.introduced_in <> 'reserved' AND sr.graph_participant = true AND (gn.table_name IS NULL OR gn.node_type IS NULL)selector building{prefix, table_name, row_type_label, reason}findings (reason ∈no_graph_node_row|graph_node_table_mismatch|unknown), the result wired intojsonb_build_objectasresolver_unreachable_tables, and a new clauseAND jsonb_array_length(v_resolver_unreachable) = 0appended to theis_compliantpredicate. Scope deliberately narrow per Locked Call 1 — onlygraph_participant=truerows are gated; RESOLVER-006a's flag explicitly excludes platform infrastructure (Bucket A), tenancy primitives (Bucket B), and deferred-sweep candidates (Buckets C-deferred, D), and the column default istrueso future serial-bearing tables cannot bypass G10 without an explicit, documented disposition. G7 allow-list grew by 3, dropped 0 (Locked Call 2): all three new RESOLVER SECDEF functions legitimately needauthenticated EXECUTE—resolve(text)called by the Codex address bar with user JWT,graph_serials_by_ids(text, uuid[])called by the resolve edge function with user JWT for hydration + redaction,pre_council_resolve_batch(text[])primary caller is service-role (COUNCIL deliberation) but authenticated callers may invoke (defensive). G10 finding shape per Locked Call 4 — sample-serial validation is G1's responsibility, not G10's (single responsibility). Permissions reaffirmed:REVOKE ALL FROM PUBLIC;GRANT EXECUTE TO authenticated, service_role.COMMENT ON FUNCTIONupdated to document the RESOLVER-006 contract. Inline DO-block smoke ran in-transaction and asserted (a) the response containsresolver_unreachable_tables, (b)is_compliant=true—RAISE EXCEPTIONon failure so the migration would rollback rather than ship a regression; migration committed cleanly so both held. SR-003 satisfied — every reference in the new body cited to its canonical migration in the prompt header verification table (preserved body from20260610220415_*.sqllines 1–197;resolvesignature from20260611144507_*.sqllines 6–7;graph_serials_by_idsfrom20260611131452_*.sqllines 1–6;pre_council_resolve_batchfrom20260611145759_*.sqllines 1–6;serial_registry.graph_participantfrom RESOLVER-006a;graph_node.table_name/node_typefrom20260610213926_*.sqllines 4–14). SR-001/SR-002 N/A (no domain triggers, no manifest changes). PRE-004 idempotency:CREATE OR REPLACE FUNCTION. Rollback: re-apply prior canonical body from20260610220415_*.sql(without G10 and without the 3 allow-list entries); documentation rollback viagit revert. Documentation closeout shipped same commit:docs/RESOLVER_PACK_RETROSPECTIVE.md(new, serialRETRO-RESOLVER-20260611-0001) — 8-prompt summary, 6 lessons learned, deferred-items ledger, pack-closing metrics, full COUNCIL hand-off brief;docs/RESOLVER_PACK_ROADMAP.md§6 closing line replaced to mark pack closed;docs/FOUNDATION_ROADMAP.md§2 pack table — RESOLVER row updatedEXECUTING → ✅ COMPLETE, ended 2026-06-11, prompt count 7 → 8, narrative replaced; §5 SWEEP-RESOLVER-ADR-D4-MISMATCH status flipped🟢 FILED → ✅ RESOLVED — closed by RESOLVER-004 shipping Option B; row-level soft-delete remains deferred to TRAVERSABILITY-SWEEP-BITEMPORAL as originally specified;docs/COUNCIL_PR_REVIEW_CHECKLIST.mdcarries the appended "Pack closeout: RESOLVER (closed 2026-06-11)" note referencing the retrospective serial. After this: RESOLVER pack closed; G10 ACTIVE inserial_compliance_check;is_compliant=truepreserved through close; the Resolver Law is now executable AND structurally enforced; COUNCIL pack (pack 5) is unblocked. -
RESOLVER-006a: Ships
serial_registry.graph_participanttriage + Bucket Cgraph_nodebackfill — pre-closeout prerequisite for RESOLVER-006 G10 (PROMPT-RESOLVER-20260611-0006a, baseline1bd3700). SQL-only single migration with three semantic blocks in one transaction (Locked Call 6): (1)ALTER TABLE public.serial_registry ADD COLUMN IF NOT EXISTS graph_participant boolean NOT NULL DEFAULT true, ADD COLUMN IF NOT EXISTS graph_participant_reason text— defaulttrueinversion (Locked Call 1) so future serial-bearing tables cannot accidentally bypass G10;COMMENT ON COLUMNdocuments the fixed reason vocabulary at the schema layer (Locked Call 7). (2) FiveUPDATEstatements keyed byprefix(Locked Call 5) dispose all 26 RESOLVER-006-pre-flight violations across four buckets per Brian's 2026-06-11 10:44 CDT triage — Bucket Aplatform_infra(7: TRVN,TRVS,GEST,GSCAN,MVRL,ALOG,FRSH), Bucket Btenancy_primitive_adr_d12(5: WS,WMEM,ACC,INV,APIK — TRAVERSABILITY ADR D12), Bucket C deferreddeferred_sweep(5: BCAT,BPT,SNIP,ESCR,TDEP → SWEEP-RESOLVER-C-DEFERRED-001), Bucket Ddeferred_sweep_payos_graph(5: COMP,RSNAP,VSNAP,VLVR,SPARM → SWEEP-PAYOS-GRAPH-001), plus an explicit no-op preservingtruefor the 4 Bucket C real participants (PG,KSESS,IREG,ATL) for auditability. (3)INSERT INTO public.graph_nodebackfills 4 rows for the promoted participants —content_node→pages,agent_session→kyles_sessions,integration_node→integrations_registry,signal_node→atlas_signals— allis_terminal=true(Locked Call 3 — edges deferred to SWEEP-RESOLVER-PARTICIPANT-EDGES-001, no manifest/trigger work in this prompt) withhas_workspace_id=truefor the three workspace-scoped tables andfalseforintegrations_registryonly (Locked Call 4 — verified platform-global intypes.ts);ON CONFLICT (node_type) DO NOTHINGfor idempotency. Inline DO-block smoke ran in-transaction and emittedNOTICE total_active=78 participants=56 excluded=22 remaining_g10_violations=0(the equation RESOLVER-006 G10 will gate on); vocabulary integrity check usedRAISE WARNING(not EXCEPTION) so unrecognized reasons would surface in logs without blocking — zero warnings emitted. POST-flight verified: both columns present with correct types/defaults, disposition counts exact (NULL=56 / deferred*sweep=5 / deferred_sweep_payos_graph=5 / platform_infra=7 / tenancy_primitive_adr_d12=5), all 4 backfill rows landed with correcthas_workspace_id,remaining_violations=0, zero rows with bad-vocabulary reasons. Pre-existingis_compliant=falsebaseline (carryingsecdef_authenticated_leaksforgraph_serials_by_ids/pre_council_resolve_batch/resolvefrom RESOLVER-002/005) was NOT touched by this migration — orthogonal to G10 and tracked separately. SR-001/SR-002 N/A (no domain triggers, no manifest changes). SR-003 satisfied — no dynamic SQL; every referenced object cited to its canonical migration in the prompt header (serial_registrycolumns from20260609183047*_.sql,graph*nodecolumns from20260610213926*_.sql). PRE-004 idempotency:ADD COLUMN IF NOT EXISTS×2,ON CONFLICT DO NOTHINGon backfill, prefix-keyed UPDATEs are repeatable. Rollback:DELETE FROM public.graph_node WHERE node_type IN ('content_node','agent_session','integration_node','signal_node');thenALTER TABLE public.serial_registry DROP COLUMN IF EXISTS graph_participant, DROP COLUMN IF EXISTS graph_participant_reason;. After this: RESOLVER-006 G10 can re-apply readingWHERE graph_participant = true; the 26-row hit drops to 0; pack closes. -
RESOLVER-005: Ships
public.pre_council_resolve_batch(p_serials text[]) → jsonb— the COUNCIL hand-off RPC (PROMPT-RESOLVER-20260611-0005, baselinee93634f). SQL-only migration; no edge function, no frontend changes (COUNCIL deliberation calls server-side as service*role per Locked Call 1; HTTP wrapper deferred until an external caller emerges).LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path=public. Returns an envelope{as_of, resolver_version, input_count, resolved_count, not_found_count, items: {serial: ResolvedSerial}}— counts at top level so callers reason about partial failures in O(1); singleas_of := now()anchors all per-serial resolves to one snapshot moment for replay/audit.itemsis a JSONB object keyed by serial (O(1) caller lookup; last-occurrence-wins on duplicate keys per Locked Call 2 — caller dedupes upstream). Each item is a fullResolvedSerial(Locked Call 3 — not a summary; COUNCIL gets dangling edges, redacted counts, everythingresolve()returns, no second round-trip). Hard cap 100 serials per call (Locked Call 4); over-cap returns{error:'batch_size_exceeded', hard_cap:100, items:{}}. NULL and empty input short-circuit to a clean empty envelope. Per-serial failure isolation (Locked Call 5): eachpublic.resolve(v_serial)is wrappedBEGIN ... EXCEPTION WHEN OTHERSso a bad serial yields a synthetic{serial, found:false, reason:'resolve_call_failed', error: SQLERRM, resolver_version}entry instead of aborting the batch (distinct from the 005b failure mode — no dynamic SQL here, only a static composed function call, so any exception is a true runtime issue worth preserving in SQLERRM).resolver_versionenvelope field set from the first successfulresolve()response (currently'1.1.0'per RESOLVER-004). SR-003 satisfied trivially — no dynamic SQL, noformat(), noEXECUTE; only external reference ispublic.resolve(text)whose signature is cited in the prompt header to migration20260611144507*\*.sqllines 6–7. Permissions:REVOKE EXECUTE FROM PUBLIC, anon;GRANT EXECUTE TO authenticated, service_role. Inline DO-block smoke ran in-transaction: gathered up to 3 real serials from non-reservedserial_registrytables plus one known-badZZZZ-FAKE-20260101-001, called the batch, assertedinput_countmatched, envelope had{as_of, items, resolved_count, not_found_count},itemswasjsonb_typeof='object', and the known-bad serial surfaced withfound IS NOT TRUE— migration committed cleanly so smoke passed. Performance gate per roadmap: < 500ms p99 for batch of 20 (function is O(N) overpublic.resolve()at ~12.7msgraph_walkbaseline → ~250ms expected for N=20; if production breaches the gate, file SWEEP for parallel CTE walks or a combined query). Rollback:DROP FUNCTION IF EXISTS public.pre_council_resolve_batch(text[]);. RESOLVER pack now has its COUNCIL hand-off surface — one snapshot, one call, deterministic map keyed by serial. -
RESOLVER-004: Surfaces dangling edges in
resolve()and the Codex address bar (PROMPT-RESOLVER-20260611-0004, baselined5e10c4). SQL:CREATE OR REPLACE FUNCTION public.resolve(p_serial text) → jsonbbumped toresolver_version='1.1.0'— confirmedoutbound_edges/inbound_edgesnow explicitly filterstatus <> 'dangling', and two new sibling arraysdangling_outbound/dangling_inboundcarry first-class scanner-flagged broken edges (one SELECT each overpublic.graph_edge_statusfiltered bystatus='dangling'and end-sidesplit_part(edge,'→',N)=v_node_type). Each dangling row carries{id, edge, to_id|from_id, to_node_type|from_node_type, status:'dangling', suggestion_reason, last_seen_at}mapped to live columns (ges.id,ges.suggested_target_id,ges.source_id,ges.suggestion_reason,ges.updated_at) per SR-003 — prompt-draft namesedge_serial/last_scanned_at/scanner_notewere rejected as non-existent and replaced with their canonical equivalents. Dangling rows do NOT hydrate serials (the broken side is by definition unresolvable), so the RESOLVER-002 edge function'shydrateAndRedact()walksoutbound_edges/inbound_edgesonly and the dangling arrays pass through untouched (no edge-function changes shipped). Comment rewritten to start withRESOLVER-004 (v1.1.0):. Grants restated (REVOKE … FROM PUBLIC, anon;GRANT EXECUTE TO authenticated, service_role). Inline DO-block smoke runs in-transaction: picks a sample serial, callsresolve(), assertsresolver_version='1.1.0'and that both dangling fields exist as JSON arrays — migration committed cleanly so smoke passed. Frontend: (1)src/lib/resolve.functions.tsextended withDanglingEdgetype and two new array fields onResolvedSerialResponse, plus a{ forceFresh?: boolean }opt onresolveSerial()that appends?_t=<ts>cache-buster (ADR D4 mitigation — heal re-resolves bypass the 60s edge-fn memory cache); shortCircuit() now emits emptydangling_outbound/dangling_inboundfor shape consistency. (2)src/components/codex/ResolvedPanel.tsxaddsDanglingEdgesCardrendered between the redaction banner and the confirmed-edges grid — warning-toned card titledBroken Edges (N), struck-through uuids, direction icons, aRun Healbutton that callssupabase.functions.invoke("graph-heal-scan", { body: { workspace_id } })usinguseWorkspace()(same shape asdaily-letter-generate), spinner during heal, sonner toasts on success/error, calls back intoonHealComplete. Card auto-hides when both arrays are empty. (3)src/routes/codex.resolve.tsxadds arefreshKeystate bumped byonHealComplete; theuseEffectkeys on[serial, refreshKey]and passesforceFresh: refreshKey > 0so post-heal re-resolves get a fresh response. No new edge function —graph-heal-scanreused as-is per Locked Call 5; per-row heal targeting filed as future SWEEP. SR-003 verification table in the prompt header cites every column to its canonical migration (20260531134542_*.sqllines 4–14 +20260610190214_*.sqlline 39 for the extendedstatusCHECK). Rollback: re-apply migration20260611125637_*.sqlto restore v1.0.0; revert the three frontend files. Dark matter visible: the operator now sees scanner-flagged broken edges in the same address-bar response that shows confirmed ones, with one-click heal. -
RESOLVER-003: Ships the Codex address bar — first user-visible TRAVERSABILITY surface (
PROMPT-RESOLVER-20260611-0003, baseline13fb2f4). Frontend only, no SQL, no edge function changes. Four files added: (1)src/lib/resolve.functions.ts— helperresolveSerial(serial)that mirrors the ADR D5 lockedResolvedSerialResponseshape verbatim (no transformation layer; shape drift surfaces loudly per Council Protocol Part VI), short-circuits malformed serials client-side with regexSERIAL_REkept in lockstep with RESOLVER-002's edge regex, callsGET ${VITE_SUPABASE_URL}/functions/v1/resolve/v1/:serialwith the user'ssupabase.auth.getSession()access_token inAuthorization: Bearer, maps 401 →unauthorizedthrow, 400 → malformed short-circuit, !ok →resolve_failed: <error>throw; (2)src/components/codex/AddressBar.tsx— single pinned input, upper-cases on submit, soft warning when shape fails client regex but still submits (server confirms), Enter-to-submit; (3)src/components/codex/ResolvedPanel.tsx— renders identity block (row_type_label + serial + table_name + id), found/reason chip, workspace-scoped chip, amberredacted_edge_countbanner per ADR D3, outbound/inbound edge cards with clickableEdgeRowthat callsonNavigate(targetSerial), dashed-border placeholder card naming SOT-003 / COUNCIL-003 as the dependency packs per ADR D5/D6, footer withresolved_at+resolver_version; (4)src/routes/codex.resolve.tsx— TanStack file-route mounted at/codex/resolve(flat dot-separated convention matches the rest ofsrc/routes/), URL-driven via Zod-validated?serial=search param,useEffectre-resolves on serial change with cancel guard,navigate({ search: { serial } })so browser back works and links are shareable,head()sets per-route title. ADR honoring: D1 single endpoint hit, D3 redaction banner shown honestly, D5 found/reason chip differentiatesmalformed_serial/unknown_prefix/row_not_found, D6 placeholder fields shown as known-incomplete. Explicitly NOT shipped: autocomplete, recents/breadcrumbs, dangling-status UI (RESOLVER-004), mobile polish beyond the responsive grid. Rollback:rmthe four files; routeTree.gen.ts regenerates clean. -
RESOLVER-002: Ships HTTP wrapper
/functions/v1/resolve/v1/:serialplus helper RPCpublic.graph_serials_by_ids(p_node_type text, p_ids uuid[]) → jsonb(PROMPT-RESOLVER-20260611-0002, baseline41643c6). Helper RPC isLANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path=public; resolves(node_type → table_name, has_workspace_id)frompublic.graph_nodethen batch-selects{id: {serial, workspace_id|null}}viaformat('… public.%I …', v_table_name)withEXECUTE … USING $1; degradation-pathEXCEPTION WHEN OTHERS / RAISE WARNING(not NOTICE) returns'{}'::jsonbso the edge function still produces a valid resolved response withto_serial=nullfor that neighbor (distinct from the 005b ghost-column failure mode — runtime/transient only, not structural). Permissions:REVOKE EXECUTE FROM PUBLIC, anon;GRANT EXECUTE TO authenticated, service_role. SR-003 verification table in the prompt header cites every schema reference (graph_node.node_type/table_name/has_workspace_idfrom migration20260610213926_*.sqllines 4–14;public.resolve(text)from migration20260611125637_*.sqllines 6–7; per-tableid uuid/serial textcolumns rely on Schema Auditor G1 invariant, currentlyis_compliant=true). Edge functionsupabase/functions/resolve/index.tsis the thin HTTP wrapper per ADR D2: parses serial from/v1/:serial(regex^[A-Z]+(-[A-Z0-9]+)?-\d{8}-\d{3,}$), three-tier auth per ADR D3 (anon → 401; service-role key escape hatch → unredacted; authenticated →auth.getUser(token)+workspace_memberslookup), callspublic.resolve()via caller-JWT-bound client so RLS audits log the real principal (service-role uses service-role key), then runs single-passhydrateAndRedact()— collects(node_type, id)pairs from outbound+inbound edges, onegraph_serials_by_idsRPC per distinct node_type, populatesto_serial/from_serialand strips cross-workspace edges while incrementingredacted_edge_count(ADR D3 — no{redacted:true}placeholder, count is the only signal; service-role skips redaction so count stays 0). 60s per-instance memory cache per ADR D4 keyed by${serial}::${cache_suffix}where suffix is sorted workspace-id join (service-role uses literal"service_role") so workspace-scoped and service-role responses never collide; emitsX-Resolver-Cache: HIT|MISSandCache-Control: (public|private), max-age=60. HTTP semantics per ADR D5: malformed → 400, missing auth → 401, OPTIONS → 204, non-GET → 405, RPC error → 500, all other outcomes (includingfound:falsewithreason: unknown_prefix|row_not_found) → 200. CORS allows*+GET, OPTIONS.supabase/functions/resolve/deno.jsonmirrorsgraph-readiness-report.supabase/config.tomlregisters[functions.resolve] verify_jwt = false— we do our own JWT validation inresolveAuthCtx()so that the malformed-serial and service-role-key paths can return clean errors without Supabase's auto-401 intercepting (consistent withkyle-api,graph-heal-scanprecedent;graph-readiness-report'sverify_jwt=trueis the exception, not the rule for service-role-friendly endpoints). Rollback:DROP FUNCTION IF EXISTS public.graph_serials_by_ids(text, uuid[]);andsupabase functions delete resolve(no prior version at baseline). RESOLVER-001's NULLto_serial/from_serialbaseline now hydrated at the edge layer;redacted_edge_countfinally populated per ADR D3. -
RESOLVER-001: Ships
public.resolve(p_serial text) → jsonb— the constitutional primitive for Council Protocol Law 3 (PROMPT-RESOLVER-20260611-0001, baseline83c7027). Composesgraph_node_by_serial()with a 1-hop edge expansion overgraph_edge_status × graph_manifest(filtered byedge_direction IN ('forward','both')outbound /('reverse','both')inbound;status <> 'dangling';node_typeend-match) and returns the locked Council Protocol Part VIResolvedSerialshape: identity (node_type,table_namerendered aspublic.<t>,id,row_type_label,is_terminal,has_workspace_id),outbound_edges/inbound_edges(1-hop only;to_serial/from_serialNULL in v1 per Note A — hydration deferred to RESOLVER-002),redacted_edge_count(ADR D3 RPC-zero; filled at edge-function layer in RESOLVER-003), and named placeholdersrecent_state_changes(SOT-003) /open_deliberations(COUNCIL-003), plusresolved_atandresolver_version='1.0.0'. Reason derivation forfound=false:malformed_serial(null/empty),unknown_prefix(noserial_registryrow withintroduced_in <> 'reserved'), elserow_not_found.LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path=public. Permissions:REVOKE … FROM PUBLIC, anon;GRANT EXECUTE TO authenticated, service_role(verified viapg_proc.proacl:{authenticated=X/postgres, service_role=X/postgres}, no anon, no PUBLIC). SR-003 satisfied — every column/table/function reference cited to its canonical migration source in the prompt header verification table. Inline DO-block smoke test ran during the migration transaction (PL/pgSQL loop variant per prompt escape hatch, withundefined_column/undefined_tableguard), enforcingfound=true,resolver_version='1.0.0', and presence of all locked fields — migration committed cleanly so smoke passed. POST-001 signature confirmed (args='p_serial text',returns='jsonb',proconfig=[search_path=public]); POST-003 comment present (starts withRESOLVER-001:); POST-004 search_path locked. POST-008/008b functional verification deferred to authenticated-context check (the introspection role intentionally lacks EXECUTE per POST-002). No edge function in this prompt (RESOLVER-002 wraps). Rollback:DROP FUNCTION IF EXISTS public.resolve(text);. -
RESOLVER-000 (canonical alignment): Aligned the three documentation artifacts to the canonical RESOLVER-000 prompt text verbatim.
docs/RESOLVER_ADR.mdrewritten to canonical §0–§6 structure (Purpose, Context, Decisions D1–D7 with Alternatives/Rationale/Consequence, Verification Flags Closed —danglingstatus confirmed in migration20260610190214— Open Items including SOT-003 / COUNCIL-003 named pack dependencies, References, Change Log). D3 codifies theredacted_edge_countfield. SR-003 wording indocs/FOUNDATION_ROADMAP.md§5.5 replaced with locked "Dynamic SQL Column Reference Discipline" 4-step procedure ("Violation = automatic POST fail. Source: Kyle ratification 2026-06-10. Established after ghost-column bug caught by POST-008."). SR-003 candidate row in §5 sweep table marked✅ RESOLVED — promoted to ACTIVE Standing Rule SR-003 in RESOLVER-000 (commit pending). PRE-006 row indocs/COUNCIL_PR_REVIEW_CHECKLIST.mdrewritten to canonical wording, verification statement template updated, and a per-pack gate-map row added:RESOLVER (all prompts with SQL or edge functions) | PRE-001..PRE-006 (as applicable) | POST-001..POST-008. No SQL, no edge functions, nosrc/changes. -
RESOLVER-000: Constitution check ratification + ADR + SR-003. All 7 questions ratified with Option A: (D1) one endpoint
/resolve/v1/:serial, (D2) RPC core + edge function wrapper, (D3) three-tier auth with workspace-scoped redaction, (D4) 60s edge-function memory cache, (D5) 200+found:falsefor unknown serials, (D6) SR-003 ships with RESOLVER-000, (D7) both-halves rollback. New document:docs/RESOLVER_ADR.md(serialADR-RESOLVER-20260610-0001). Standing Rule SR-003 promoted from candidate to ACTIVE inFOUNDATION_ROADMAP.md§5.5. PRE-006 added toCOUNCIL_PR_REVIEW_CHECKLIST.mdfor dynamic SQL schema verification.docs/RESOLVER_PACK_ROADMAP.mdstatus: ROADMAP_DRAFT → ACTIVE. TRAVERSABILITY pack status updated to✅ COMPLETEin foundation tracker. RESOLVER pack EXECUTING. -
TRAVERSABILITY-007b: G8 (
manifest_coverage_gaps) refined to FK-aware counting per SWEEP-012. Previous version usedpg_class.reltuplesas a row-count estimate, which false-positively flagged 13 soft edges where every source row had NULL on the FK column. Refined predicate: per-edge dynamicCOUNT(*) WHERE fk_col IS NOT NULL. Now flags only true coverage gaps (source rows exist with non-NULL FK AND ledger has zero matching entries).is_compliant=truereturned cleanly. TRAVERSABILITY pack TRULY COMPLETE. -
TRAVERSABILITY-007: Pack-closing migration. Extends
serial_compliance_check()with G8 (manifest_coverage_gaps— flags manifest edges with non-empty source tables and zero ledger evidence) and G9 (dangling_edge_summary— informational summary of dangling ledger rows for STATE_OF_TRUTH consumption). Updates G7 SECDEF allow-list to permitgraph_node_by_serialandgraph_walk(the TRAVERSABILITY-006 read-time primitives are intentionally callable by authenticated; manifest is platform metadata). G8 contributes tois_compliant; G9 is informational and does not. Commitsdocs/TRAVERSABILITY_PACK_RETROSPECTIVE.md(serialRETRO-TRAVERSABILITY-20260610-0001) as the pre-RESOLVER constitution check. TRAVERSABILITY pack COMPLETE. -
TRAVERSABILITY-006: SQL-native
graph_nodemanifest table (TRVN prefix) seeding 51 GraphNodeType rows mirroringworldportGraph.tsNODE_TABLE. 4 terminal nodes flagged (site,agent,block,daily_letter); 2 platform-global tables flagged (agent_dispatch_log,codex_jobs). Dropsgraph_target_table()heuristic. Rewritesrun_graph_scanner()andrun_graph_scanner_backfill_step()to usegraph_nodeas the single source of truth for node_type → table_name resolution. SWEEP-008 resolved: backfill now correctly handleshas_workspace_id=falsesource tables (passes NULL::uuid toges_upsert_edge). Ships two read-time traversal primitives:graph_node_by_serial(text)→ JSONB descriptor for any serial, andgraph_walk(serial, max_hops, node_limit)→ JSONB BFS traversal with ADR D3 bounds (default 3/500, hard ceiling 5/5000). Trigger surface (47 wirings, 4 dispatchers) and pg_cron schedules unchanged. SR-002 sweep CLEAN at baselinecd7a70a(75 TS = 75 DB; 0 nulls). Note: pre-flightunique_node_typescame back 50 (not 51) —daily_letteris terminal AND has no inbound manifest edges yet, so it doesn't appear ingraph_manifest; the 51-row seed still includes it for completeness. -
TRAVERSABILITY-005c: Drop
public._diag_005btable — debug artifact from the 005b discovery chain that surfaced thesuggested_target_typeghost-column bug. The fix shipped in20260610205553+20260610205709; this cleans up the diagnostic table that should not have persisted. AddedCOMMENT ON SCHEMA publicdocumenting the_diag_*no-persist convention. SWEEP-006 (filed in FOUNDATION_ROADMAP.md §5) tracks the broader convention. Smoke runpost_005c_smoke(GSCAN-GLOBAL-20260610-006):status=success,duration_ms=81,rows_scanned=1008,error_detail=NULL. Manifest=75 rows preserved. -
TRAVERSABILITY-005b: Manifest-driven backfill catch-up. New
run_graph_scanner_backfill_step(text,text,text,text,text)SECURITY DEFINER helper (service_role only) walks one manifest edge: finds source rows where the FK is populated but nograph_edge_statusledger row exists, and callsges_upsert_edge()to insert one. Terminal target nodes (site,agent,block) resolved via a small inline fallback since they never appear as afrom_node(slated for replacement in TRAVERSABILITY-006).run_graph_scanner(text)rewritten with (a) a corrected dangling sweep that derives target node type fromsplit_part(edge,'→',2)— the prior 005a body referenced a non-existentsuggested_target_typecolumn and silently rolled back every run via its outerEXCEPTION WHEN OTHERShandler; (b) a manifest-driven backfill loop over everygraph_manifestrow withedge_direction IN ('forward','both'); (c) per-iterationBEGIN/EXCEPTIONblocks so one bad edge surfaces inerror_detailinstead of killing the whole run; (d) a 25 000 ms soft time budget that preserves the 30 000 ms hard ceiling. Bootstrapbootstrap_005b(GSCAN-GLOBAL-20260610-003):duration_ms=278,rows_scanned=1008,rows_marked_dangling=0,rows_backfilled=247,error_detail=NULL. Spot check:prompts.site_id IS NOT NULL= 85 =prompt→siteledger rows. 47-trigger surface, helpers, manifest, and both pg_cron jobs unchanged. -
TRAVERSABILITY-005a:
graph_manifesttable + AUTO_INFER bidirectional adjacency. Newpublic.graph_manifesttable (uniqueness on(from_node,to_node,via_col,table_name)) carrying serial prefixTRVS(TRVS-GLOBAL-YYYYMMDD-NNN, set bytg_graph_manifest_set_serialBEFORE INSERT trigger). Newgraph_edge_directionenum (forward/reverse/both). Public read RLS (platform-global metadata; matchespg_catalogposture); service_role-only writes via migrations. Seeded all 75 manifest edges verbatim fromsrc/lib/worldportGraph.tsat baseline21ef21b, every row carryingedge_direction='both'per Kyle directive 2026-06-10 (Q2 Interpretation 3: manifest IS the bidirectional adjacency map shared by scanner and the futuregraph_walk()). Newgraph_target_table(text)SECURITY DEFINER helper resolves a node_type → its canonical table name from the manifest (heuristic; replaced by dedicated column in TRAVERSABILITY-006).run_graph_scanner(text)rewritten to a manifest-driven sweep — replaces the hardcoded 9-target CASE from 004 with a dynamic loop over distinctsuggested_target_typevalues ingraph_edge_statusthat issuesUPDATE...WHERE NOT EXISTSagainst the resolved target table. Same signature, return shape, RLS, andservice_role-only privileges. Bootstrap runbootstrap_005aexecuted successfully. Backfill catch-up deferred to TRAVERSABILITY-005b;graph_node_tablededicated column +graph_walk()to TRAVERSABILITY-006. 47-trigger surface (8+25+4+10) preserved; both pg_cron jobs (graph-scanner,freshness-mv-refresh) still alive. -
TRAVERSABILITY-004: pg_cron graph scanner. New
graph_scanner_runstable (mirrorsmv_refresh_logshape; serial prefixGSCAN, formatGSCAN-GLOBAL-YYYYMMDD-NNN) with workspace-member read RLS and platform-only writes. Newrun_graph_scanner(text)function — service_role only, SECURITY DEFINER — marks ledger rows asdanglingwhen target row has been hard-deleted from one of 9 high-volume target node types (site, agent, tenant, task, incident, loop, prompt, document, prompt_pack). New pg_cron jobgraph-scannerregistered with*/5 * * * *cadence (matchesfreshness-mv-refreshprecedent). Bootstrap run executed on migration apply. Backfill catch-up deferred to TRAVERSABILITY-005; less-trafficked target dangling deferred to TRAVERSABILITY-007 G9. The trigger surface (47 wirings, 4 dispatchers) is unchanged. -
TRAVERSABILITY-003d: Documents domain trigger migration — closes the 4-domain split. Built
tg_ensure_graph_edge_status_row_documents()reusing theges_upsert_edge(...)helper from 003b. Wired AFTER INSERT/UPDATE triggers on 10 Documents source tables (blueprints, build_chains, context_pointers, design_bridge, documents, github_pull_requests, prompt_pack_documents, prompt_pack_items, prompts, research_vault) maintaining 18 graph edges — including 3 multi-edge dispatchers (documents=4, prompts=4, context_pointers=2). Backfilled via no-op UPDATE on all 10 tables (updated_at where present, else created_at). Pre-flight SR-001 manifest-vs-FK sweep CLEAN (0 gaps, 0 phantoms) at baseline 112f7b2. Identity (003a, 8 triggers), Operations (003b, 25 triggers), Communications (003c, 4 triggers) unchanged. Total: 47 trigger wirings across 4 dispatchers — all 75 manifest edges now actively maintained on write. Unblocks TRAVERSABILITY-004 (pg_cron scanner), -005 (AUTO_INFER), -006 (graph_walk primitives), -007 (Guards G8+G9). -
TRAVERSABILITY-003c: Communications domain trigger migration. Built
tg_ensure_graph_edge_status_row_communications()reusing theges_upsert_edge(...)helper from 003b. Wired AFTER INSERT/UPDATE triggers on 4 Communications source tables (agent_chat_messages, email_integration_config, email_ingest, telegram_chat_links) maintaining 5 graph edges. Backfilled ledger rows for existing data. Identity (003a) and Operations (003b) unchanged. ~41 of 75 manifest edges now actively maintained on write. -
TRAVERSABILITY-003b: Operations domain trigger migration. Built
tg_ensure_graph_edge_status_row_operations()dispatching to a newges_upsert_edge(uuid,text,text,uuid,text,uuid,text)helper (service_role only) for clean per-edge UPSERT with ADR D6 dangling-target detection. Wired AFTER INSERT/UPDATE triggers on 25 Operations source tables (agent_action_approvals, agent_dispatch_log, agent_task_assignments, agent_tool_calls, block_installations, browser_sessions, code_health_issues, codex_jobs, deployments, execution_plans, fleet_deployments, fleet_deployment_operations, fleet_templates, fleet_template_versions, incidents, loop_state_transitions, loops, plan_audit_log, revenue_events, revenue_projections, scheduled_operation_runs, scheduled_operations, tasks, tenant_churn_signals, tenant_events) maintaining 44 graph edges. Backfilled via no-op UPDATE (updated_at where present, else created_at, else id=id for 7 timestamp-less tables); populated ledger rows for tables with existing data (agent_tool_call×2=37, block_installation×2=1, revenue_event=5, scheduled_operation=2). Identity domain (003a) function, triggers, and helper untouched. ~36 of 75 manifest edges now actively maintained on write. -
TRAVERSABILITY-003a: Identity domain trigger migration. Phase 1 schema prep:
graph_edge_status.workspace_idmade nullable (ADR D2) with RLSges_selectupdated to admitworkspace_id IS NULLglobal rows;'dangling'added to status CHECK constraint (ADR D6); reverse-traversal indexidx_graph_edge_status_target_edgeon(suggested_target_id, edge)added (ADR D8). Phase 2 triggers: AFTER INSERT/UPDATE on 8 Identity-domain source tables (agentos_connections, agentos_connection_health, agentos_events, agentos_observability_counters, integration_credential_links, integration_events, site_integrations, tenants) write ledger rows via newtg_ensure_graph_edge_status_row_identity()SECURITY DEFINER function. UPSERT per ADR D5; dangling FK targets surface asstatus='dangling'. Backfill no-op UPDATEs ran on all 8 tables (site_integration→site=20 confirmed, integration_credential_link→site_integration=27, integration_event→site_integration=29, tenant→site=4 unconfirmed). Existingtg_ensure_graph_edge_status_row()(research_vault + documents) untouched. -
TRAVERSABILITY-002: Expanded
src/lib/worldportGraph.tsmanifest from 14 → 51GraphNodeTypevalues and 24 → 75GRAPH_EDGESacross the Identity, Operations, Communications, and Documents domains. All edges derived verbatim from §1 ofdocs/TRAVERSABILITY_MANIFEST_AUDIT.mdat TRAVERSABILITY-001c baseline (31f8d9e) — no inference.NODE_TABLEextended with table mappings for every new node type soRecord<GraphNodeType, string>stays exhaustive. Header comment updated to reference ADR D12 and the 001c baseline.AutoInfer*types,edgeKey, andAUTO_INFER_RULESuntouched. No duplicate(from,to,via,table)tuples. Sets the stage for the trigger migrations in TRAVERSABILITY-003a/b/c/d. -
TRAVERSABILITY-001c: FULL REBUILD of
docs/TRAVERSABILITY_MANIFEST_AUDIT.md§1 from SQL truth (pg_catalog.pg_constraintquery against activeserial_registryprefixes at baseline6d6fd6b). Replaces all three prior memory-driven revisions (001/001a/001b). Four documented overrides applied on top of raw query output: research_vault dual-prefix preservation (AUDIT+RV both A); TDEP junction-table exemption (B); BLK reference-data downgrade (B) with BPT and COMP cascade downgrades; GEST self-reference exclusion (B). Final counts: A=48, B=27 (75 active + 5 reserved). Methodology change: future classification audits are SQL-driven, not manual — Schema Auditor v3 G8 (TRAVERSABILITY-007) will enforce this automatically. ADR D12 unchanged. Onlydocs/TRAVERSABILITY_MANIFEST_AUDIT.mdandCHANGELOG.mdtouched. -
TRAVERSABILITY-001b: Reconciled off-by-3 discrepancy in
docs/TRAVERSABILITY_MANIFEST_AUDIT.md. SNIP (copy_snippets) and DL (daily_letters) reclassified A→B per ADR D12 (tenancy-only and terminal-root respectively — neither had real entity FKs beyond workspace_id). RPROJ (revenue_projections) stays A but edge column corrected from→ workspaceto→ block (block_id)— the real entity FK on this table. Final classification: A=43, B=32, total=75 active + 5 reserved. §1 and §2 now consistent. -
TRAVERSABILITY-001a: Revised
docs/TRAVERSABILITY_MANIFEST_AUDIT.mdto honor the existingworldportGraph.tsconvention thatworkspace_idis tenancy scope, NOT a graph edge. Reclassified 8 tables from Category A to Category B (workspace_members, workspace_accounts, workspace_invites, integrations_registry, agents, api_keys, sites, workspaces). Identity domain shrank from 12 to 4 tables. Added ADR D12 formalizing the convention. Canonical exemption string established for tenancy-only / terminal-root tables. New totals: Category A=42, Category B=33, reserved=5, total=80. -
TRAVERSABILITY-001: Authored
docs/TRAVERSABILITY_MANIFEST_AUDIT.md— classification of all 75 activeserial_registryprefixes into Category A (50 — add as graph manifest node), Category B (25 — exempt with documented reason), Category C (0 — all edge cases resolved during classification). Domain split assigned for TRAVERSABILITY-003a/b/c/d trigger migrations: Identity (12 tables), Operations (21 tables), Communications (6 tables), Documents (13 tables). Audit is read-only post-merge. -
TRAVERSABILITY-000: Authored
docs/TRAVERSABILITY_ADR.md— Architecture Decision Record for the TRAVERSABILITY pack. Captures 11 decisions (D1–D11) including: node identifier is(table_name, id)+serialshortcut (noentity_id),workspace_idnullable + CHECK ongraph_edge_statusfor global tables,graph_walkcaps at 3 hops default / 5 ceiling / 500 nodes default, soft-deprecation on DELETE without bitemporal columns (sweep filed), UPDATE triggers in scope, trigger migrations split by domain, RPC core + REST wrapper. ADR is read-only post-merge; future changes require new ADR superseding this one. -
AUDITOR-003: Added
summary_stringfield topublic.serial_compliance_check()— single-line human-readable result. Reads'✅ COMPLIANT · N active prefixes, M reserved, 0 drift across 7 guards.'when clean, or'❌ NON-COMPLIANT · ...'enumerating each non-empty drift category by count when not. Closes the SCHEMA_AUDITOR_v2 pack. G5 (TypeScript AST scan) deferred to a separate CI-guards sweep. -
AUDITOR-002: Added
public.serial_registry_exceptionscompanion table toserial_registryfor documented immutable-history allowlists (PK(table_name, prefix_pattern), RLS enabled, SELECT for authenticated, INSERT/UPDATE/DELETE for service_role only). Seeded 19 baseline exceptions: 7 from SERIAL-008 trigger cleanup (DEP, EML, LOOP, PG, PR, RUN, AEV), 11 from SERIAL-008d shared-trigger cleanup (APPR, ASGN, HLTH, AOC, ESCR, LST, PLOG, RPROJ, INV, ECFG, TGLINK), and 1 for legacy RouteOS tasks (TSK). Extendedserial_compliance_check()with G6 (HISTORICAL_PREFIX_MISMATCH) — flags sampled rows whose serial prefix maps to a different table inserial_registry. Refined G4 (SCOPE_SEGMENT_DRIFT) to consult the exceptions table so documented historical rows no longer surface. Refined G7 (SECDEF_AUTHENTICATED_LEAK) to skipRETURNS triggerfunctions structurally — removed the 5 trigger entries from the AUDITOR-001 allowlist. Function now returns 13 fields including the newhistorical_prefix_mismatcharray;is_compliant=truerequires all 7 drift arrays empty. -
AUDITOR-001: Extended
public.serial_compliance_check()with three new drift guards. G3 (PREFIXCOLLISION) — finds prefixes called by trigger functions on multiple tables. G4 (SCOPE_SEGMENT_DRIFT) — samples up to 5 recent rows per registry table and surfaces any serial not matching canonical^[A-Z]+-([A-Z0-9]{2,8}|GLOBAL)-\d{8}-\d+$. G7 (SECDEF_AUTHENTICATED_LEAK) — finds SECURITY DEFINER functions with authenticated EXECUTE, against an allowlist. Helperaudit_sample_serials_for_table(text)added (service_role only).is_compliant=truenow requires all 6 drift arrays empty. G7 allowlist expanded beyond spec to include pre-existing legitimate authenticated-callable SECDEF fns surfaced by pre-flight (workspace auth helpers, compute* helpers, user-action RPCs, secret hashing, and 5 trigger functions). G4 will surface pre-2026-06-09 immutable-history rows on first call; AUDITOR-002 will introduceserial_registry_exceptionsto allowlist them. -
SERIAL-008f: Two small fixes. (1) Corrected
duration_msmath inrefresh_mv_platform_freshness_logged()— parenthesize-then-cast so sub-second refreshes log non-zero ms (was logging 0 for any refresh under 1 second due to premature integer cast). (2) RevokedEXECUTEfromauthenticatedonrefresh_freshness_tier1-tier4— closes the same privilege leak the SERIAL-008e follow-up migration closed for the wrapper function. All 5 cron helpers now correctly limited toservice_roleand pg_cron. -
SERIAL-008e: Wired pg_cron
freshness-mv-refreshjob (5-minute cadence) through a logged wrapperpublic.refresh_mv_platform_freshness_logged(). Each REFRESH now produces anmv_refresh_logrow with status (running→successorerror), duration, row count, anderror_detailon failure. Wrapper does NOT re-raise on failure — the cron job continues, and the error row is the audit trail. Closes the deferral noted inmv_refresh_logtable comment from SERIAL-006. -
SERIAL-008d: Closed shared-trigger format drift. (1) Rewrote
mbo_set_serial_tgto pass'GLOBAL'instead ofNULL— fixes 34 SERIAL-001-installed global-scope triggers in one stroke. (2) Created dedicated triggers for the 4 site-scoped SERIAL-001 tables (design_bridge/DSB,email_integration_config/ECFG,site_integrations/SINT,telegram_chat_links/TGLINK) that look up site code via FK. Updated 11serial_registry.notesto reflect the fix (APPR's note was rewritten in a follow-up because its existing wording didn't match the spec's REPLACE pattern). No backfill of historical scope-less serials. SERIAL pack is now structurally complete. -
SERIAL-008c: Fixed AGT prefix collision.
agentos_connectionsnow mints serials with prefixAGTCONNinstead ofAGT(which still belongs toagents). NewAGTCONNregistry row added. HistoricalAGT-*serials onagentos_connectionsare immutable broken history and will misroute through Resolver — documented in SERIAL_DICTIONARY footnote (forthcoming). -
SERIAL-008b: Registry reconciliation round 2. Seeded 11 missing prefixes wired by SERIAL-001's dynamic installer (APPR, ASGN, HLTH, AOC, ECFG, ESCR, LST, PLOG, RPROJ, TGLINK, INV). Closes SWEEP-004 by adding PPI prefix + trigger + backfill for
prompt_pack_items. Registry: 79 rows (74 active + 5 reserved). AGT collision andmbo_set_serial_tgNULL-fallback drift remain — see SERIAL-008c and SERIAL-008d. -
SERIAL-008: Closing pack. (1) Rewrote 7 trigger functions to emit canonical 'GLOBAL' fallback (tg_serial_dep, email_ingest_set_serial, tg_serial_loop, tg_serial_page, tg_serial_pr, tg_serial_sched_run, agentos_events_set_serial). (2) Created
public.serial_compliance_check()returning JSONB compliance summary — first permanent State Verifier check. (3) Section 3 skipped: pg_cron unchanged (gate requires explicit confirmation; not present in drop). No backfill of historical serials. Compliance check surfaced 13 previously-unknown unregistered tables withserialcolumns — filed for new SWEEP item, not auto-handled. -
SERIAL-007: Populated
docs/SERIAL_DICTIONARY.md— Active Prefixes table (62 rows), Reserved Prefixes table (5 rows), How Serials Are Generated, Serial Format Reference, How To Add a New Prefix. Documented derived-site format pattern (FDO, FLV) and the 7 trigger functions with canonical-format drift filed for SERIAL-008. -
SERIAL-007a: Reconciled
serial_registryagainst live triggers. Seeded 12 pre-SERIAL orphan prefixes (AEV, BSESS, DEP, EML, FDO, FDP, FLT, FLV, LOOP, PG, PR, RUN). Registry now has 67 rows (62 active + 5 reserved). Six trigger functions still use legacy 'GEN' fallback and one uses NULL — both filed for SERIAL-008.
SERIAL-006 — Materialized View Refresh Log
-
Added
mv_refresh_logtable (12 columns: id, serial, view_name, workspace_id, triggered_by, started_at, completed_at, duration_ms, rows_affected, status, error_detail, created_at). -
Serial prefix
MVRL. Format:MVRL-GLOBAL-YYYYMMDD-NNN. -
BEFORE INSERT trigger via dedicated
tg_mv_refresh_log_set_serial()function (not sharedmbo_set_serial_tg— shared function passes NULL site_code, which producesMVRL-YYYYMMDD-NNN; dedicated function passes 'GLOBAL' explicitly). -
FK: workspace_id → workspaces(id) ON DELETE SET NULL.
-
RLS: authenticated SELECT (workspace_id IS NULL OR is_workspace_member); no write policies — service_role only.
-
serial_registryrow for MVRL added. -
pg_cron wiring deferred to SERIAL-008.
-
feat: SERIAL-005 — Category C cleanup. Added missing
TDEProw toserial_registry(gap: SERIAL-002 seed derived frommbo_serial_counterswhich had no TDEP entries at seed time; column and trigger were live from SERIAL-001, registry was not). Createddocs/SERIAL_DICTIONARY.mdskeleton withblock_dependenciesexemption section and junction table convention. All three Category C serialized tables (WS, WMEM, TDEP) now have complete registry coverage. Zero orphan prefixes inmbo_serial_counters.
SERIAL-004 — Schema Auditor Helper Function
Files added:
supabase/functions/_shared/serialAuditor.ts— Schema Auditor Council seat helper. ExportsschemaAuditor_checkSerialPresenceandschemaAuditor_listPrefixesForTable. Read-only queries againstinformation_schema.columnsandserial_registry.supabase/functions/_shared/README.md— Establishes the_shared/shared-utility convention for edge function imports.
Convention established: supabase/functions/_shared/ is the canonical location for
TypeScript modules shared across multiple edge functions. Future packs must use this
directory for shared utilities (URL imports, Deno-compatible, no side effects).
No schema changes. No application code changes.
Depends on: SERIAL-002 (serial_registry seeded).
Used by: COUNCIL pack (Schema Auditor edge function, seat 4).
-
feat: SERIAL-003 — Writer-discipline audit +
research_vaultset_serialBEFORE INSERT trigger (AUDIT prefix, site-code aware). RefactoredsiteAudit.core.tsPhase G to drop app-layermbo_generate_serialRPC andserial:field; trigger mints the value,.select("serial")reads it back. DefensiveIF NEW.serial IS NULL OR NEW.serial = ''guard preserves the lingeringscheduled-site-auditedge fn assignment until SWEEP-005 (filed indocs/FOUNDATION_ROADMAP.md§5, tangled with audit-core dedup). Full audit table indocs/SERIAL_WRITER_AUDIT.md— all server fns clean, all edge fns clean except the one deferred.- follow-up: dispatched trigger by
doc_type(AUDIT foraudit_report, RV otherwise), aligned fallback to'GLOBAL', updatedserial_registry(new RV row + clarified AUDIT notes).
- follow-up: dispatched trigger by
-
feat: Codex Console at /codex — dispatch form (with smoke-test preset, advanced fields, confirm modal) and live Recent Codex Jobs table with realtime updates, detail viewer, and operator cancel.
-
feat: Enforce graph membership via DB trigger + healing page item viewer & manual site picker.
-
fix: Atlas recompute crashed with
column i.title does not exist— incidents table usesserial/summary, nottitle. -
feat: Graph-healing scan button now surfaces success/error toasts instead of failing silently.
Changelog
feat: Graph healing follow-ups — unhealed badge, confirmed % in report, heal-forward hook
Shell.tsx: "Graph Healing" sidebar item now renders the unhealed-edge count as a warning badge (same pattern as the dashboard orphan badge). Hidden when count is zero; updates on window focus.scripts/run-graph-audit.ts+supabase/functions/graph-readiness-report/index.ts: Addedconfirmed_fillper edge (sourced fromgraph_edge_status) alongside rawsoft_link_fill. Markdown report shows both columns; verdict line now also surfaces overall confirmed-link trust %. Scoring formula unchanged (additive reporting only).graph-heal-scan: Now accepts{ workspace_id, source_table, source_id }for single-node heal-forward in addition to the existing batch mode. Reuses the canonical AUTO_INFER rules — no duplication.- New
src/lib/healForward.ts: Fire-and-forget helper that invokesgraph-heal-scanfor a single node; swallows errors so creation flows never block. src/lib/uploadDocument.ts+src/components/research-vault/VaultEntryModal.tsx: Call heal-forward after successful document / vault inserts. New rows immediately land in the ledger as confirmed (deterministic) or unconfirmed+suggestion — never silently empty. Kyle system writers (kyle-session-distill,worldport-brief-generate) intentionally skip heal-forward.
feat: PROMPT-19 Graph healing ledger + research/doc→site healing (flag-unhealed, heal-forward)
- Migration
20260531134523: Newgraph_edge_statustable — workspace-scoped ledger tracking every soft-edge instance asconfirmed,unconfirmed, ornot_applicable, with suggested target, evidence, confidence, decider. RLS viais_workspace_member/can_write_workspace; inserts service-role only; unique on (workspace_id, edge, source_id);updated_attrigger. src/lib/worldportGraph.ts: AddedAUTO_INFER_RULESregistry +AutoInferRule/AutoInferOutcome/AutoInferContexttypes andedgeKey()helper. Conservative pure-function rules forresearch_vault→site(site_id set → confirmed; app_tag match → suggested) anddocument→site(site_id set → confirmed;linked_record_type='site'→ confirmed + write; capture_metadata or tags single match → suggested). Ambiguous evidence →unconfirmed, never a silent guess.- New edge fn
graph-heal-scan(verify_jwt=false, service-role): scans soft-edge source rows in a workspace, applies the rule, upserts ledger rows, and performs deterministic domain writes only whenconfidence='auto'ANDapplyDomainWrite=true. Never auto-appliessuggested. Returns{ totals, per_edge }. - New
src/lib/graphHealing.functions.ts: Server functionslistGraphHealing,confirmGraphEdge(writes domain FK + ledger),markEdgeNotApplicable,rejectEdgeSuggestion,runGraphHealScan,unhealedCount. All gated byrequireSupabaseAuth; RLS applies. - New
/graph-healingroute: three buckets (Needs review / Unconfirmed / Confirmed) per edge, with Confirm / Reject / N/A actions and a "Run heal scan" button. Header shows confirmed %. Shell.tsx: New "Graph Healing" nav item under Build.supabase/config.toml: Registered[functions.graph-heal-scan] verify_jwt = false.- Partial / follow-up: Shell unhealed-count badge, audit-report
confirmed %integration, and heal-forward hook on document/research creation are scaffolded server-side (unhealedCountserver fn ready) but not yet wired into the sidebar render andgraph-readiness-report— to land in a follow-up.
chore: Run graph readiness audit — populate WORLDPORT_GRAPH_READINESS.md with live numbers
- New
scripts/run-graph-audit.ts(Prompt 18.1): Service-role script that iterates every workspace, reads thegraph_audit_*views, and overwritesWORLDPORT_GRAPH_READINESS.md+WORLDPORT_GRAPH_READINESS.jsonwith live numbers. Aggregation + scoring logic copied verbatim fromsupabase/functions/graph-readiness-report/index.tsso the script and edge function stay identical. package.json: Addedgraph:auditscript (bun run scripts/run-graph-audit.ts).- First run summary:
WorldPort — needs_attention (64) — avg soft fill 19% — orphans 0 — dangling 0. Soft-link fragmentation is the next phase to address (most soft edges <25% populated); no orphans or dangling FKs.
feat: WorldPort graph readiness audit + canonical graph manifest (foundation only)
- New
src/lib/worldportGraph.ts: Canonical graph manifest — 15 node types and 24 edges (6 hard, 18 soft) derived directly fromsrc/integrations/supabase/types.tsRelationships. Single source of truth for future graph-traversal tools. No invented edges. - New migration
20260531124717_graph_readiness.sql: Adds 5 read-onlygraph_audit_*VIEWS (security_invoker = true, granted to authenticated + service_role):graph_audit_node_counts,graph_audit_soft_link_fill,graph_audit_dangling,graph_audit_orphans,graph_audit_isolated. No domain tables changed. No FK constraints altered. No data mutated. - New
supabase/functions/graph-readiness-report/index.ts: Member-authed (verify_jwt = true). For a givenworkspace_id, queries the audit views and returns{ node_counts, soft_link_fill, orphans, dangling, isolated_nodes, unenforced_edges, readiness_score, verdict, recommendations }. Read-only. - New
WORLDPORT_GRAPH_READINESS.md: Human-readable report at repo root; documents the canonical manifest and is overwritten by each audit run. - Modified
supabase/config.toml: Registered[functions.graph-readiness-report] verify_jwt = true. - No Kyle-facing graph-read tools built in this prompt. Foundation + report only — graph-traversal tools come in a later prompt, after the operator certifies the report.
feat: Resumable agent sessions + team author attribution
- Modified
src/routes/agents.$agentId.tsx: Added?session=search param (zod-validated), History sheet listing the 10 most-recent sessions for this agent+workspace, and a "New" button. Default session id resolves to the latest existing session for the agent so chat history no longer "vanishes" on refresh.<AgentChatInterface>keyed by session id so switching forces a fresh mount. - Modified
src/lib/agentChat.functions.ts: Resolvesauthor_namefromworkspace_members.display_name(fallback to JWTname/email local-part), stores it on the user message'smetadata.author_name, and prefixes user turns with[Name]:when building the composio history payload so the model can attribute decisions to specific operators. - Modified
src/components/agents/AgentChatInterface.tsx: Selectsmetadataand renders the small author label above each user bubble. - Modified
supabase/functions/kyle-session-distill/index.ts: Transcript now tags operator turns asOPERATOR (Name):and the distill prompt instructs Kyle to attribute decisions/action items to the named operator when clear. - Dependency: Added
@tanstack/zod-adapterfor the search-param validator.
feat: Proactive daily Kyle briefing (scheduled, read-only)
- Migration: Added
kyle_briefingtoscheduled_operations.operation_typeCHECK and seeded one daily op per workspace (0 12 * * *UTC ≈ 7am CT). Idempotent. - New
supabase/functions/kyle-briefing-generate/index.ts: Service-role edge function. Gathers open incidents, ready prompts, recentagent_tool_calls, orphan counts (documents/context_pointers/tasks/incidents) and stale vault count for a workspace; calls Anthropic via AI SDK to produce a markdown briefing (Top 3 / Status / What Kyle noticed / Suggested next actions). Upserts aresearch_vaultrow (doc_type='briefing',app_tag='@WPT',source_url='mbo://briefing/<date>') — one row per day, never duplicates. Returns{ ok, vault_id, highlights_count }. Read-only; never executes any action. - Modified
src/routes/api/public/scheduler-tick.ts: Addedkyle_briefingdispatch branch (same pattern asworldport_brief), invoking the edge function with service-role bearer. - Modified
supabase/config.toml: Registered[functions.kyle-briefing-generate] verify_jwt = false.
feat: MBO-native action tools for Kyle (via kyle-api, approval-gated)
- Modified
supabase/functions/kyle-composio/index.ts: Registered 8 MBO-native AI SDK tools. Reads (mbo_list,mbo_get) execute immediately by calling thekyle-apiedge function withAuthorization: Bearer ${KYLE_MBO_API_KEY}. Writes (mbo_create_task,mbo_file_incident,mbo_add_prompt,mbo_link,mbo_vault_append,mbo_update_blueprint) DO NOT execute — they insert a pending row intoagent_action_approvalsvia a sharedrecordProposalhelper, with the full kyle-api call shape stored inproposed_args.mbo_call.entityis constrained to thekyle-apiALLOWED_ENTITIES enum at the tool layer. System prompt updated so Kyle knows it can operate MBO directly but writes still require operator approval. - Modified
supabase/functions/agent-action-execute/index.ts: On approve, iftool_slugstarts withmbo_andproposed_args.mbo_callis present, the function forwards the call tokyle-apiwithKYLE_MBO_API_KEYinstead of invoking Composio. Result is persisted toagent_action_approvals.resultand audited inagent_tool_callsas before. Composio path unchanged for non-MBO tool slugs. - Secret:
KYLE_MBO_API_KEY(operator generates a key in/integrations/api-keysnamedkyle-agent, then pastes plaintext into Lovable Cloud secrets — hash stays inapi_keys; plaintext never committed or logged).
feat: Write-scope approval gate — human-in-the-loop for Kyle
- Migration
agent_action_approvals: New table withworkspace_id/agent_id/session_id,tool_slug/toolkit,risk(write|destructive CHECK),proposed_args(jsonb, redacted),summary,status(pending|approved|rejected|executed|failed CHECK),decided_by/decided_at,result/error. RLS on,is_workspace_member()for SELECT,can_write_workspace()for UPDATE. INSERT is service-role only (no member INSERT policy). Indexed on(workspace_id,status)and(session_id). GRANTs to authenticated + service_role. - Modified
supabase/functions/kyle-composio/index.ts: Added explicit risk classifier (READONLY_PATTERNS/DESTRUCTIVE_PATTERNSregex). Composio tools are filtered to read-only before passing togenerateText. A localpropose_actionAI SDK tool (zod-validated args) is added — it inserts anagent_action_approvalsrow (statuspending, args redacted viaredactArgs) and returns "Proposed — awaiting operator approval." System prompt rewritten so Kyle MUST route any write/destructive action throughpropose_action. Audit logging untouched. - New
supabase/functions/agent-action-execute/index.ts(verify_jwt = true): Body{ approval_id }. Loads the row via the caller's JWT (RLS enforces workspace), requiresstatus='approved', opens a Composio session, executes the named tool withproposed_args, updates the row toexecuted+resultorfailed+error, and writes anagent_tool_callsaudit row. Never logs secrets. - Modified
supabase/config.toml: Added[functions.agent-action-execute]verify_jwt = true. - New
src/lib/agentApprovals.functions.ts: Three RLS-scoped server fns underrequireSupabaseAuth—listPendingApprovals({ workspace_id, session_id? }),approveAction({ approval_id })(sets approved + decided_by/decided_at, then forwards the caller's bearer toagent-action-execute),rejectAction({ approval_id, reason? })(sets rejected; only flips rows still inpending). - New
src/components/agents/PendingApprovals.tsx: For the active workspace + session, polls pending approvals every 5 s. Each card shows the summary, tool slug, a risk chip (warning|danger), and Approve / Reject buttons (worldport primitives). On Approve → callsapproveAction→ toast result + invalidate. Returnsnullwhen empty. - Modified
src/components/agents/AgentChatInterface.tsx: Renders<PendingApprovals workspaceId={workspaceId} sessionId={sessionId} />betweenToolCallBadgesand the input bar — visible inline in the chat for the active session. - Modified
src/components/worldport/Shell.tsx: Added a smallPendingApprovalsBadgenext to the bell in the TopBar — workspace-scoped count badge that polls every 10 s and renders nothing when zero. AddeduseQueryimport. PerLOVABLE_RULES.md: "Kyle write/destructive tools route through agent_action_approvals; never auto-execute."
feat: Mobile MBO partner chat at /kyle (installable shortcut)
- New
src/routes/kyle.tsx:createFileRoute("/kyle")— full-screen Kyle chat. Resolves theprovider='composio'agent named "Kyle" for the activeuseWorkspace()(same lookup asKyleLauncher). Layout adapts touseIsMobile(): on mobile, fixed inset wrapper from top tobottom-[64px]withpb-[env(safe-area-inset-bottom)]so the chat input sits above the bottom nav and respects safe-area insets; on desktop, centeredmax-w-3xlath-[calc(100dvh-64px)]. Header has a "New" button that generates a freshsession_idand a "History" Sheet that loads up to 10 distinct recentsession_ids fromagent_chat_messages(most-recent first) with a preview line; tapping one loads that session's messages. Renders the existing<AgentChatInterface>(no fork) keyed bysessionId. Falls back to the same "Kyle isn't set up for this workspace yet" message asKyleLauncher. - Modified
src/components/agents/AgentChatInterface.tsx: Added optionalsessionIdandclassNameprops. WhensessionIdis provided, it's used verbatim (thekeyon the parent re-mounts on change); otherwise the existingcrypto.randomUUID()behavior is preserved.classNameoverrides the defaulth-[60vh] border rounded-lgwrapper so callers can opt into full-flex / full-height layouts. All existing callers (KyleLauncher, etc.) continue to work unchanged. - Modified
src/components/mobile/MobileBottomNav.tsx: Replaced the "Notes" slot with a "Kyle" tab (lucideZap) linking to/kyle. Existing tabs (Home, Queue, Tasks, Menu) and styling preserved. - Modified
public/manifest.json: Added ashortcutsarray with{ name: "Ask Kyle", short_name: "Kyle", url: "/kyle", description: "Open your WorldPort partner" }so long-pressing the installed MBO app icon offers "Ask Kyle" as a deep link. All existing manifest fields unchanged. No new packages, no service worker, no fork of chat logic — desktopKyleLauncherand mobile/kyleroute coexist on the sameAgentChatInterface.
feat: Auto-distill Kyle think-sessions into the research vault
- New
supabase/functions/kyle-session-distill/index.ts: Service-role edge function (POST { workspace_id, agent_id, session_id }) that loads allagent_chat_messagesfor a session (ordered), skips with{ skipped: "too_short" }if fewer than 2 messages, builds a compact OPERATOR/KYLE transcript (capped ~24k chars), and produces a structured distillation via@ai-sdk/anthropic+generateTextusingAGENT_MODEL(no tools, plain text). The model output follows a fixed shape —# Title,## TL;DR,## Key decisions,## Action items,## Open questions,## Sources / links mentioned. Upserts exactly ONEresearch_vaultrow per session keyed on(workspace_id, doc_type='session_note', source_url='mbo://session/<session_id>'): updatestitle,overview(= parsed TL;DR),content(full markdown),app_tag='@WPT',status='active',freshness_score=0.9,last_reviewed_at,updated_atif it exists; otherwise inserts and lets the serial trigger assignserial. Returns{ ok, vault_id }. Never logs conversation contents. - Modified
src/lib/agentChat.functions.ts: After the assistant reply is inserted (composio branch only), fire-and-forget a POST to${SUPABASE_URL}/functions/v1/kyle-session-distillwith the service-role bearer. Debounced:count(*)of session messages must be ≥ 4 AND either even, OR the previous message'screated_atis > 90 s ago. Wrapped in try/catch with.catch(() => {})on the fetch — never awaited, never throws, never blocks the user's reply. Read tools, openai/aria/kyle_base44 branches untouched. - Modified
supabase/config.toml: Added[functions.kyle-session-distill]verify_jwt = falseso it can be invoked server-to-server. No new tables — distillation is additive on top ofagent_chat_messages;research_vaultretains exactly one canonicalsession_noterow persession_id.
feat: Kyle loads living WorldPort brief + semantic session memory
- Modified
supabase/functions/kyle-composio/index.ts: Before eachgenerateTextturn, the function now loads (a) the canonical living WorldPort brief fromresearch_vault(workspace_id,doc_type='context_brief',app_tag='@WPT') and (b) semantically-recalled prior research/sessions — embeds the latest user message with the sametext-embedding-3-smallmodel used byvault-search/embed-vault-entryand calls the existingmatch_research_vaultRPC with the service-role admin client (p_workspace_id,p_query_embedding,p_limit=5). The top matches are formatted as- {title}: {overview}lines (capped ~6k chars). Brief + memory are appended to the base persona under labeled sections (## WorldPort Context …and## Relevant prior sessions / research) and passed assystemtogenerateText. Brief truncated to ~12k chars to stay within model context. Both loads are wrapped in try/catch withconsole.errorlogging only the failure message — Kyle still answers from the base persona if the brief row is missing or recall fails. No new embedding pipeline, no schema changes, audit logging and tool loop untouched. Never logs brief or memory contents.
feat: Living WorldPort context brief — auto-refreshed every 6h
- New
supabase/functions/worldport-brief-generate/index.ts: Service-role edge function (POST { workspace_id }) that assembles a single Markdown brief from live MBO data — pinnedcontext_pointerswithkind='fundamental'(top 30, pinned first),sites(code/name/status, cap 60), openincidentscount + top 5 by severity, 10 most-recentblueprints, 10 most-recentresearch_vaultrows withfreshness_score >= 0.5(excludingdoc_type='context_brief'so the brief never references itself), and the latestdaily_lettersrow (first ~400 chars). Output capped to ~24k chars. Upserts ONE canonicalresearch_vaultrow per workspace keyed on(workspace_id, app_tag='@WPT', doc_type='context_brief')— updatestitle,content,overview,freshness_score=1.0,last_reviewed_at,updated_atif it exists, otherwise inserts withstatus='active'and lets the serial trigger assignserial. Returns{ ok, vault_id, chars, generated_at }. Never logs content. - Modified
supabase/config.toml: Added[functions.worldport-brief-generate]withverify_jwt = falseso scheduler-tick can invoke it server-to-server with the service-role bearer. - Modified
src/routes/api/public/scheduler-tick.ts: Newworldport_briefbranch indispatch(op)— POSTs to${SUPABASE_URL}/functions/v1/worldport-brief-generatewithAuthorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}and the op'sworkspace_id. Resultokrequires bothres.okandout.ok === true; failure surfaces asbrief HTTP <status>.nextRunAtand all other branches unchanged. - Migration: Extended the
scheduled_operations_operation_type_checkCHECK constraint to allow'worldport_brief'. Seeded onescheduled_operationsrow per existing workspace (name='WorldPort Context Brief',operation_type='worldport_brief',schedule_type='cron',cron_expression='0 */6 * * *',status='active',next_run_at=now(),alert_on_failure=false) usingWHERE NOT EXISTSso re-running is a no-op. No new tables — the brief lives inresearch_vault, the schedule lives inscheduled_operations.
feat: Dashboard 3-zone layout + Kyle activity strip
- New
src/components/dashboard/KyleActivityStrip.tsx: Resolves the workspace's Kyle agent (agents.provider='composio'+name='Kyle') then queries the last 5agent_tool_callsrows for that agent. Renders a horizontal strip of small chips inside a worldportCard— each chip shows aZapicon, toolkit/tool_slug label (truncated to 20 chars), and a status dot (bg-successforstatus='ok', otherwisebg-danger). Relative time shows on hover viatitle. Returnsnullwhen no rows exist — no empty-state card. Uses Tailwind v4 design tokens only. - Modified
src/routes/index.tsx: Restructured the sequential widget stack into three labeled zones — Now (4 StatCards + TodayFocusCard 2/3 + InboxWidget 1/3), Today (Daily Letter 2/3 + VelocitySparklineStrip 1/3), and Context (KyleActivityStrip, then 2-col grids for AgentStatusWidget+AriaStatusCard, FreshnessWidget+FinancialStrip, ActivityFeed+SiteHealthStrip). Zone headers usetext-[11px] font-semibold uppercase tracking-[0.05em] text-text-tertiary. RemovedTopPromptsCardand itstopPromptsResquery (superseded by TodayFocusCard); droppedtopPromptsfromDashboardData. All other queries, the realtime channel subscription, and the refresh selector are unchanged.TopPromptsCard.tsxitself was not deleted.
feat: Cross-entity link chips on Incidents + Tasks
- New
src/components/incidents/IncidentLinkChips.tsx: RendersChiplink chips forlinked_task_id(→/tasks?selected=…),linked_prompt_id(→/prompt-queue?selected=…), andlinked_pr_serial(→/prompt-queue?selected=…). Null-safe — returns nothing when no links are present. Chips use the worldportChipprimitive withtone="info"and wrap TanStackLinkwithstopPropagationso row/card clicks don't fire. - New
src/components/tasks/TaskLinkChips.tsx: RendersChiplink chips forsource_email_id(→/email) andparent_task_id(→/tasks?selected=…). Shows "↗ Subtask of {parent_serial?.slice(0,16)}" when a parent serial is supplied. Same null-safe,stopPropagation,Chipprimitive approach. - Modified
src/components/incidents/IncidentTable.tsx: Added "Links" column between Status and Opened. Each row renders<IncidentLinkChips>via the existingTDprimitive. Updated empty-statecolSpanfrom 7 → 8. - Modified
src/routes/incidents.tsx: AddedIncidentLinkChipsimport. In the mobile card view, chips render below the existing status/site/serial chip row.select("*")already covers the linked fields — no query change needed. - Modified
src/components/tasks/TaskCard.tsx: ImportedTaskLinkChips. Added a new flex row below the priority/site/due/assignee chip row (outside the card's inner<button>to keep valid HTML). Chips render forsource_email_idandparent_task_id. - Modified
src/components/tasks/TaskList.tsx: ImportedTaskLinkChips. In the mobile list item layout, chips render below the existing priority/site/due row inside theflex-1content area. - Modified
src/routes/tasks.tsx: Addedselected?: stringto theSearchtype so/tasks?selected=…is valid for incoming cross-entity links.select("*")already coverssource_email_idandparent_task_id. - No new deps, no schema changes, no Kanban column changes.
feat: ⌘K command palette — navigate, quick actions, recent
- New
src/components/worldport/CommandPalette.tsx: shadcnCommandwrapped inDialog. Three groups: Navigate (flat list derived from exportedNAV+BUSINESS_ITEMSinShell.tsx, with section hint chips), Actions (New Prompt →/prompt-queue?new=1, New Task →/tasks?new=1, New Incident →/incidents?new=1, New Capture →/capture/note, Ask Kyle), and Recent (last 5mbo_audit_logrows for the current workspace via TanStack Query, only fetched while open). Entity type → route map covers prompt / incident / task / agent / site / document; relative timestamp on each row. - Modified
src/components/worldport/Shell.tsx: ExportedNAVandBUSINESS_ITEMSso the palette can build its flat route list without duplication. Mounted<CommandPalette>once insideShellalongsideKyleLauncher. Added adocumentkeydownlistener that toggles the palette on⌘K/Ctrl+Kand prevents the default browser shortcut. Replaced the static TopBar<input>with a visually identical button that opens the palette;onOpenPaletteprop threaded throughTopBar. Escape closes via shadcnDialogdefault behavior. - Ask Kyle wiring: Palette dispatches a
windowCustomEvent('kyle:open');KyleLauncherlistens for it viauseEffectand opens its existingSheet. No new component coupling. - No new deps; no route file changes (the
?new=1auto-open in destination routes is out of scope for this prompt per spec).
feat: Nav IA redesign — 4 workflow sections, Business fold
- Modified
src/components/worldport/Shell.tsx: Replaced the 6-section entity-groupedNAVwith 4 workflow-verb sections: Now / Build / Operate / Settings. Removed "Tools" (Workspace moved to Settings), "Intelligence" (Atlas + Daily Letter → Operate; Revenue, Tenants, Valuation, Marketplace, Analytics → new "Business" sub-group), and "Platform" (Incidents → Operate; Sites & Pages + Research Vault → Build; Blueprints/Documents dropped from sidebar). - Business sub-group: New
BUSINESS_ITEMSconstant rendered as a collapsible group at the bottom of the sidebar, collapsed by default. Toggle usesTrendingUpicon +ChevronDowncaret; expanded items indentml-4. In icon-only mode the toggle still renders as a singleTrendingUpbutton. - Icon fixes: Daily Letter now uses
Newspaper(was a duplicateMailwith Email). Removed unusedFileTextimport. - No new deps, no route changes, no schema changes. Orphan badge on Dashboard preserved.
feat: AOS-MBO-CMP-004 Tool-call activity UI + floating "Ask Kyle" launcher
- New
src/components/agents/ToolCallBadges.tsx: Presentational chip strip. TanStack Query againstagent_tool_callsfor the currentagent_id+session_id, newest first, limit 20. Each chip shows aWrenchicon, thetoolkit(ortool_slugfallback), and a state dot (bg-emerald-500for ok /bg-destructiveotherwise). Empty result renders nothing. Theme tokens only — no hex. - Modified
src/components/agents/AgentChatInterface.tsx: Renders<ToolCallBadges />between the message list and the composer.handleSendnow invalidates both["agent-chat", agentId, sessionId]and["agent-tool-calls", agentId, sessionId]so badges refresh after each turn. Chat logic unchanged. - New
src/components/agents/KyleLauncher.tsx: Fixed bottom-right floating "Ask Kyle" button (Zap icon,bg-primary text-primary-foreground,z-50). Opens a shadcnSheet(right side,sm:max-w-lg) that resolves the workspace viauseWorkspace(), looks up theprovider='composio'agent named "Kyle" for that workspace, and mounts the existing<AgentChatInterface />. Missing-Kyle path shows a friendly "Kyle isn't set up for this workspace yet" message instead of crashing. - Modified
src/components/worldport/Shell.tsx: ImportsKyleLauncherand renders<KyleLauncher />once as a sibling of the main content area, so it appears on every authenticated page./login,/invite/$token,/onboardingdo not mount the Shell and are unaffected. - No new deps: reused shadcn
Sheet(already used byMobileMenuSheet/workspace) and worldport primitives. No new colors —bg-background,bg-muted,text-muted-foreground,text-primary-foreground,bg-emerald-500,bg-destructiveonly.
feat: AOS-MBO-CMP-003 Wire composio provider into sendAgentMessage
- Modified
src/lib/agentChat.functions.ts: Addedcomposiobranch insidesendAgentMessageserver function, before the existingopenai | aria_perplexity | kyle_base44branch. Whenagent.provider === "composio", fetches full chat history fromagent_chat_messages, builds a message array, and calls thekyle-composioedge function server-to-server via service-role bearer at${process.env.SUPABASE_URL}/functions/v1/kyle-composio. Responsecontentbecomes the agent reply; errors surface as[Kyle] tool error: …. Falls through to the existingagent_chat_messagesinsert andlast_active_atupdate — no duplicate tool logging (edge function handlesagent_tool_callsaudit rows). - No signature changes:
requireSupabaseAuth,ChatInputschema, and the agent reply insert path remain untouched.
feat: AOS-MBO-CMP-002 kyle-composio edge function
- New
supabase/functions/kyle-composio/index.ts: Deno edge function (verify_jwt=false; server-to-server with service-role bearer). Initializes Composio withVercelProvider, creates a session for fixedCOMPOSIO_USER_ID = "user_0qev3c", pulls tools, and runsgenerateText(AI SDK) againstanthropic(MODEL)withstopWhen: stepCountIs(10). System prompt is operator-focused / role-greeting (no "Brian" hardcode). Model read fromAGENT_MODELwith defaultclaude-sonnet-4-6— never hardcoded. - Audit trail: Walks
result.steps[].toolCalls+toolResults, derives{ tool_slug, toolkit (prefix-before-underscore lowercased), status, args_summary, error, latency_ms }.args_summaryredacts any key matching/key|secret|token|password/iand truncates long values. One row per tool call inserted intoagent_tool_callsvia the service-role client, tagged withworkspace_id,agent_id,session_id,message_id. - Response:
{ ok: true, content, tools_used: [{ tool_slug, toolkit, status }] }. Errors return{ ok: false, error }(500).finallylogs only{ ts, status, latency_ms, tool_count }— never message contents, args, or secrets. - New
supabase/functions/kyle-composio/deno.json: import map for@composio/core,@composio/vercel,@ai-sdk/anthropic,aivianpm:specifiers (Deno requirement). Packages live ONLY in this function — rootpackage.json/bun.lockuntouched. - Modified
supabase/config.toml: added[functions.kyle-composio] verify_jwt = false. - Env:
ANTHROPIC_API_KEYadded to the project secrets (COMPOSIO_API_KEYalready present).AGENT_MODELoptional.
feat: AOS-MBO-CMP-001 Composio integration — migration + Kyle agent seed
- New migration
supabase/migrations/20260531000023_composio_kyle.sql: Createspublic.agent_tool_callsaudit table (workspace_id → workspaces, agent_id → agents, session_id, message_id → agent_chat_messages SET NULL, tool_slug, toolkit, status default 'ok', args_summary, error, latency_ms, created_at) with indexes on workspace_id / agent_id / session_id. RLS enabled; SELECT viais_workspace_member(workspace_id), INSERT viacan_write_workspace(workspace_id). Grants:SELECTtoauthenticated(members read-only),ALLtoservice_role(backend-only writes per spec —INSERTintentionally NOT granted toauthenticated). - Kyle agent seed: Verified
agents.provideris free-text (no enum ALTER needed). Inserted single rowname='Kyle',provider='composio',api_endpoint=NULL,rolecarries the Composio operator description (agents has nodescriptioncolumn;serialminted by existingtg_serial_agenttrigger →AGT-NWL-20260530-001). Guarded withWHERE NOT EXISTSagainst re-seed. - Outcome: Migration applied cleanly. Seed row confirmed (1 row,
provider='composio',name='Kyle'). No FK toauth.users, noUSING (true), no secrets stored inargs_summary.
feat: BP-MBO-20260527-AGT-008 Smoke + CHANGELOG + LOVABLE_RULES addendum
- New
scripts/smoke-agentos-integration.ts: offline integration smoke (bun run scripts/smoke-agentos-integration.ts). Builds a fakeagentos_connectionsrow, mints a 60s HS256 JWT, hits an in-memory mock AgentOS health endpoint, runsdiffPlanagainst matched spec/live to assert a no-op (operations.length === 0), and round-trips anexusospayload throughsanitizeOutboundasserting it becomes[redacted:forbidden-token]withredacted === ["nexusos"]. Prints PASS/FAIL and exits non-zero on failure. Not wired into CI per LOVABLE_RULES Forbidden Actions — runs locally before pack approval. - Modified
LOVABLE_RULES.md: appended the permanent### AgentOS Integration (added by BP-MBO-20260527-AGT-008)section verbatim from the pack prompt. Pinned: AgentOS repo/branch/manifest, server-only call path, seat-secret location, sanitizer enforcement, plan/role/trigger enums, inbound-event HMAC contract, locked deployment-op ordering, and theretire_agentno-auto-rollback rule. - New
docs/AGENTOS-INTEGRATION-CONTRACT.md: operator-facing contract — how to add a connection, the 32 AgentOS routes MBO uses, the 7 inbound event types and their counter side effects, the sanitizer rules, and the smoke command. - Pack summary (BP-MBO-20260527 AgentOS spoke, AGT-001 → AGT-008): AGT-001 manifest pin +
agentos_connections/agentos_event_subscriptionsschema; AGT-002 server-side HTTP client + JWT minting + sanitizer + Vitest coverage; AGT-003/tenantsconnection management; AGT-004 fleet templates list + form-driven editor with version locking; AGT-005 deployment engine (dry-run, ordered apply with retries, reversible-only rollback); AGT-006 inboundagentos-eventsedge function + observability counters + drift flag; AGT-007 read-only observability widgets on tenant + agent detail views; AGT-008 smoke + docs + rules addendum.
feat: BP-MBO-20260527-AGT-007 AgentOS observability widgets
- New hooks
src/lib/agentos-observability-queries.ts:useDispatchVolume,useQuotaBurnDown,useInboundEvents,useDriftStatus— read-only TanStack Query hooks over AGT-006's counters/events, workspace-scoped via RLS, zero-filled day buckets so charts render without layout shift on empty windows. - New server fn
src/lib/agentosQuota.functions.ts:fetchAgentosQuotaStateproxies liveclient.quotaState()so the seat secret never reaches the browser; returnsnullwhen the connection is unverified or AgentOS is unreachable. Polled every 60s viarefetchInterval. - New widgets under
src/components/agents/:DispatchVolumeChart(recharts line, completed vs failed, last 30d),QuotaBurnDownChart(live limits/usage chips + warnings area),DriftBanner(session-dismissible amber banner driven byfleet_deployments.drift_suspected_at),InboundEventsFeed(last 25 events with redacted/handler-error chips). - Modified
src/routes/tenants.$tenantId.tsx: verified connections now render an "Observability" section below the fleet deployment history (drift banner + two-up charts + events feed). - Modified
src/routes/agents.$agentId.tsx: added an "AgentOS" tab showing the same observability stack scoped to the workspace's most recently verified tenant connection.
feat: BP-MBO-20260527-AGT-006 Inbound AgentOS events + observability counters
- New migration:
agentos_events(serial prefixAEV, unique(connection_id, event_id)for dedupe, workspace-scoped SELECT RLS) andagentos_observability_counters(per-day per-metric, unique(connection_id, bucket, metric)). Addedfleet_deployments.drift_suspected_atcolumn. New SECURITY DEFINER helperagentos_bump_counter(workspace_id, connection_id, metric, delta)restricted toservice_rolefor atomic counter upserts. - New edge function
supabase/functions/agentos-events/: HMAC-SHA256 verification againstAGENTOS_INBOUND_HMAC(sentry-webhook shape),x-agentos-connection-idheader lookup, inline payload sanitizer (forbidden tokensnexusos/preserver→[redacted:forbidden-token], captured inredacted[]). Dispatches to inline handlers fordispatch.completed,dispatch.failed,memory.updated,quota.warning(also writeslast_erroron connection),seat.invited/seat.accepted(no-op v1),fleet.config.changed(flipsdrift_suspected_aton most recent applied deployment). Handler errors stored onagentos_events.handler_errorbut never reject; idempotent on dedupe. - New frontend registry under
src/integrations/agentos/events/:router.tsexportsAGENTOS_EVENT_HANDLERS+AGENTOS_HANDLER_BY_TYPE(event-type → metric descriptor) consumed by AGT-007 widgets. Per-event descriptor files underhandlers/.sanitize-inbound.tsre-exports the AGT-002 sanitizer for parity with the edge function. - Modified
supabase/config.toml: registered[functions.agentos-events]withverify_jwt = false(HMAC, not JWT). - Secret required:
AGENTOS_INBOUND_HMAC(shared with NetworkOS — never logged).
feat: BP-MBO-20260527-AGT-005 Deployment engine (dry-run, apply, rollback)
- New migration:
fleet_deployments+fleet_deployment_operations(workspace-scoped RLS viais_workspace_member/can_write_workspace, serial prefixesFDP/FDO, indexes on(connection_id, created_at DESC)and(deployment_id, ordinal)). Names deviate from the prompt'sdeploymentsto avoid colliding with the pre-existing site deployments table. - New server fns under
src/lib/deployments/(kept out ofsrc/server/because TanStack Start import-protection blockssrc/server/**from client bundles):plan.ts— purediffPlan(spec, live)returning orderedPlannedOperation[]in the locked priority:upgrade_plan→enable_channel→install_tool→register/update/retire_agent→set_memory_policy. ExportsREVERSIBILITYmap (retire_agent is the only irreversible op).apply.functions.ts—planDeployment(dry-run, persists nothing) andapplyDeployment(idempotent: reuses existingappliedrow, rejectsapplying). Executes ops in order with 3-retry exponential backoff (200/600/1800ms), on failure auto-reverts succeeded reversible ops.rollback.functions.ts—previewRollbacklists succeeded ops in reverse with reversibility flag;applyRollbackcreates a new deployment row withrollback_ofpointing to the original and reverts each reversible op via the AGT-002 client.
- New components under
src/components/deployments/:DeployModal.tsx— three phases (select template+version → dry-run preview → confirm/apply). Lists only published templates and published versions. Preview shows op count, warnings, and per-op reversible/irreversible chip.DeploymentHistoryTable.tsx— last 25 deployments for a connection with state chip and Rollback button onappliedrows.RollbackModal.tsx— irreversible ops rendered with red border + tooltip explaining AgentOS retention may have wiped state.
- Modified:
src/routes/tenants.$tenantId.tsx— verified connections now render a "Fleet deployments" card with Deploy CTA, history table, and rollback flow.
feat: BP-MBO-20260527-AGT-003 Connect-tenant flow on /tenants/:tenantId
- New migration:
agentos_connection_health(append-only) — columnsconnection_id,workspace_id,checked_at,ok,latency_ms,raw. RLS: workspace members read, workspace writers insert; no update/delete policies. Indexidx_agentos_health_conn(connection_id, checked_at DESC). Grants toauthenticatedandservice_role. - New server fns (
src/lib/agentosConnection.functions.ts):createAgentosConnection— inserts pending row, derivesseat_secret_ref = AGENTOS_SEAT_<id>. Secret value is NEVER persisted to Supabase; operator adds it to Lovable Cloud secrets under that name.verifyAgentosConnection— mints JWT, hitsGET /api/blocks/agentos/health, updatesstatus+last_verified_at/last_error, appends row toagentos_connection_health. Never logs secret or JWT.revokeAgentosConnection— flips status torevoked.- (Located in
src/lib/rather thansrc/server/because TanStack Start import-protection blocks client imports fromsrc/server/**.)
- New components:
src/components/tenants/AgentosConnectionPanel.tsx— three-field form (tenant UUID defaulted from URL, API origin defaulting tohttps://api.worldport.dev, automation seat secret). On submit the secret is cleared from memory and the operator is shown the exact Cloudflare secret NAME to add, then can click "Verify connection".src/components/tenants/AgentosHealthCard.tsx— status pill, last verified, last error tag, last-10 ping sparkbar, Re-verify and Revoke buttons.
- Modified:
src/routes/tenants.$tenantId.tsx— appends an "AgentOS Fleet" section below the existing tabs that swaps panel ↔ health card based on(workspace_id, target_tenant_id)connection presence.
feat: BP-MBO-20260527-AGT-002 AgentOS client + sanitizer + JWT minting
src/integrations/agentos/types.ts: contract types mirroring AgentOS manifestfb095c5b—AgentOsPlan(observer/companion/pro/business/command),AgentOsRole(owner/admin/member/automation),AgentSpec,SeatInvite,ToolInstall,QuotaState,DashboardStats,MemoryPolicy,AgentosConnectionRow,CloudflareEnv, and the strictAgentOsClientinterface with the 18 verbatim method signatures.src/integrations/agentos/errors.ts:AgentOsAuthError(401/403),AgentOsQuotaError(429 w/retryAfterms),AgentOsTransientError(5xx/network).src/integrations/agentos/sanitize.ts: recursive walker,FORBIDDEN_TOKENS = ["nexusos", "preserver"], case-insensitive replace with[redacted:forbidden-token]. Returns{ clean, redacted }for both directions — sanitize and continue, never fail.src/integrations/agentos/jwt.ts:mintAutomationJwt({ connection, env })— HS256 via Web Crypto, 60s TTL, reads secret fromenv[connection.seat_secret_ref]at call time. Never logs secret or JWT body. Runs only in the Cloudflare Worker.src/integrations/agentos/client.ts:createAgentOsClient(args): AgentOsClient(strict) pluscreateAgentOsClientWithMeta(args)(returns{ data, redactedOutbound, redactedInbound }so callers can log toagentos_eventsin AGT-006). Maps HTTP status → typed errors; outbound bodies sanitized before serialize, inbound bodies sanitized before return.src/integrations/agentos/sanitize.test.ts: vitest spec covering case-insensitive redaction, nested walking, clean payloads, and primitive handling. (@ts-nochecksince vitest types aren't installed; the lovable vite plugin runs the file.)
feat: BP-MBO-20260527-AGT-001 AgentOS connection schema + registry seed
integrations_registry: seededagentosrow (nameAgentOS, categoryagent-fleet, default healthunknown) — idempotent viaON CONFLICT (key) DO NOTHING.- New table
public.agentos_connections: workspace-scoped record of per-(workspace × target tenant) AgentOS connection state — columns includetarget_tenant_id/target_tenant_label,api_origin,automation_seat_id,seat_secret_ref(Cloudflare Worker secret NAME only — no plaintext),signing_key_id,status(pending|verified|failed|revoked),last_verified_at,last_error,app_tagdefault'@MBO'. Unique on(workspace_id, target_tenant_id). No FK ontarget_tenant_id(cross-DB). - Indexes:
idx_agentos_conn_ws (workspace_id, status),idx_agentos_conn_site (site_id). - Serial trigger:
tg_serial_agtconnmintsAGTprefix viambo_generate_serial(new prefix, no collision with SITE/PROMPT/BP/BC/INC/TSK/CTX/DOC/DL/PACK/PAGE). - RLS: enabled with four policies (
select/insert/update/delete) usingpublic.is_workspace_member/public.can_write_workspace. Workspace B cannot see Workspace A's rows. - GRANTs:
SELECT,INSERT,UPDATE,DELETEtoauthenticated,ALLtoservice_role.
feat: NXS-CLEAN-003 Doc/asset sweep + annotate historical pack
docs/prompt-packs/BP-MBO-PACK-001-LOVABLE.md: added editor's note immediately under the H1 flagging the NexusOS → NetworkOS decommission and pointing future work to NetworkOS /packages/network-os. Body left verbatim as historical record per the read-only-historical-pack convention.public/LovableBuildSequence+public/WORLDPORT_DESIGN_TOKENS.css: no changes needed — both files were already swept clean in NXS-CLEAN-001 (the seed-data line and the design-system header comment both already read NetworkOS).- Validation grep (
nexusos|NexusOSexcluding LOVABLE_RULES.md, historical pack, 2026-05-2x migrations) returns empty.
feat: NXS-CLEAN-002 Forward-migrate NexusOS seed rows to NetworkOS
- DB migration (forward only): updates
public.sitesrows wherecode='NXS' AND name='NexusOS'— setsname='NetworkOS',description='Hub-and-spoke event fabric (in worldport-platform/packages/network-os)',repo_url='https://github.com/teamsilverine/worldport-platform/tree/main/packages/network-os'. Idempotent; re-running is a no-op. - Code column untouched:
@NXSretained as airport-tag per locked decision. - Scope: historical seed migrations 20260521000817/012847/021203/021941/022414 left intact. All literal
'NexusOS'matches in those files were sites-table rows, so a single UPDATE covers every workspace's seed row — no additional table updates needed. - Verified:
SELECT code, name, repo_url FROM public.sites WHERE code='NXS'returnsNXS | NetworkOS | https://github.com/teamsilverine/worldport-platform/tree/main/packages/network-os.
feat: NXS-CLEAN-001 Relabel NexusOS → NetworkOS in rules + onboarding
- LOVABLE_RULES.md: replaced the NexusOS row in the three-apps table with the corrected NetworkOS framing (lives in
teamsilverine/worldport-platformatpackages/network-os, accessed via AgentOS spoke). Added new "Decommissioned terms" section codifying that@NXSstays as a tag, NetworkOS is the display name, and the word "NexusOS" must not be reintroduced. Airport-code line on line 75 left intact per locked decision. - Files modified:
src/routes/onboarding.tsx(seed-sites copy now lists NetworkOS),public/WORLDPORT_DESIGN_TOKENS.cssheader comment,public/LovableBuildSequenceseed-data line — all stale "NexusOS" display-name references replaced. - Out of scope: the
nxs_sitesseed row rename (DB migration in NXS-CLEAN-002) and historical migration files (left untouched).
feat: PROMPT-MBO-WORKSPACE-002 — Add password_ref integer field to workspace_accounts
- DB migration: added optional
password_ref integercolumn with comment clarifying no passwords are stored — just a numeric cross-reference to an offline list. - Files modified:
src/routes/workspace.tsx— addedPassword Ref #numeric input next to Username in the edit slide-over (integer-only, optional, with tooltip), and account card now shows a small lock icon + ref number under the login email when set.
feat: PROMPT-MBO-WORKSPACE-001 — Workspace Accounts Registry
- DB migration: new
workspace_accountstable (workspace_id scoped, RLS viais_workspace_member/can_write_workspace,ACC-serial trigger). Seeded ~28 accounts (GitHub, Supabase, Lovable, Vercel, Cloudflare, Stripe, LawPay, DocuSign, Twilio, Resend, Google Workspace, Notion, etc.) for every existing workspace. - Files created:
src/routes/workspace.tsx—/workspaceroute with realtime search, category filter chips, favorites strip, recently-visited horizontal strip, 3/2/1-col responsive grid, Google favicon + first-letter fallback, integration status badge, site code @pills, slide-over Add/Edit sheet with full field set, archive + favorite toggles, visit tracking on Open. - Files modified:
src/components/worldport/Shell.tsxandsrc/components/mobile/MobileMenuSheet.tsx— new "Tools" nav section with Workspace entry (LayoutGrid icon). - Note: Implemented with workspace-scoped RLS instead of the prompt's
auth.role() = 'authenticated'policy, per LOVABLE_RULES (every new table must be workspace-scoped). MeterOS emit skipped — nonetworkosmodule exists in this repo.
feat: Combine voice notes into a single AI-synthesized thought
- DB migration: added
archived_at(timestamptz) andmerged_into(uuid self-FK ON DELETE SET NULL) tocontext_pointers. New indexes formerged_intoand active-by-workspace queries. Context Map query now filtersarchived_at IS NULL. - Files created:
src/lib/voiceNotes.functions.ts— two server fns:synthesizeVoiceNotes(Lovable AI Gateway, google/gemini-2.5-flash, strict-JSON system prompt that dedupes + orders without inventing facts; fallback to timestamped concat) andsaveCombinedVoiceNote(inserts new voice_note, archives sources withmerged_intolink). - Files modified:
src/routes/context-map.tsx— "Combine voice notes" toggle in header, checkbox overlay on cards (only voice_note kind selectable), floating action bar showing selection count, modal that previews AI synthesis with editable title/body and source list before commit.
feat: VAULT Overview field + drag-and-drop document upload on New vault entry
- DB migration: added
overview(text) andfile_name(text) columns toresearch_vault. Existingfile_uricolumn now wired through the modal. - Files modified:
src/components/research-vault/VaultEntryModal.tsx— added Overview textarea below Title and an Attachment dropzone that accepts PDF/MD/TXT/DOCX/CSV/JSON up to 25MB. Uploads go to thembo-documentsbucket under{user_id}/vault/{timestamp}-{name}. Drag-and-drop + click-to-browse, with upload progress, remove action, and existing-file display when editing.
BP-MBO-20260525-B10-001 — Full Idea-to-Production Loop: 8-state machine, DB-enforced transitions, audit trail
- Context: Item 28/28 (B10). Final item in the prompt pack. Ships the cross-cutting
loopsstate machine that ties capture/research/plan/build/review/deploy/verify/lock into a single object. - DB migration: new workspace-scoped
loops(serialLOOP, state CHECK across 8 values, FKs → sites/prompts/github_pull_requests/deployments, locked_at/locked_by, loop_duration_minutes).loop_state_transitionsaudit table (from_state, to_state, actor_kind, notes). Nullableloop_idcolumns added toprompts,github_pull_requests,deployments. SECURITY DEFINERloop_transition(_loop_id, _to_state, _notes, _actor_kind)RPC validates the legal-transition graph (capture→{research,plan}, research→plan, plan→build, build→review, review→{deploy,build}, deploy→verify, verify→{lock,build}, lock→∅), refuses writes on locked loops, writes the audit row, computesloop_duration_minuteson lock. AFTER-INSERT trigger seeds the initial transition row. RLS viais_workspace_member/can_write_workspacewithlocked_at IS NULLguard on update/delete. - Files created:
src/lib/loopStates.ts(canonical LOOP_STATES + LEGAL_NEXT + STATE_TONE — single source of truth).src/components/loops/StateMachineVisualizer.tsx(8-dot ProgressStepper + horizontal step-node visualizer with past=success, current=primary ring, future=muted).src/routes/loops.tsx(layout Outlet).src/routes/loops.index.tsx(filterable list with state-count chips, inline +New Loop creator, LoopCard with ProgressStepper + site chip + duration).src/routes/loops.$loopId.tsx(detail view with StateMachineVisualizer, history timeline of transitions with from→to + notes + actor_kind, Advance panel showing only legal next-states with optional notes textarea, errorComponent + notFoundComponent per TanStack discipline). - Files modified:
src/components/worldport/Shell.tsx(added Workflow icon + Loops nav item under Operations). - Progress: 28 of 28 shipped. 🎉 PROMPT PACK COMPLETE.
BP-MBO-20260525-B4-001 — Atlas Auto-Surface: prioritized workspace signal feed
- Context: Item 27/28 (B4). Introduces
/atlasas the operator's "what needs my attention right now" view, aggregating signals from across MBO into one ranked feed. - DB migration: new workspace-scoped
atlas_signals(serialATL, kind/severity/title/summary/route/source_table/source_id/score/metadata jsonb, dismissed_at). RLS viais_workspace_member/can_write_workspace. Newcompute_atlas_signals(_workspace_id)SECURITY DEFINER RPC wipes undismissed rows and re-derives: overdue tasks (score 80+), open incidents (score 95), high/critical-churn tenants (score 70 + risk gap), blocks shipped in last 7d (40), research vault entries in last 3d (30). - Route
/atlas: severity rollup (critical/warning/info), grouped sections by kind with icons (Clock/AlertTriangle/TrendingDown/Package/Sparkles), per-signal Card with score chip and one-click dismiss, deep-link to source route. Recompute button calls the RPC and invalidates the query. - Nav: Atlas pinned to top of Intelligence section in
Shell.tsx. - Files: new
src/routes/atlas.tsx, new migration, editedShell.tsx,CHANGELOG.md, regeneratedtypes.ts. - Progress: 27 of 28 items complete.
BP-MBO-20260524-E9-001 — Revenue Intelligence consolidation: per-block detail + projections + formatCents
- Context: Item 26/28 (E9). Most deliverables already shipped in Items 22–25 (
/revenue,/tenants,/marketplace,/valuation,blocks_catalog,block_installations,comp_multiples,valuation_snapshots,revenue_snapshots,compute_valuationRPC replacing the spec'svaluation-snapshotedge function per the stack's TanStack-server-fn policy). This patch adds the remaining gaps: forward projections table, per-block revenue route, and the canonical cents formatter. - DB migration: new workspace-scoped
revenue_projections(block_id FK→blocks_catalog CASCADE, projection_date, projected_mrr_cents, projected_tenants, assumptions jsonb, UNIQUE(workspace_id, block_id, projection_date)). RLS viais_workspace_member/can_write_workspace+ service_role bypass; idx_revproj_ws_block_date for range queries. Seed: 12 months of forward projections per active block per workspace using 5% MoM compound growth from current contributed MRR. - Files created:
src/lib/formatCents.ts(canonical integer-cents → USD utility withcompactoption for K/M abbreviation; division by 100 happens only at display).src/routes/revenue.$blockId.tsx(block detail page: header with category/layer/wave/status/quality chips and back link to/revenue; 4-stat row MRR/ARR/Tenants/Installations; 12-month trailing MRR area chart; full installations table with tier, status chip, MRR, tenant link to/tenants/$tenantId, health score, churn-risk chip color-toned by threshold; 12-month projection LineChart fromrevenue_projections). errorComponent + notFoundComponent on the route per TanStack discipline. - Files modified: none beyond CHANGELOG (route is auto-registered by TanStack file-based routing; types regen via Supabase integration).
- Progress: 26 of 28 shipped.
BP-MBO-20260525-D4-001 — Valuation Model: live ARR × multiple, what-if levers, daily snapshots, investor view
- DB migration: new workspace-scoped
sensitivity_parameters(paramname/value/notes UNIQUE per workspace, seeded 8 rows: platform_premium_conservative|base|aggressive, adoption_dampener*, multiple_band 0.30, default_multiple 5.0).valuation_snapshots(snapshot_date UNIQUE per workspace, total_arr_cents, conservative|base|aggressive cents, assumptions jsonb).valuation_levers(lever_type CHECK add_block|add_feature|add_tenant, block_slug, feature_name, lever_label, three delta_cents columns). RLS viais_workspace_member/can_write_workspaceon all three. SECURITY DEFINERcompute_valuation(workspace_id)walksblock_installations, joinscomp_multiples(avg p50, falls back to default_multiple), applies adoption_factor = tenants/(tenants+dampener) per scenario, multiplies by platform premium, upserts today's snapshot, then refreshes +block levers (up to 10 uninstalled blocks at 10 initial tenants × $200/mo) and +tenant lever (avg ARPU × default_multiple × premium). Seed: 30 days of synthetic trend snapshots per workspace with gentle upward jitter. - Files created:
src/routes/valuation.tsx(three RangeCards Conservative|Base|Aggressive with assumption sub-text; meta strip with ARR + 30-day Δ% + sensitivity band; Recharts AreaChart showing Conservative dashed line + Base solid filled + Aggressive shaded band; +Block top-5 leverboard sorted by base delta with per-scenario range; +Tenant single card; Recompute button callscompute_valuationRPC; link to Investor View).src/routes/investor.tsx(full-width no-sidebar layout, hero with Platform ARR + 30-day sparkline, 3 KPIs Live Blocks/Active Tenants/Avg ARPU, 3-up valuation range with base accented, comp benchmarks table joiningcomp_multiples+blocks_catalog, highest-impact next move card, Print/Export PDF button viawindow.print()). - Files modified:
src/components/worldport/Shell.tsx(added TrendingUp icon import + Valuation nav item under Intelligence). - Progress: 25 of 28 shipped.
BP-MBO-20260525-D3-001 — Tenant Management: per-tenant view, churn signals, upgrade history
- DB migration: new workspace-scoped
tenants(serialTEN, status trial|active|past_due|paused|churned, MRR/ARR cents, health_score, churn_risk 0–100, tenure_months, contact info, stripe_customer_id, primary_site_id FK→sites).tenant_events(serialTEVT, event_type install|upgrade|downgrade|churn|recovery|payment.*|note|contact|churn_risk.change, mrr_delta_cents, metadata jsonb).tenant_churn_signals(8 weighted signals per tenant, UNIQUE(tenant_id,signal_key)). Added nullabletenant_idFK onblock_installations. Triggerstg_serial_tenant/tg_serial_tenant_eventviambo_generate_serial. RLS viais_workspace_member/can_write_workspaceon all three tables. SECURITY DEFINER functioncompute_tenant_churn(_tenant_id)recomputes 8 signals (declining_usage, open_tickets, payment_retries, block_downgrade, low_login, feature_stall, sentiment, renewal_close) using existing data (incidents, tenant_events, last_activity_at) and updates churn_risk + health_score. Seed: 4 sample tenants per workspace (Northwind, Atlas, Meridian, Summit) with onboarding + payment events and computed health. - Files created:
src/routes/tenants.tsx(layout Outlet).src/routes/tenants.index.tsx(4-stat header for count/MRR/ARR/at-risk, search + status filter + sort by MRR|risk|name, table with health & churn-risk chips color-coded).src/routes/tenants.$tenantId.tsx(header with status chip, MRR/ARR/Health/Churn metrics color-accented, click-to-email/tel/Stripe links; 4 tabs Overview/Blocks/Health/History; Overview shows top active risk signals + recent activity; Blocks lists installed blocks via FK join; Health shows all 8 signals with raw_metric JSON, Recompute button callscompute_tenant_churnRPC; History is reverse-chronotenant_eventswith Δ MRR). - Files modified:
src/components/worldport/Shell.tsx(added Tenants nav item under Intelligence using Users icon). - Progress: 24 of 28 shipped.
BP-MBO-20260525-D2-001 — Block Marketplace: catalog, dependency graph, one-click install
- DB migration: new global tables
block_categories(slug PK, name, icon, sort_order),blocks_catalog(slug UNIQUE, name, category_slug, layer, wave, status CHECK live|beta|building|planned|deprecated, quality_tier CHECK experimental|standard|enterprise, short/long_description, icon, is_marketplace_listed default true),block_pricing_tiers(block_id FK CASCADE, tier_name CHECK Free|Starter|Growth|Pro|Enterprise, monthly_cents, features jsonb, sort_order, UNIQUE(block_id,tier_name)),block_dependencies(block_id+depends_on_block_id FKs CASCADE, kind CHECK required|recommended, UNIQUE),comp_multiples(comp_name, comp_arr_cents, arr_multiple_p50). Workspace-scopedblock_installations(workspace_id FK→workspaces CASCADE, block_id FK CASCADE, serial, tier_name, status CHECK active|paused|uninstalled|pending_payment, monthly_cents, installed_at/by, UNIQUE(workspace_id,block_id)) withtg_serial_block_installationmintingBI-YYYYMMDD-NNN. RLS: global tables readable by authenticated; installations viais_workspace_member(select),can_write_workspace(write), service_role bypass. Seed: 12 categories + 8 blocks (RouteOS, GeocodeOS, ImportOS, DocketForge, DocsOS, EsignOS, LedgerOS, PayOS) with 3 pricing tiers each and required/recommended dependencies + comp rows. - Files created:
src/routes/marketplace.tsx(layout Outlet).src/routes/marketplace.index.tsx(sidebar with search input + Category/Status filter lists; sort dropdown name/price/status; responsive grid of BlockCards showing icon, name, category·layer·wave, status/quality/tier chips, Install button using cheapest tier or Installed state, Details button).src/routes/marketplace.$blockSlug.tsx(hero with status/quality/category/layer/wave chips + installed indicator, missing-required-deps warning card with co-install list, 3-column pricing tier table with feature checklists, dependencies list with installed indicators, optional comparable-companies table). Install mutation co-installs missing required deps at their Starter tier in a single insert. - Files modified:
src/components/worldport/Shell.tsx(added Package icon + Marketplace nav item under Intelligence). - Progress: 23 of 28 shipped.
BP-MBO-20260525-D1-001 — Revenue Intelligence Dashboard: MRR/ARR trends, tenant deltas, revenue event log
- DB migration: extended
revenue_snapshotswithnew_tenants,churned_tenants,per_site jsonb(default{}). Newrevenue_eventstable (workspace_id FK→workspaces ON DELETE CASCADE, site_id FK→sites ON DELETE SET NULL, serial, event_type CHECK innew|expansion|contraction|churn|refund, tenant_name, amount_cents bigint, occurred_at, notes). Indexidx_rev_events_ws_timeon (workspace_id, occurred_at DESC). RLS enabled withre_select/re_write/re_svc_allviais_workspace_member/can_write_workspace. BEFORE INSERT triggertg_serial_revenue_eventmintsREV-YYYYMMDD-NNNviambo_generate_serial. Seeded 30 days of synthetic snapshots and 5 sample events per workspace that had none. - Files created:
src/routes/revenue.tsx(90-day query ofrevenue_snapshots+ 20 latestrevenue_events, KPI strip viaFinancialStrip, 4-up delta cards for MRR Δ 7d / new 30d / churn 30d / open claims, three area charts for MRR/Tenants/ARR, event list).src/components/revenue/RevenueTrendChart.tsx(recharts AreaChart with gradient fill, token-driven colors viareadChartColors).src/components/revenue/RevenueEventList.tsx(icon+tone per event type, +/- amount color, relative time). - Files modified:
src/components/worldport/Shell.tsx(addedDollarSignRevenue nav item under Intelligence). - Progress: 22 of 28 shipped.
BP-MBO-20260524-B2-001 — In-Workspace Browser: embedded live preview of each managed app inside Site Detail
- DB migration: extended
siteswithpreview_url,embed_blocked(bool default false),embed_last_tested,embed_last_screenshot_at,embed_notes. Newbrowser_sessions(workspace_id, site_id, serial BSESS-prefixed UNIQUE, url_used, started_at, ended_at, embed_method iframe|proxy|screenshot|blocked, notes) with BEFORE INSERT serial triggertg_serial_browser_sessioncallingmbo_generate_serial('BSESS', site.code). RLS: bs_select viais_workspace_member, bs_insert viacan_write_workspace, bs_svc_all for service_role; standard grants. - Files created:
src/lib/embedProbe.functions.ts(TanStack serverFnprobeEmbedwithrequireSupabaseAuth+ zod input {site_id, url, workspace_id}; 5s-timeout HEAD fetch via AbortController, parsesX-Frame-OptionsDENY|SAMEORIGIN and CSPframe-ancestors 'none'|'self'to decide embeddability; updatessites.embed_blocked|embed_last_tested|embed_notesscoped by workspace_id; returns {embeddable, reason}).src/components/sites/SitePreviewPanel.tsx(desktop-only viahidden lg:block; toolbar with Preview/Live URL toggle gated on which URLs exist, RefreshCcw to re-key the iframe, Re-probe button, ShieldAlert status chip, "Open externally" anchor, current-URL display, last-probed timestamp + reason; auto-probes whenembed_last_testedis null or >24h old; on first render of a non-blocked URL inserts abrowser_sessionsrow withembed_method='iframe'; iframe usessandbox="allow-scripts allow-same-origin allow-forms allow-popups",referrerPolicy="no-referrer", full-height; blocked-state card with reason + new-tab CTA + re-probe; empty-state when no URL). - Files modified:
src/components/sites/SiteTabs.tsx(addedpreviewto SiteTabKey union and inserted "Preview" tab between Pipeline and Design Bridge).src/routes/sites.$siteId.tsx(imported SitePreviewPanel, renders whentab === 'preview').src/components/sites/SiteHeader.tsx— already exposes alive_urlexternal-link icon, spec satisfied without change. - Deviation from spec:
supabase/functions/embed-probe/index.tsDeno edge function reimplemented as a TanStack server function per project rule "Do NOT use Supabase Edge Functions for app-internal logic". HEAD-fetch + header parsing semantics, 5s timeout, and update fields all preserved. No new npm packages installed. Noallow-top-navigationin iframe sandbox. Mobile (< lg) hides the panel entirely. - Progress: 20 of 28 shipped.
BP-MBO-20260524-B6-001 — Complete Dashboard (PandaOS parity): Today's Focus + 14-day Velocity strip
- Files created:
src/components/dashboard/TodayFocusCard.tsx(critical-path roll-up — fetches open SEV1/SEV2 incidents, ready P0/P1 prompts, and overdue tasks (due_at < now, status not in done/archived/cancelled) in one parallel batch; merges & ranks SEV1→P0→P1→overdue; renders tone-coded chip + serial + title rows with deep-link to /incidents|/prompt-queue|/tasks; "Clear runway" empty state when all green).src/components/dashboard/VelocitySparklineStrip.tsx(14-day per-day series for prompts shipped [status='shipped', completed_at], incidents opened [created_at], tasks completed [completed_at], captures [context_pointers.created_at]; inline 120×32 SVG sparkline component, totals + today count per metric, 2×4 grid, 60s stale). - Files modified:
src/routes/index.tsx(mounted<TodayFocusCard />and<VelocitySparklineStrip />above the existing FreshnessWidget; both reuse the dashboardworkspaceIdand refresh on the existing realtime channel). - Progress: 19 of 28 shipped.
BP-MBO-20260524-B7-001 — Scheduled Autonomous Operations: cron registry, approval flow, failure alerts
- DB migration: new
scheduled_operations(workspace_id, site_id, serial OPS-prefixed, name, description, operation_type kyle_session|aria_research|prompt_fire|webhook_call|edge_fn|sql_query, operation_config jsonb, schedule_type cron|interval, cron_expression, interval_minutes ≥5, status draft|review|active|paused|archived, next_run_at, last_run_at, run/success/failure counters, alert_on_failure, created_by/submitted_by/_at/approved_by/_at) with BEFORE INSERT serial triggertg_serial_sched_ops+ updated_at trigger + partial index on (status,next_run_at) WHERE status='active'; newscheduled_operation_runs(operation_id FK CASCADE, serial RUN-prefixed, status running|success|failed, started/completed/duration_ms, error_message, response jsonb, optional incident_id FK) with serial trigger; standard ws_read/ws_write/svc_all RLS via is_workspace_member + can_write_workspace (run rows are svc-write only — only the tick writes). - Files created: src/routes/api/public/scheduler-tick.ts (TSS public route invoked by pg_cron via pg_net every minute; selects up to 20 due active ops
next_run_at <= now(), inserts run row status=running, dispatches per type — prompt_fire updatesprompts.status='ready'; webhook_call fetches with configured method/headers/body; kyle_session/aria_research/edge_fn currently stubbed responses — and on completion updates run with status/duration/error/response, opens SEV2 incident ifalert_on_failureand failed, then updates oplast_run_at/next_run_at/run-counters; cron parser handles*/N * * * *,0 */N * * *,M H * * *, falls back to +60min). src/lib/scheduler.functions.ts (TanStack serverFnscreateScheduledOperation— rejects sql_query, inserts status=draft, returns id+serial;schedulerDecide— submit (draft→review by any member), activate (review|paused→active, gated owner/admin, computes next_run_at), pause (gated, active→paused, clears next_run_at), archive (gated); writes mbo_audit_logscheduler.{action}). src/components/scheduler/SchedulerPage.tsx (filter chips All/Draft/Review/Active/Paused/Archived, op cards with status chip, serial, type, human schedule label, relative next/last run, counters, action buttons Submit/Activate/Pause/Archive role-gated). src/components/scheduler/OperationCreateModal.tsx (single-screen wizard: name/description, operation type select, per-type config — prompt picker for prompt_fire, URL/method/body for webhook_call, fn name/payload for edge_fn, prompt/query for kyle/aria; schedule = cron preset list (every 5/15/60m, daily 08:00/17:00 UTC) + custom cron + livedescribeCronlabel, or fixed interval ≥5m; alert_on_failure toggle; inserts as draft via serverFn). src/routes/scheduler.tsx (route shell w/ meta). - Files modified: src/components/worldport/Shell.tsx (added Scheduler nav item under Operations with Clock icon).
- Deviation from spec: edge functions
scheduler-tick/scheduler-approvereimplemented as TSS public route + TanStack serverFn per project rule "Do NOT use Supabase Edge Functions". sql_query operation type is registered in the enum but blocked at creation. To activate scheduling, run in SQL:CREATE EXTENSION IF NOT EXISTS pg_cron; CREATE EXTENSION IF NOT EXISTS pg_net; SELECT cron.schedule('scheduler-tick','* * * * *',$$SELECT net.http_post(url:='https://worldport-mbo.lovable.app/api/public/scheduler-tick', headers:=jsonb_build_object('apikey','<anon>','content-type','application/json'), body:='{}'::jsonb);$$); - Progress: 18 of 28 shipped.
BP-MBO-20260524-B8-001 — PR/Deployment Pipeline: GitHub webhook, commit↔serial linking, deploy health check
- DB migration: new
github_pull_requests(workspace_id, site_id, serial PR-prefixed, github_repo, github_pr_number UNIQUE(repo, number), title, body, state open|closed|merged, author, head_branch, base_branch, checks_status, linked_mbo_serial, linked_entity_type prompt|task|incident|plan|deployment, opened_at/merged_at/closed_at, mbo_approved_by/_at) with BEFORE INSERT serial triggertg_serial_prusingmbo_generate_serial('PR', site.code), updated_at trigger, standard ws_read/ws_write/svc_all RLS via is_workspace_member + can_write_workspace. Extendeddeploymentswith pr_id (FK), pr_number, branch, deploy_url, environment (default 'production'), health_check_status healthy|degraded|down|unknown, health_check_score 0-100, health_check_at, started_at, duration_ms, incident_id (FK incidents), external_id with UNIQUE partial index. - Files created: src/routes/api/public/github-webhook.ts (TSS public route; HMAC-SHA256 verification against
GITHUB_WEBHOOK_SECRETviax-hub-signature-256with timingSafeEqual; routes byx-github-eventheader —pull_request→ upserts row matched to site by repo_url ILIKE, regex-extracts MBO serial/(?:PROMPT|TASK|TSK|INC|PLAN|DEPLOY|DEP|PR|BP|SITE)-[A-Z0-9]{2,8}-\d{8}-\d{3,4}/gfrom title+body, maps prefix → linked_entity_type, marks linked prompts shipped on merge;deployment_status→ upserts deployment, on failure inserts SEV2 incident + links via incident_id, on success runs inline health check (10s timeout fetch, scores 100 if <3s / 70 if <5s / 50 if slow / 0 if non-200|timeout), updates sites.health_score/health_status/last_deploy/last_health_check_at, opens SEV2 incident if score<50; always returns 200 so GitHub stops retrying). src/lib/pipeline.functions.ts (TanStack serverFns:deployHealthCheckre-runs check against deploy_url||site.live_url;mergePullRequestverifies workspace_role IN owner|admin via workspace_members, requiresGITHUB_TOKEN, callsPUT /repos/{repo}/pulls/{n}/mergewith merge_method squash|merge|rebase, updates PR state→merged + mbo_approved_by/_at, logs mbo_audit_log actionpr.merged). src/components/sites/DeploymentPipelineTab.tsx (Open PRs cards with title/#/state chip/checks chip/linked-serial chip/author/branches/opened-relative + GitHub external link + Merge button gated on isAdmin; Recent deployments rows with serial/status chip/health-score chip (green ≥80, amber ≥50, red else)/commit-sha7/branch/duration/incident pill + Check button to re-run health; Recently-closed compact list). - Files modified: src/components/sites/SiteTabs.tsx (added
pipelineto SiteTabKey union + SITE_TABS between Deployments and Design Bridge). src/routes/sites.$siteId.tsx (imported DeploymentPipelineTab, rendered when tab==='pipeline'). - Configuration required: set workspace secrets
GITHUB_WEBHOOK_SECRET(shared with the GitHub repo webhook) andGITHUB_TOKEN(a fine-grained PAT withpull_requests:writeon the target repos) before merge actions work. Configure each repo webhook to POST tohttps://<host>/api/public/github-webhookwith eventspull_request+deployment_status. Sites are matched to repos bysites.repo_urlcontaining the repo full_name. - Deviation from spec: edge functions
github-webhook/deploy-health-check/github-merge-rpcreimplemented as TSS public route + TanStack serverFns per project rule "Do NOT use Supabase Edge Functions"; HMAC verification, role gating, and 200-always semantics preserved. - Progress: 17 of 28 shipped.
BP-MBO-20260524-B3-001 — Email → Workflow Pipeline: Gmail/Outlook → classified → Task|Incident|Prompt|Note
- DB migration: new
email_ingest(workspace_id, site_id, serial EML-prefixed, source_provider gmail|outlook|manual|other, message_id, thread_id, from_address, from_name, to_addresses[], subject, body_text, body_html, received_at, status pending|approved|rejected|ignored, classified_as task|incident|prompt|note|unknown, classification_confidence, urgency_score 0-100, extracted_fields jsonb, classified_at, spawned_type/_id/_serial, reviewed_by/_at) with UNIQUE(workspace_id, message_id), per-ws status & received_at indexes, BEFORE INSERT serial trigger using mbo_generate_serial('EML', site.code), updated_at trigger, standard ws_select / ws_write / svc_all RLS via is_workspace_member + can_write_workspace. Newemail_integration_config(workspace_id, site_id, provider gmail|outlook, vault_secret_id, token_expiry, from_whitelist[], subject_keywords[], is_active, last_sync_at) with UNIQUE(workspace_id, provider) + same RLS pattern. Addedtasks.source_email_id+incidents.source_email_idFKs (ON DELETE SET NULL) with partial indexes. - Files created: src/lib/emailWorkflow.functions.ts (TanStack serverFns
classifyEmail+decideEmailwith requireSupabaseAuth + zod; classify calls Lovable AI Gatewaygoogle/gemini-2.5-flashJSON-mode returning {classified_as, confidence, urgency_score, extracted_fields}, falls back to keyword heuristic when key missing; idempotent — skips if classified_at set. decideEmail handles approve/reject/ignore — approve spawns tasks/incidents/prompts/context_pointers per override_classified_as with severity derived from urgency [≥80 SEV1 / ≥60 SEV2 / ≥40 SEV3 / else SEV4], writes spawned_type/_id/_serial back to email row, logs mbo_audit_log). src/routes/email.tsx (Outlet), src/routes/email.index.tsx (filter chips All/Pending/Approved/Rejected/Ignored, two-column inbox + sticky detail panel: from/subject/classification card w/ confidence + urgency + suggested priority, body preview, classify-now button, Create-as / Site / Priority overrides, Approve|Reject|Ignore action bar; toast confirms spawned serial), src/routes/email.setup.tsx (3-step wizard: provider cards Gmail/Outlook → connect placeholder → from-whitelist + subject-keywords filters; upserts to email_integration_config on conflict workspace_id+provider). - Files modified: src/components/worldport/Shell.tsx (Operations nav adds
{ label: "Email", to: "/email", icon: Mail }), src/components/inbox/InboxWidget.tsx (added pending-emails query — showsMail · N new emailschip linking to /email when count > 0). - Skipped per stack rules: edge functions
supabase/functions/email-classifyandsupabase/functions/email-approve— replaced with TanStack serverFns per project "Do NOT use Supabase Edge Functions" rule. OAuth flow + DOMPurify HTML rendering deferred until backend OAuth is wired — body_text is used and HTML is never rendered raw.
BP-MBO-20260524-B1-001 — Execution Plan Preview: structured plan + human approval gate
- DB migration: extended
execution_planswithobjective,files_touched jsonb,serials_to_mint jsonb,expected_outcome,risk_notes,prompt_body_hash,generated_by(kyle|aria|manual),generation_model,generation_ms,plan_version,reviewed_by,reviewed_at,reject_reason; relaxedplan_bodyNOT NULL; expanded status check to include pending|rejected|failed|outdated; added idx_exec_plans_ws + idx_exec_plans_status. Newplan_audit_log(workspace_id, plan_id, planned_files jsonb, actual_files jsonb, drift_detected, drift_summary) with standard ws_select / ws_insert + svc_all RLS. - Files created: src/lib/executionPlan.functions.ts (TanStack serverFns
generateExecutionPlan+decideExecutionPlanwith requireSupabaseAuth + zod; 10 plans/hour/workspace rate limit; calls Lovable AI Gatewaygoogle/gemini-2.5-flashJSON-mode for structured plan, falls back to regex-based stub when key missing or call fails; marks prior draft/pending plans asoutdatedon regen; decide writes prompts.status=ready|blocked + inserts mbo_audit_log entry), src/components/prompts/PlanGenerateButton.tsx (Generate / View Plan + status chip, calls serverFn via useServerFn), src/components/prompts/ExecutionPlanModal.tsx (header serial+status chip, objective card, files-touched table with create/modify/delete chips + reasons, serials-to-mint list, expected outcome, collapsible risk notes, rejection reason; Approve / Reject (inline textarea) actions wired to decideExecutionPlan; pending → shows action bar, approved → review timestamp, rejected → reason). - Files modified: src/components/prompts/PromptSidePanel.tsx (replaced legacy draft/approve insert mutations with PlanGenerateButton + ExecutionPlanModal; shows AlertTriangle warning row when plan is pending/rejected/outdated reminding user to approve before marking Ready), src/routes/sites.$siteId.tsx (PromptsTab gained
Plancolumn with status dot — gray no plan / yellow pending / green approved|done / red rejected|failed; fetched via groupedexecution_plansquery that takes latest per prompt_id). - Skipped per stack rules: edge functions
supabase/functions/plan-generateandsupabase/functions/plan-approve— replaced with TanStack serverFns per project "Do NOT use Supabase Edge Functions" rule. OpenAI swap: uses Lovable AI Gateway with no user-supplied key required. - Commit: (Lovable fills this in after push)
BP-MBO-20260524-B5-001 — Multi-Agent Team Configuration: agents registry, chat, assignments
- DB migration: extended
agentswith slug, provider (default 'custom'), api_endpoint, can_execute_prompts, tasks_completed, error_rate, skills[], avatar_url, paused; unique idx on (workspace_id, slug). Newagent_chat_messages(workspace_id, agent_id, session_id, role check user|agent|system, content, metadata jsonb) + idx_acm_session. Newagent_task_assignments(workspace_id, agent_id, entity_type check prompt|task|build_chain|incident, entity_id, status check assigned|active|done|failed, assigned_by, assigned_at, completed_at; UNIQUE agent_id+entity_type+entity_id). Standard ws_read / ws_write / svc_all RLS on both new tables. - Files created: src/routes/agents.tsx (Outlet), src/routes/agents.index.tsx (grid of AgentCards w/ avatar, status chip, tasks/errors/queue stats, [+ New Agent] modal with name/slug/role/provider/api_endpoint/skills checkboxes/can_execute toggle, EmptyState "Seed Kyle + Aria" button inserts both default agents), src/routes/agents.$agentId.tsx (Assignments | Performance | Skills | Chat tabs), src/components/agents/AgentChatInterface.tsx (per-session UUID, user/agent bubbles, "Thinking…" placeholder, Enter to send), src/lib/agentChat.functions.ts (TanStack serverFn
sendAgentMessagewith requireSupabaseAuth middleware, zod-validated input, fetches agent → inserts user msg → routes by provider [openai/aria_perplexity/kyle_base44 → POST api_endpoint, custom → echo handler] → inserts agent reply → updates agents.last_active_at), src/components/dashboard/AgentStatusWidget.tsx (2-col grid of agent chips: name + status dot + last_active_at relative). - Files modified: src/components/worldport/Shell.tsx (Operations nav + Bot icon → /agents), src/routes/prompt-queue.tsx (new
assignAgentupserts to agent_task_assignments on conflict agent_id+entity_type+entity_id alongside prompts.agent_id update; wired into both desktop + mobile PromptSidePanel onUpdateAgent), src/routes/index.tsx (mounted AgentStatusWidget after ActivityFeed). - Skipped per stack rules: edge function
supabase/functions/agent-chat— replaced with TanStack server fn instead (per project "Do NOT use Supabase Edge Functions" rule). - Pending: API key masking (api_key_id column not added — out of scope until secrets vault lands), group chat UI (schema supports session_id grouping; UI is post-B5 per spec).
BP-MBO-20260524-B9-001 — Native Integrations Hub v2: event→health trigger, public webhook, link/unlink sites
- Files created: src/routes/api/public/integration-event.ts (HMAC-SHA256 verified POST; body validated with zod
{site_integration_id uuid, event_type, severity info|warning|error, message, payload?}; inserts intointegration_eventsvia service role; 401 on bad sig, 500 ifINTEGRATION_WEBHOOK_SECRETnot set), src/components/integrations/LinkIntegrationModal.tsx (workspace-scoped site picker filtered to unlinked sites, inserts site_integrations row w/ status=active, health=unknown), supabase/migration (site_integrations.last_event_severity check info|warning|error, idx_integration_events_si_created DESC, idx_site_integrations_site,tg_si_health_from_eventSECURITY DEFINER trigger on integration_events INSERT — updates parent last_event_at + last_event_severity + degrades health_status error→down / warning→degraded / info=unchanged + score delta -15/-5 clamped 0-100) - Files modified: src/routes/integrations.$integrationKey.tsx (added "Link site" button in SectionTitle right slot opening LinkIntegrationModal, Unlink action column with confirm prompt, unlinkMut deletes site_integrations row — cascades events+credentials)
- Secrets required (placeholder — add when ready):
INTEGRATION_WEBHOOK_SECRET(any 32+ char random string; share with whatever provider will POST). Webhook returns 500 cleanly until set. - Webhook URL:
https://worldport-mbo.lovable.app/api/public/integration-event— sender computessha256=<hex>HMAC of the raw request body and sends asX-Signatureheader. - Commit: (Lovable fills this in after push)
BP-MBO-20260524-E8-001 — Site Management v2: deployments tab, pages CRUD modal, health timestamp, counter sync triggers
- Files created: src/components/sites/DeploymentHistory.tsx (read-only deployments table, top 20 ordered desc, status chip + truncated commit/message, queryKey
["site-tab", siteId, "deployments"]), src/components/sites/PageModal.tsx (create+edit modal for pages with/path validation, replaces inline AddPageSheet), supabase/migration (deployments table workspace+site scoped w/ DEP serial + tg_touch_updated_at, sites.last_health_check_at, idx_deployments_site, tg_sync_site_prompt_count + tg_sync_site_incident_count triggers handling INSERT/UPDATE/DELETE incl. site reassignment) - Files modified: src/components/sites/SiteTabs.tsx (added
deploymentsto SiteTabKey + SITE_TABS), src/routes/sites.$siteId.tsx (Deployments tab render, last_health_check_at displayed as "Last checked: …" / "Never checked" w/ Activity icon, PagesTab rewritten to use PageModal w/ row-click edit + queryKey["site-tab", siteId, "pages"], removed inline AddPageSheet and unused Page type) - Lazy tabs: Each tab is already mounted only when active (single conditional render per tab), so per-tab queries fire on demand. Standardised query key to
["site-tab", siteId, "pages"|"deployments"]. - Commit: (Lovable fills this in after push)
BP-MBO-20260524-E7-001 — Mobile Capture v2: PWA manifest, offline queue, iOS fix, quick classify, file guard
- Files created: public/manifest.json (MBO standalone PWA, theme #6366F1, 192/512 maskable icons referenced; PNG icons NOT created per spec), src/lib/offlineQueue.ts (native IndexedDB queue — no idb dep; enqueueCapture / flushQueue / getPendingCount; inserts into context_pointers and deletes row on success, leaves on failure), supabase/migration (documents.capture_metadata jsonb for lat/lng/captured_at/device_type/file_size_bytes, idx_documents_workspace)
- Files modified: src/routes/__root.tsx (theme-color #6366F1, apple-mobile-web-app-capable + status-bar-style meta, manifest link), src/components/mobile/MobileChrome.tsx (dedupe <style> via #mobile-chrome-style, online/offline listeners + top banner "You're offline — captures will be queued (N pending)", auto-flush + back-online toast 2s), src/routes/capture.voice.tsx (MediaRecorder undefined guard with browser hint, streamRef instead of state, audio/mp4 first then opus webm, 10-min hard auto-stop with 9:50 warning, 24MB size guard, 4-button quick classify step with 3s auto-advance to voice_note, editable transcript review screen before save), src/routes/capture.note.tsx (tags parsing
.filter(t => t.trim().length>0).map(t=>t.trim()), MAX_CHARS=50000 input cap + visible counter, navigator.vibrate(100) on save, offline detection → enqueueCapture + "Saved offline" message), CHANGELOG.md - Commit: (Lovable fills this in after push)
BP-MBO-20260524-E6-001 — Notes / Global Inbox: Telegram intake webhook
- Files created: src/routes/api/public/telegram-webhook.ts (public POST; verifies
X-Telegram-Bot-Api-Secret-Tokenagainst TELEGRAM_WEBHOOK_SECRET; parses!decision/!blocker/!reminder/!arch/!link/!warnprefixes → kind; extracts#taghashtags; resolves workspace viatelegram_chat_links.chat_idlookup else TELEGRAM_DEFAULT_WORKSPACE_ID; voice/audio → getFile → audio_url; inserts into context_pointers with source='telegram'; replies with serial via sendMessage; always returns 200), supabase/migration (telegram_chat_links: chat_id UNIQUE, workspace_id FK→workspaces ON DELETE CASCADE, default_site_id, default_kind, label; workspace RLS via is_workspace_member / can_write_workspace; service_role bypass) - Files modified: CHANGELOG.md
- Secrets required (placeholders — add when ready): TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET, TELEGRAM_DEFAULT_WORKSPACE_ID (optional fallback)
- Setup:
curl "https://api.telegram.org/bot<TOKEN>/setWebhook?url=https://worldport-mbo.lovable.app/api/public/telegram-webhook&secret_token=<TELEGRAM_WEBHOOK_SECRET>" - Commit: (Lovable fills this in after push)
BP-MBO-20260524-E5-001 — Research Vault / Atlas: pgvector, semantic search, freshness, create/edit UI
- Files created: supabase/functions/embed-vault-entry/index.ts (DB-webhook handler; OpenAI text-embedding-3-small → research_vault.embedding via service role; skips unchanged text; never re-throws), supabase/functions/vault-search/index.ts (JWT-auth; embeds query then RPC
match_research_vault), src/components/research-vault/VaultEntryModal.tsx (markdown editor, doc_type/app_tag/source_url/site_id fields, fire-and-forget embed invoke), supabase/migration (pgvector extension, research_vault.embedding vector(1536) + last_reviewed_at + freshness_score, HNSW cosine index, freshness index,match_research_vaultRPC, workspace_id backfill) - Files modified: src/routes/research-vault.tsx (workspace-scoped queries via useWorkspace, 50-row pagination with Load more, 400ms debounced semantic search via vault-search edge fn, freshness chip color green<30d / amber<90d / red, Mark reviewed + Edit actions, New entry button), supabase/config.toml (vault-search verify_jwt=true, embed-vault-entry verify_jwt=false), CHANGELOG.md
- Secrets required: OPENAI_API_KEY (already configured)
- Commit: (Lovable fills this in after push)
BP-MBO-20260524-E4-001 — Prompt Queue v2: execution plan, result webhook, agent assign, A/B compare, vault insert
- Files created: src/components/prompts/PromptCompareModal.tsx (two-column A/B reader, B search by serial/title, read-only), src/components/prompts/VaultPickerModal.tsx (research_vault title+content ilike search, click-to-insert), supabase/functions/prompt-result-webhook/index.ts (Bearer-auth callback, finds prompt by serial, sets status+result_summary+completed_at, audit-log inserts), supabase/migration (agents skeleton table + RLS + AGT serial trigger, execution_plans table + RLS + EXP serial trigger + touch trigger, prompts.agent_id FK→agents, prompts.result_summary)
- Files modified: src/components/prompts/PromptSidePanel.tsx (Execution Plan section with Generate/Approve, Agent dropdown, Compare button, Result success card), src/components/prompts/PromptModal.tsx (agent_id dropdown, Insert-from-vault button, site validation helper), src/routes/prompt-queue.tsx (explicit workspace_id filter on sites + prompts queries, agent_id in create), supabase/config.toml (prompt-result-webhook verify_jwt=false), CHANGELOG.md
- Secrets required: PROMPT_WEBHOOK_SECRET (edge function env)
- Commit: (Lovable fills this in after push)
BP-MBO-20260524-E3-001 — Incidents v2: Sentry intake, escalation rules, post-mortem, causal links, mitigated status
- Files created: supabase/functions/sentry-webhook/index.ts (HMAC-SHA256 verified Sentry intake → incidents insert, idempotent on external_id), src/components/incidents/PostMortemEditor.tsx (@uiw/react-md-editor lazy + template), supabase/migration (incidents.external_id/linked_task_id/linked_prompt_id/linked_pr_serial/postmortem_content, status check +mitigated, incident_escalation_rules table w/ RLS, idx_incidents_open_age, uq_incidents_external_id)
- Files modified: src/components/incidents/IncidentDetail.tsx (Mitigate pipeline step, Details/Post-mortem/Activity tabs, causal-link chips to /tasks + /prompt-queue, "Open prompt to fix" mutation), src/routes/incidents.tsx (workspace_id filter on incidents+sites, "mitigated" in STATUS_OPTIONS), supabase/config.toml (sentry-webhook verify_jwt=false), CHANGELOG.md
- Secrets required: SENTRY_WEBHOOK_SECRET, WORKSPACE_ID (edge function env)
- Commit: (Lovable fills this in after push)
BP-MBO-20260524-E2-001 — Tasks v2: blocked status, subtasks, dependencies, activity, attachments, bulk
- Files created: src/components/tasks/TaskDependencyBadge.tsx, supabase/migration (tasks.parent_task_id + blocked_reason, documents.linked_task_id, task_dependencies table, tasks_status_check w/ 'blocked', indexes)
- Files modified: src/components/tasks/KanbanBoard.tsx (added Blocked column, multi-select, subtask + dep badges plumb-through), src/components/tasks/TaskCard.tsx (checkbox, subtask chip, blocked reason banner, dep badge), src/components/tasks/TaskModal.tsx (tabs: Details/Subtasks/Activity/Attachments, blocked_reason field, mbo_audit_log feed, document attach via uploadDocument), src/routes/tasks.tsx (workspace_id filters, subtask+dep count queries, BulkActionsBar wiring), src/lib/uploadDocument.ts (linkedTaskId option), CHANGELOG.md
- Schema: tasks.parent_task_id (self-FK), tasks.blocked_reason, documents.linked_task_id, task_dependencies (workspace-scoped, RLS via is_workspace_member/can_write_workspace + svc_all)
- Behavior: subtasks hidden from board (shown in modal); status='blocked' surfaces reason field + warning border; bulk: mark done / assign site / change priority / delete
- Commit: (Lovable fills this in after push)
BP-MBO-20260524-F0-004-FRESHNESS-FOUNDATION — freshness thresholds, records, cron, MV, dashboard widget
- Files created: supabase/migrations/20260524215405**.sql (thresholds + records + RLS + compute/upsert functions), supabase/migrations/20260524215437**.sql (tier refresh fns, mv_platform_freshness, v_platform_freshness, pg_cron jobs, GRANT/REVOKE hardening), src/styles/freshness.css, src/hooks/useFreshnessClass.ts, src/components/dashboard/FreshnessBadge.tsx, src/components/dashboard/FreshnessWidget.tsx
- Files modified: src/routes/index.tsx (wired FreshnessWidget), CHANGELOG.md
- Schema:
freshness_thresholds(seeded 26 rows, 4 tiers),freshness_records(per-entity scores),compute_freshness()(linear/exponential/cliff decay),upsert_freshness_record(),refresh_freshness_tier1..4(),mv_platform_freshness(workspace rollup),v_platform_freshness(security-invoker view); pg_cron jobs scheduled per tier - Security: RLS via is_workspace_member/can_write_workspace on both tables; svc_all for service_role; REVOKEd anon/authenticated from internal fns + MV; frontend reads via v_platform_freshness only
- Verification: widget renders on / showing platform arc, tier bars, top-5 stale entities; realtime subscription on freshness_records; auto-refresh 60s
- Commit: (Lovable fills this in after push)
BP-MBO-20260524-F0-003-WORKSPACE-MIGRATION — finalize workspace multi-tenancy on 12 core tables
- Files modified: LOVABLE_RULES.md, CHANGELOG.md
- Schema migrations: added
UNIQUE (workspace_id, code)onsites; addedUNIQUE (workspace_id, letter_date)ondaily_letters - Verification: prior state already satisfied 11/12 blueprint requirements (no
owner_idcolumns, all 10 leaf tablesworkspace_id NOT NULL, ws_* policies in place, child tables parent-scoped). Only the two missing per-workspace unique constraints needed to be added. - Rules updated: LOVABLE_RULES.md "Workspace multi-tenancy" section now reflects that all 20 tables are workspace_id NOT NULL.
- Commit: (Lovable fills this in after push)
BP-MBO-20260524-F0-002B-SVC-ROLE-SYMMETRY — add svc_all service_role policy to 12 core tables
- Files created: supabase/migrations/20260524000002_svc_role_symmetry.sql (auto-named by Lovable Supabase integration)
- Files modified: CHANGELOG.md
- Schema migrations: svc_all policy added to blueprints, build_chains, context_pointers, daily_letters, documents, incidents, prompts, sites, tasks, site_integrations, integration_credential_links, integration_events
- Verification: ✓ migration applied · pre-existing linter warnings unchanged (not introduced by this migration)
- Commit: (Lovable fills this in after push)
BP-MBO-20260524-E1-001 — Dashboard v2
- Added
revenue_snapshotstable (workspace_id, snapshot_date, MRR/ARR/tenants/claims) with workspace-member read RLS. - New components:
AriaStatusCard,FinancialStrip,DesktopCaptureFab. - Dashboard: realtime channel (
prompts/incidents/tasks/agents) invalidates the dashboard query; refresh interval selector (30s/1m/5m/Off, default 1m); mobile stat grid 2×2. ActivityFeed+recentActivityextended withagententity (sourced frommbo_audit_logentity_type='agent').