Skip to content

feat(subscriptions): add proactive Pulse core - #91429

Draft
vdekrijger wants to merge 19 commits into
codex/ai-subscription-contexts-stack-v1from
codex/proactive-prompt-subscriptions-core-v1
Draft

feat(subscriptions): add proactive Pulse core#91429
vdekrijger wants to merge 19 commits into
codex/ai-subscription-contexts-stack-v1from
codex/proactive-prompt-subscriptions-core-v1

Conversation

@vdekrijger

@vdekrijger vdekrijger commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Problem

AI report subscriptions can surface opportunities, but they cannot safely prepare actions or measure whether teams adopt them.

This layer adds protected execution and the outcome loop behind the subscription surface introduced in #91428.

Changes

  • Pulse ranks recommendations with why now, confidence, effort, a baseline, and expected metric movement.
  • Adoption stays separate from artifact preparation, and adopted actions schedule outcome readouts.
  • The sandbox builds and tests one draft pull request before policy-gated publication.
  • Experiment drafts remain inactive, scoped, and human-activated.
  • Cross-run memory suppresses duplicate recommendations before publication.
  • Task-scoped OAuth and MCP allowlists bound analysis and write capabilities.
  • Temporal scheduling batches authorization and gives each child one bounded snapshot.
  • Server settings default Pulse execution off until operators enable it.

Before:

flowchart LR
    Schedule[Schedule] --> Report{{AI report}}
    Report --> Delivery[Report delivery]

    class Schedule,Delivery phYellow
    class Report phBlue

    classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff;
    classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000;
Loading

After:

flowchart LR
    Schedule[Schedule] --> Snapshot[Bounded snapshot]
    Snapshot --> Pulse{{Pulse analysis}}
    Pulse --> Gates[Policy gates]
    Gates --> Sandbox[Build and test]
    Sandbox --> Artifacts[Draft artifacts]
    Artifacts --> Delivery[Report delivery]
    Delivery --> Decision[Adoption decision]
    Decision --> Readout[Metric readout]
    Readout --> Memory[Cross-run memory]

    class Schedule,Delivery phYellow
    class Pulse phBlue
    class Gates phRed
    class Snapshot,Sandbox,Artifacts,Decision,Readout,Memory phGray

    classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff;
    classDef phRed fill:#f54e00,stroke:#f54e00,color:#fff;
    classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000;
    classDef phGray fill:#e5e7eb,stroke:#c7ccd1,color:#000;
Loading

This layer has no new visible UI. #91433 enforces disabled tools in the agent runtime; the final layer renders setup, artifacts, decisions, and readouts.

How did you test this code?

  • Backend tests cover grants, revocation, publication gates, outcome memory, readouts, tenant isolation, and failure containment.
  • Temporal worker integration covers over-budget and successful scheduled workflows.
  • Migration checks cover schema drift, concurrent indexes, and blocked operations.
  • Local mypy, OpenAPI and MCP generation, product isolation, and strict preflight completed successfully.
  • Independent QA approved correctness, security, and scheduler performance.
  • CI remains pending on this draft branch.

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Automatic notifications

  • Publish to changelog?

Docs update

Internal architecture, outcome-loop, and operations documents describe rollout switches, recovery, and failure semantics.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Codex implemented and tested this layer with parallel correctness, security, performance, and migration review.
  • Skills: /qa-team, /django-migrations, /writing-dataclasses, /running-ci-preflight, /writing-pr-descriptions, and /stacking-prs.
  • Review replaced per-child authorization scans with one schedule-wide manifest and bounded reads.
  • Public-artifact review found no customer-derived or private material.

@vdekrijger vdekrijger self-assigned this Aug 31, 2026
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

🚨 Trunk lane — universal lane

This PR is assigned to the universal lane. It cannot merge in parallel with other PRs, so it can take longer to merge. Ask dev-ex if you think this is wrong.

Bundle size — no change

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 68.17 MiB · no change

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.37 MiB · 22 files no change ███░░░░░░░ 30.3% of 4.51 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.82 MiB · 3,245 files no change █████████░ 90.8% of 9.71 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
762 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
307.0 KiB ../node_modules/.pnpm/posthog-js@1.422.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
267.7 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
263.5 KiB ../node_modules/.pnpm/posthog-js@1.422.5_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.js
252.5 KiB src/taxonomy/core-filter-definitions-by-group.json
154.2 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
104.7 KiB src/lib/api.ts
95.2 KiB ../packages/quill/packages/quill/dist/index.js
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Toolbar bundle — eager 2.26 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.26 MiB · 18 files no change ████░░░░░░ 39.5% of 5.72 MiB
Deferred (lazy) 2.11 MiB · 45 files no change n/a — loads on demand
Loader dist/toolbar.js 1.1 KiB no change █░░░░░░░░░ 5.8% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
749.4 KiB dist/toolbar/toolbar-app-MMYGYX5P.css
588.2 KiB dist/toolbar/chunk-chunk-6JQSKVXS.js
484.7 KiB dist/toolbar/chunk-chunk-FOUIAG54.js
133.8 KiB dist/toolbar/chunk-chunk-RW4NJO2H.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
71.3 KiB dist/toolbar/toolbar-app-XLN75TDL.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-KUFHOGL7.js
21.0 KiB dist/toolbar/chunk-chunk-2FR6R57Y.js
6.8 KiB dist/toolbar/chunk-chunk-DV7IWQNF.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

Dist folder size — 🔺 +49.8 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1435.60 MiB · 🔺 +49.8 KiB (+0.0%)

ℹ️ MCP UI apps size — 33 app(s), 17662.9 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 600.0 KB 195.2 KB
action 458.2 KB 195.2 KB
action-list 564.9 KB 195.2 KB
cohort 457.2 KB 195.2 KB
cohort-list 563.8 KB 195.2 KB
email-template 457.0 KB 195.2 KB
error-details 472.9 KB 195.2 KB
error-issue 457.9 KB 195.2 KB
error-issue-list 564.7 KB 195.2 KB
experiment 562.0 KB 195.2 KB
experiment-list 565.6 KB 195.2 KB
experiment-results 567.1 KB 195.2 KB
feature-flag 567.6 KB 195.2 KB
feature-flag-list 571.4 KB 195.2 KB
feature-flag-testing 461.4 KB 195.2 KB
inline-scan 457.7 KB 195.2 KB
insight-actors 563.0 KB 195.2 KB
invite-email-preview 456.4 KB 195.2 KB
llm-costs 560.0 KB 195.2 KB
session-recording 459.0 KB 195.2 KB
survey 458.8 KB 195.2 KB
survey-global-stats 562.7 KB 195.2 KB
survey-list 565.5 KB 195.2 KB
survey-stats 562.7 KB 195.2 KB
trace-span 457.6 KB 195.2 KB
trace-span-list 564.7 KB 195.2 KB
vision-observation-list 563.9 KB 195.2 KB
workflow 457.5 KB 195.2 KB
workflow-list 564.2 KB 195.2 KB
loops-review 461.9 KB 195.2 KB
query-results 755.2 KB 195.2 KB
render-ui 838.3 KB 195.2 KB
visual-review-snapshots 462.0 KB 195.2 KB
⚠️ Django migration SQL — 9 new migrations to review

We've detected new migrations on this PR. Review the SQL output for each migration:

products/subscriptions/backend/migrations/0001_initial.py

BEGIN;
--
-- Create model Opportunity
--
CREATE TABLE "subscriptions_opportunity" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "stable_key" varchar(512) NOT NULL, "title" varchar(400) NOT NULL, "summary" text NOT NULL, "status" varchar(16) DEFAULT 'open' NOT NULL, "first_seen_at" timestamp with time zone NOT NULL, "last_seen_at" timestamp with time zone NOT NULL, "team_id" integer NOT NULL);
--
-- Create model ActionProposal
--
CREATE TABLE "subscriptions_actionproposal" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "stable_action_key" varchar(512) NOT NULL, "kind" varchar(32) NOT NULL, "normalized_target" jsonb DEFAULT '{}' NOT NULL, "status" varchar(64) NULL, "first_seen_at" timestamp with time zone NOT NULL, "last_seen_at" timestamp with time zone NOT NULL, "team_id" integer NOT NULL, "opportunity_id" uuid NOT NULL);
--
-- Create model PublicResearchSubject
--
CREATE TABLE "subscriptions_publicresearchsubject" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "name" varchar(255) NOT NULL, "description" varchar(1000) NOT NULL, "canonical_domain" varchar(255) NOT NULL, "allowed_result_domains" jsonb DEFAULT '[]' NOT NULL, "query_templates" jsonb DEFAULT '[]' NOT NULL, "eligible" boolean DEFAULT true NOT NULL, "reviewed_at" timestamp with time zone NULL, "reviewed_by_id" integer NULL, "created_by_id" integer NULL, "disabled_at" timestamp with time zone NULL, "team_id" integer NOT NULL);
--
-- Create model ProactiveSubscriptionConfig
--
CREATE TABLE "subscriptions_proactivesubscriptionconfig" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "subscription_id" integer NOT NULL, "enabled" boolean DEFAULT false NOT NULL, "repository" varchar(255) NULL, "create_draft_pr" boolean DEFAULT false NOT NULL, "team_id" integer NOT NULL, "public_research_subject_id" uuid NULL);
--
-- Create model PulseRun
--
CREATE TABLE "subscriptions_pulserun" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "subscription_id" integer NOT NULL, "delivery_id" uuid NOT NULL, "status" varchar(16) DEFAULT 'pending' NOT NULL, "started_at" timestamp with time zone NULL, "finished_at" timestamp with time zone NULL, "config_snapshot" jsonb DEFAULT '{}' NOT NULL, "report_snapshot_ref" varchar(512) NOT NULL, "task_id" uuid NULL, "analysis_task_run_id" uuid NULL, "execution_task_run_id" uuid NULL, "failure_code" varchar(128) NULL, "metrics" jsonb DEFAULT '{}' NOT NULL, "team_id" integer NOT NULL);
--
-- Create model EvidenceToolCall
--
CREATE TABLE "subscriptions_evidencetoolcall" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "tool_call_id" varchar(255) NOT NULL, "tool_name" varchar(255) NOT NULL, "tool_schema_version" varchar(128) NOT NULL, "normalized_arguments_ref" varchar(512) NOT NULL, "normalized_result_ref" varchar(512) NOT NULL, "actor_id" integer NULL, "started_at" timestamp with time zone NULL, "completed_at" timestamp with time zone NULL, "result_truncated" boolean DEFAULT false NOT NULL, "error_class" varchar(255) NULL, "raw_arguments_ref" varchar(512) NULL, "raw_result_ref" varchar(512) NULL, "raw_expires_at" timestamp with time zone NULL, "purged_at" timestamp with time zone NULL, "team_id" integer NOT NULL, "run_id" uuid NOT NULL);
--
-- Create model EvidenceSet
--
CREATE TABLE "subscriptions_evidenceset" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "content_hash" varchar(64) NOT NULL, "item_refs" jsonb DEFAULT '[]' NOT NULL, "team_id" integer NOT NULL, "run_id" uuid NOT NULL);
--
-- Create model DeliveryLedger
--
CREATE TABLE "subscriptions_deliveryledger" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "destination" varchar(255) NOT NULL, "logical_key" varchar(512) NOT NULL, "status" varchar(32) DEFAULT 'pending' NOT NULL, "rendered_content_ref" varchar(512) NULL, "rendered_content_hash" varchar(64) NULL, "provider_idempotency_key" varchar(512) NOT NULL, "attempt_count" integer DEFAULT 0 NOT NULL CHECK ("attempt_count" >= 0), "accepted_at" timestamp with time zone NULL, "failure_code" varchar(128) NULL, "team_id" integer NOT NULL, "run_id" uuid NOT NULL);
--
-- Create model RepositoryGrant
--
CREATE TABLE "subscriptions_repositorygrant" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "authorizer_id" integer NOT NULL, "automation_owner_id" integer NOT NULL, "integration_id" integer NOT NULL, "repository_installation_id" varchar(255) NOT NULL, "repository" varchar(255) NOT NULL, "capabilities" jsonb DEFAULT '{}' NOT NULL, "grant_version" integer DEFAULT 1 NOT NULL CHECK ("grant_version" >= 0), "active" boolean DEFAULT true NOT NULL, "revoked_at" timestamp with time zone NULL, "config_id" uuid NOT NULL, "team_id" integer NOT NULL);
--
-- Add field repository_grant to proactivesubscriptionconfig
--
ALTER TABLE "subscriptions_proactivesubscriptionconfig" ADD COLUMN "repository_grant_id" uuid NULL CONSTRAINT "subscriptions_proact_repository_grant_id_570a9f04_fk_subscript" REFERENCES "subscriptions_repositorygrant"("id") DEFERRABLE INITIALLY DEFERRED; SET CONSTRAINTS "subscriptions_proact_repository_grant_id_570a9f04_fk_subscript" IMMEDIATE;
--
-- Create model RunAction
--
CREATE TABLE "subscriptions_runaction" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "action_key" varchar(512) NOT NULL, "kind" varchar(32) NOT NULL, "title" varchar(400) NOT NULL, "rationale" text NOT NULL, "expected_impact" text NOT NULL, "confidence" double precision NULL, "effort" varchar(64) NOT NULL, "rank" smallint NOT NULL CHECK ("rank" >= 0), "implementation_selected" boolean DEFAULT false NOT NULL, "acted_on" boolean DEFAULT false NOT NULL, "acted_on_at" timestamp with time zone NULL, "acted_on_by_id" integer NULL, "status" varchar(16) DEFAULT 'proposed' NOT NULL, "evidence_set_id" uuid NULL, "opportunity_id" uuid NOT NULL, "proposal_id" uuid NOT NULL, "run_id" uuid NOT NULL, "team_id" integer NOT NULL);
--
-- Create model Artifact
--
CREATE TABLE "subscriptions_artifact" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "kind" varchar(32) NOT NULL, "phase" varchar(64) NOT NULL, "idempotency_key" varchar(512) NOT NULL, "external_id" varchar(255) NULL, "external_url" varchar(2000) NULL, "task_id" uuid NULL, "execution_task_run_id" uuid NULL, "experiment_id" integer NULL, "external_state" varchar(32) NULL, "active_claim" boolean DEFAULT false NOT NULL, "metadata" jsonb DEFAULT '{}' NOT NULL, "failure_code" varchar(128) NULL, "verified_at" timestamp with time zone NULL, "status" varchar(24) DEFAULT 'reserved' NOT NULL, "proposal_id" uuid NOT NULL, "team_id" integer NOT NULL, "opportunity_id" uuid NOT NULL, "run_id" uuid NOT NULL, "action_id" uuid NOT NULL);
--
-- Create constraint sub_pulse_opportunity_team_key_unique on model opportunity
--
ALTER TABLE "subscriptions_opportunity" ADD CONSTRAINT "sub_pulse_opportunity_team_key_unique" UNIQUE ("team_id", "stable_key");
--
-- Create constraint sub_pulse_proposal_identity_unique on model actionproposal
--
ALTER TABLE "subscriptions_actionproposal" ADD CONSTRAINT "sub_pulse_proposal_identity_unique" UNIQUE ("team_id", "opportunity_id", "stable_action_key", "kind");
--
-- Create constraint sub_pulse_run_team_delivery_unique on model pulserun
--
ALTER TABLE "subscriptions_pulserun" ADD CONSTRAINT "sub_pulse_run_team_delivery_unique" UNIQUE ("team_id", "delivery_id");
--
-- Create constraint sub_pulse_run_one_active_subscription on model pulserun
--
CREATE UNIQUE INDEX "sub_pulse_run_one_active_subscription" ON "subscriptions_pulserun" ("team_id", "subscription_id") WHERE "status" IN ('pending', 'analyzing', 'reserving', 'executing');
--
-- Create constraint sub_pulse_tool_call_run_id_unique on model evidencetoolcall
--
ALTER TABLE "subscriptions_evidencetoolcall" ADD CONSTRAINT "sub_pulse_tool_call_run_id_unique" UNIQUE ("run_id", "tool_call_id");
--
-- Create constraint sub_pulse_ledger_run_destination_unique on model deliveryledger
--
ALTER TABLE "subscriptions_deliveryledger" ADD CONSTRAINT "sub_pulse_ledger_run_destination_unique" UNIQUE ("run_id", "destination");
--
-- Create constraint sub_pulse_ledger_team_key_unique on model deliveryledger
--
ALTER TABLE "subscriptions_deliveryledger" ADD CONSTRAINT "sub_pulse_ledger_team_key_unique" UNIQUE ("team_id", "logical_key");
--
-- Create constraint sub_pulse_config_team_subscription_unique on model proactivesubscriptionconfig
--
ALTER TABLE "subscriptions_proactivesubscriptionconfig" ADD CONSTRAINT "sub_pulse_config_team_subscription_unique" UNIQUE ("team_id", "subscription_id");
--
-- Create constraint sub_pulse_run_action_key_unique on model runaction
--
ALTER TABLE "subscriptions_runaction" ADD CONSTRAINT "sub_pulse_run_action_key_unique" UNIQUE ("run_id", "action_key");
--
-- Create constraint sub_pulse_run_one_selected_action on model runaction
--
CREATE UNIQUE INDEX "sub_pulse_run_one_selected_action" ON "subscriptions_runaction" ("run_id") WHERE "implementation_selected";
--
-- Create constraint sub_pulse_artifact_run_kind_unique on model artifact
--
ALTER TABLE "subscriptions_artifact" ADD CONSTRAINT "sub_pulse_artifact_run_kind_unique" UNIQUE ("run_id", "kind");
--
-- Create constraint sub_pulse_artifact_proposal_kind_unique on model artifact
--
ALTER TABLE "subscriptions_artifact" ADD CONSTRAINT "sub_pulse_artifact_proposal_kind_unique" UNIQUE ("proposal_id", "kind");
--
-- Create constraint sub_pulse_artifact_idempotency_unique on model artifact
--
ALTER TABLE "subscriptions_artifact" ADD CONSTRAINT "sub_pulse_artifact_idempotency_unique" UNIQUE ("team_id", "kind", "idempotency_key");
--
-- Create constraint sub_pulse_artifact_active_pr_claim on model artifact
--
CREATE UNIQUE INDEX "sub_pulse_artifact_active_pr_claim" ON "subscriptions_artifact" ("opportunity_id") WHERE ("active_claim" AND "kind" = 'draft_pr');
CREATE INDEX "subscriptions_opportunity_team_id_f9f05e11" ON "subscriptions_opportunity" ("team_id");
ALTER TABLE "subscriptions_actionproposal" ADD CONSTRAINT "subscriptions_action_opportunity_id_eb511d4e_fk_subscript" FOREIGN KEY ("opportunity_id") REFERENCES "subscriptions_opportunity" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_actionproposal_team_id_0770b305" ON "subscriptions_actionproposal" ("team_id");
CREATE INDEX "subscriptions_actionproposal_opportunity_id_eb511d4e" ON "subscriptions_actionproposal" ("opportunity_id");
CREATE INDEX "subscriptions_publicresearchsubject_team_id_fa977d7d" ON "subscriptions_publicresearchsubject" ("team_id");
ALTER TABLE "subscriptions_proactivesubscriptionconfig" ADD CONSTRAINT "subscriptions_proact_public_research_subj_94ba07c8_fk_subscript" FOREIGN KEY ("public_research_subject_id") REFERENCES "subscriptions_publicresearchsubject" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_proactivesubscriptionconfig_team_id_91335b69" ON "subscriptions_proactivesubscriptionconfig" ("team_id");
CREATE INDEX "subscriptions_proactivesub_public_research_subject_id_94ba07c8" ON "subscriptions_proactivesubscriptionconfig" ("public_research_subject_id");
CREATE INDEX "subscriptions_pulserun_team_id_20e51b53" ON "subscriptions_pulserun" ("team_id");
CREATE INDEX "sub_pulse_run_active_idx" ON "subscriptions_pulserun" ("updated_at") WHERE "status" IN ('pending', 'analyzing', 'reserving', 'executing');
CREATE INDEX "sub_pulse_run_daily_idx" ON "subscriptions_pulserun" ("created_at") WHERE NOT ("status" = 'skipped');
ALTER TABLE "subscriptions_evidencetoolcall" ADD CONSTRAINT "subscriptions_eviden_run_id_acedd81f_fk_subscript" FOREIGN KEY ("run_id") REFERENCES "subscriptions_pulserun" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_evidencetoolcall_team_id_020cd039" ON "subscriptions_evidencetoolcall" ("team_id");
CREATE INDEX "subscriptions_evidencetoolcall_run_id_acedd81f" ON "subscriptions_evidencetoolcall" ("run_id");
CREATE INDEX "sub_pulse_ev_expiry_idx" ON "subscriptions_evidencetoolcall" ("raw_expires_at", "id") WHERE ("purged_at" IS NULL AND "raw_expires_at" IS NOT NULL);
ALTER TABLE "subscriptions_evidenceset" ADD CONSTRAINT "subscriptions_eviden_run_id_e77bcbac_fk_subscript" FOREIGN KEY ("run_id") REFERENCES "subscriptions_pulserun" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_evidenceset_team_id_bc0e2241" ON "subscriptions_evidenceset" ("team_id");
CREATE INDEX "subscriptions_evidenceset_run_id_e77bcbac" ON "subscriptions_evidenceset" ("run_id");
ALTER TABLE "subscriptions_deliveryledger" ADD CONSTRAINT "subscriptions_delive_run_id_ff0e6763_fk_subscript" FOREIGN KEY ("run_id") REFERENCES "subscriptions_pulserun" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_deliveryledger_team_id_54655f76" ON "subscriptions_deliveryledger" ("team_id");
CREATE INDEX "subscriptions_deliveryledger_run_id_ff0e6763" ON "subscriptions_deliveryledger" ("run_id");
ALTER TABLE "subscriptions_repositorygrant" ADD CONSTRAINT "subscriptions_reposi_config_id_d40d61ce_fk_subscript" FOREIGN KEY ("config_id") REFERENCES "subscriptions_proactivesubscriptionconfig" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_repositorygrant_config_id_d40d61ce" ON "subscriptions_repositorygrant" ("config_id");
CREATE INDEX "subscriptions_repositorygrant_team_id_6edff48d" ON "subscriptions_repositorygrant" ("team_id");
CREATE INDEX "subscriptions_proactivesub_repository_grant_id_570a9f04" ON "subscriptions_proactivesubscriptionconfig" ("repository_grant_id");
ALTER TABLE "subscriptions_runaction" ADD CONSTRAINT "subscriptions_runact_evidence_set_id_b085901e_fk_subscript" FOREIGN KEY ("evidence_set_id") REFERENCES "subscriptions_evidenceset" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_runaction" ADD CONSTRAINT "subscriptions_runact_opportunity_id_2fd35ee1_fk_subscript" FOREIGN KEY ("opportunity_id") REFERENCES "subscriptions_opportunity" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_runaction" ADD CONSTRAINT "subscriptions_runact_proposal_id_f84e1505_fk_subscript" FOREIGN KEY ("proposal_id") REFERENCES "subscriptions_actionproposal" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_runaction" ADD CONSTRAINT "subscriptions_runact_run_id_c7ce3dfa_fk_subscript" FOREIGN KEY ("run_id") REFERENCES "subscriptions_pulserun" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_runaction_evidence_set_id_b085901e" ON "subscriptions_runaction" ("evidence_set_id");
CREATE INDEX "subscriptions_runaction_opportunity_id_2fd35ee1" ON "subscriptions_runaction" ("opportunity_id");
CREATE INDEX "subscriptions_runaction_proposal_id_f84e1505" ON "subscriptions_runaction" ("proposal_id");
CREATE INDEX "subscriptions_runaction_run_id_c7ce3dfa" ON "subscriptions_runaction" ("run_id");
CREATE INDEX "subscriptions_runaction_team_id_11e72f27" ON "subscriptions_runaction" ("team_id");
ALTER TABLE "subscriptions_artifact" ADD CONSTRAINT "subscriptions_artifa_proposal_id_b7e29192_fk_subscript" FOREIGN KEY ("proposal_id") REFERENCES "subscriptions_actionproposal" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_artifact" ADD CONSTRAINT "subscriptions_artifa_opportunity_id_66861bc2_fk_subscript" FOREIGN KEY ("opportunity_id") REFERENCES "subscriptions_opportunity" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_artifact" ADD CONSTRAINT "subscriptions_artifa_run_id_dec4876c_fk_subscript" FOREIGN KEY ("run_id") REFERENCES "subscriptions_pulserun" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_artifact" ADD CONSTRAINT "subscriptions_artifa_action_id_85045cc8_fk_subscript" FOREIGN KEY ("action_id") REFERENCES "subscriptions_runaction" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_artifact_proposal_id_b7e29192" ON "subscriptions_artifact" ("proposal_id");
CREATE INDEX "subscriptions_artifact_team_id_6e1e7c6e" ON "subscriptions_artifact" ("team_id");
CREATE INDEX "subscriptions_artifact_opportunity_id_66861bc2" ON "subscriptions_artifact" ("opportunity_id");
CREATE INDEX "subscriptions_artifact_run_id_dec4876c" ON "subscriptions_artifact" ("run_id");
CREATE INDEX "subscriptions_artifact_action_id_85045cc8" ON "subscriptions_artifact" ("action_id");
COMMIT;

products/subscriptions/backend/migrations/0002_evidencerawbody.py

BEGIN;
--
-- Create model EvidenceRawBody
--
CREATE TABLE "subscriptions_evidencerawbody" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "encrypted_arguments" text NULL, "encrypted_result" text NULL, "team_id" integer NOT NULL, "tool_call_id" uuid NOT NULL UNIQUE);
ALTER TABLE "subscriptions_evidencerawbody" ADD CONSTRAINT "subscriptions_eviden_tool_call_id_7e07c269_fk_subscript" FOREIGN KEY ("tool_call_id") REFERENCES "subscriptions_evidencetoolcall" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_evidencerawbody_team_id_1f951bd7" ON "subscriptions_evidencerawbody" ("team_id");
COMMIT;

products/subscriptions/backend/migrations/0003_pulserun_orchestration_state.py

BEGIN;
--
-- Add field cancellation_requested_at to pulserun
--
ALTER TABLE "subscriptions_pulserun" ADD COLUMN "cancellation_requested_at" timestamp with time zone NULL;
--
-- Add field finalization_deadline_at to pulserun
--
ALTER TABLE "subscriptions_pulserun" ADD COLUMN "finalization_deadline_at" timestamp with time zone NULL;
--
-- Add field wall_clock_deadline_at to pulserun
--
ALTER TABLE "subscriptions_pulserun" ADD COLUMN "wall_clock_deadline_at" timestamp with time zone NULL;
--
-- Add field skip_reason to pulserun
--
ALTER TABLE "subscriptions_pulserun" ADD COLUMN "skip_reason" varchar(128) NULL;
--
-- Add field publication_lease_id to artifact
--
ALTER TABLE "subscriptions_artifact" ADD COLUMN "publication_lease_id" uuid NULL;
--
-- Alter field status on pulserun
--
-- (no-op)
--
-- Alter field kind on actionproposal
--
-- (no-op)
--
-- Alter field kind on runaction
--
-- (no-op)
COMMIT;

products/subscriptions/backend/migrations/0004_pulse_outcome_loop.py

BEGIN;
--
-- Add field expected_change_lower to runaction
--
ALTER TABLE "subscriptions_runaction" ADD COLUMN "expected_change_lower" numeric(30, 10) NULL;
--
-- Add field expected_change_type to runaction
--
ALTER TABLE "subscriptions_runaction" ADD COLUMN "expected_change_type" varchar(32) NULL;
--
-- Add field expected_change_upper to runaction
--
ALTER TABLE "subscriptions_runaction" ADD COLUMN "expected_change_upper" numeric(30, 10) NULL;
--
-- Add field metric_direction to runaction
--
ALTER TABLE "subscriptions_runaction" ADD COLUMN "metric_direction" varchar(16) NULL;
--
-- Add field metric_name to runaction
--
ALTER TABLE "subscriptions_runaction" ADD COLUMN "metric_name" varchar(255) NULL;
--
-- Add field metric_unit to runaction
--
ALTER TABLE "subscriptions_runaction" ADD COLUMN "metric_unit" varchar(16) NULL;
--
-- Add field readout_after_days to runaction
--
ALTER TABLE "subscriptions_runaction" ADD COLUMN "readout_after_days" smallint NULL CHECK ("readout_after_days" >= 0);
--
-- Add field why_now to runaction
--
ALTER TABLE "subscriptions_runaction" ADD COLUMN "why_now" text NULL;
--
-- Create model OutcomePlan
--
CREATE TABLE "subscriptions_outcomeplan" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "subscription_id" integer NOT NULL, "measurement_spec" jsonb DEFAULT '{}' NOT NULL, "baseline_value" numeric(30, 10) NOT NULL, "baseline_from" timestamp with time zone NOT NULL, "baseline_to" timestamp with time zone NOT NULL, "adoption_status" varchar(16) DEFAULT 'pending' NOT NULL, "adoption_source" varchar(32) NULL, "adopted_at" timestamp with time zone NULL, "decided_by_id" integer NULL, "readout_status" varchar(16) DEFAULT 'waiting' NOT NULL, "next_readout_at" timestamp with time zone NULL, "attempt_count" smallint DEFAULT 0 NOT NULL CHECK ("attempt_count" >= 0), "claimed_at" timestamp with time zone NULL, "last_failure_code" varchar(128) NULL, "completed_at" timestamp with time zone NULL, "claimed_by_run_id" uuid NULL, "proposal_id" uuid NOT NULL, "source_action_id" uuid NOT NULL, "team_id" integer NOT NULL);
--
-- Create model OutcomeObservation
--
CREATE TABLE "subscriptions_outcomeobservation" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "attempt_number" smallint NOT NULL CHECK ("attempt_number" >= 0), "status" varchar(16) NOT NULL, "observed_value" numeric(30, 10) NULL, "observed_from" timestamp with time zone NULL, "observed_to" timestamp with time zone NULL, "absolute_delta" numeric(30, 10) NULL, "relative_delta" numeric(30, 10) NULL, "verdict" varchar(16) NOT NULL, "confidence" numeric(5, 4) NULL, "failure_code" varchar(128) NULL, "evidence_set_id" uuid NULL, "run_id" uuid NOT NULL, "team_id" integer NOT NULL, "plan_id" uuid NOT NULL);
--
-- Add CHECK constraint sub_pulse_action_readout_after_days_valid on runaction (NOT VALID)
--
ALTER TABLE "subscriptions_runaction" ADD CONSTRAINT "sub_pulse_action_readout_after_days_valid" CHECK (("readout_after_days" IS NULL OR "readout_after_days" IN (3, 7, 14, 28))) NOT VALID;
--
-- Create constraint sub_pulse_outcome_active_proposal_unique on model outcomeplan
--
CREATE UNIQUE INDEX "sub_pulse_outcome_active_proposal_unique" ON "subscriptions_outcomeplan" ("proposal_id") WHERE ("adoption_status" IN ('pending', 'adopted') AND "readout_status" IN ('waiting', 'scheduled', 'due', 'measuring'));
--
-- Create index sub_pulse_observation_plan_idx on field(s) plan, created_at of model outcomeobservation
--
CREATE INDEX "sub_pulse_observation_plan_idx" ON "subscriptions_outcomeobservation" ("plan_id", "created_at");
--
-- Create constraint sub_pulse_observation_plan_attempt_unique on model outcomeobservation
--
ALTER TABLE "subscriptions_outcomeobservation" ADD CONSTRAINT "sub_pulse_observation_plan_attempt_unique" UNIQUE ("plan_id", "attempt_number");
ALTER TABLE "subscriptions_outcomeplan" ADD CONSTRAINT "subscriptions_outcom_claimed_by_run_id_6ec65b91_fk_subscript" FOREIGN KEY ("claimed_by_run_id") REFERENCES "subscriptions_pulserun" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_outcomeplan" ADD CONSTRAINT "subscriptions_outcom_proposal_id_48c2e300_fk_subscript" FOREIGN KEY ("proposal_id") REFERENCES "subscriptions_actionproposal" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_outcomeplan" ADD CONSTRAINT "subscriptions_outcom_source_action_id_50a794fb_fk_subscript" FOREIGN KEY ("source_action_id") REFERENCES "subscriptions_runaction" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_outcomeplan_claimed_by_run_id_6ec65b91" ON "subscriptions_outcomeplan" ("claimed_by_run_id");
CREATE INDEX "subscriptions_outcomeplan_proposal_id_48c2e300" ON "subscriptions_outcomeplan" ("proposal_id");
CREATE INDEX "subscriptions_outcomeplan_source_action_id_50a794fb" ON "subscriptions_outcomeplan" ("source_action_id");
CREATE INDEX "subscriptions_outcomeplan_team_id_41606f1c" ON "subscriptions_outcomeplan" ("team_id");
CREATE INDEX "sub_pulse_outcome_due_idx" ON "subscriptions_outcomeplan" ("team_id", "subscription_id", "readout_status", "next_readout_at");
CREATE INDEX "sub_pulse_outcome_adoption_idx" ON "subscriptions_outcomeplan" ("team_id", "adoption_status", "updated_at");
CREATE INDEX "sub_pulse_outcome_claim_idx" ON "subscriptions_outcomeplan" ("team_id", "claimed_at");
CREATE INDEX "sub_pulse_out_sched_idx" ON "subscriptions_outcomeplan" ("next_readout_at") WHERE "readout_status" = 'scheduled';
CREATE INDEX "sub_pulse_out_measure_idx" ON "subscriptions_outcomeplan" ("claimed_at") WHERE "readout_status" = 'measuring';
CREATE INDEX "sub_pulse_out_pending_idx" ON "subscriptions_outcomeplan" ("updated_at", "id") WHERE ("adoption_status" = 'pending' AND "readout_status" = 'waiting');
CREATE INDEX "sub_pulse_out_memory_idx" ON "subscriptions_outcomeplan" ("team_id", "subscription_id", "updated_at" DESC, "created_at" DESC);
ALTER TABLE "subscriptions_outcomeobservation" ADD CONSTRAINT "subscriptions_outcom_evidence_set_id_ba56cf0a_fk_subscript" FOREIGN KEY ("evidence_set_id") REFERENCES "subscriptions_evidenceset" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_outcomeobservation" ADD CONSTRAINT "subscriptions_outcom_run_id_44952474_fk_subscript" FOREIGN KEY ("run_id") REFERENCES "subscriptions_pulserun" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "subscriptions_outcomeobservation" ADD CONSTRAINT "subscriptions_outcom_plan_id_89def515_fk_subscript" FOREIGN KEY ("plan_id") REFERENCES "subscriptions_outcomeplan" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "subscriptions_outcomeobservation_evidence_set_id_ba56cf0a" ON "subscriptions_outcomeobservation" ("evidence_set_id");
CREATE INDEX "subscriptions_outcomeobservation_run_id_44952474" ON "subscriptions_outcomeobservation" ("run_id");
CREATE INDEX "subscriptions_outcomeobservation_team_id_b370358e" ON "subscriptions_outcomeobservation" ("team_id");
CREATE INDEX "subscriptions_outcomeobservation_plan_id_89def515" ON "subscriptions_outcomeobservation" ("plan_id");
COMMIT;

products/subscriptions/backend/migrations/0005_outcome_observation_immutability.py

BEGIN;
--
-- Raw SQL operation
--

                -- migration-analyzer: safe reason=This defines a trigger; BEFORE UPDATE is event syntax and performs no data mutation.
                CREATE OR REPLACE FUNCTION subscriptions_outcome_observation_immutable()
                RETURNS trigger AS $$
                BEGIN
                    RAISE EXCEPTION 'Outcome observations are immutable';
                END;
                $$ LANGUAGE plpgsql;

                CREATE OR REPLACE TRIGGER subscriptions_outcome_observation_immutable
                BEFORE UPDATE ON subscriptions_outcomeobservation
                FOR EACH ROW EXECUTE FUNCTION subscriptions_outcome_observation_immutable();
            
COMMIT;

products/subscriptions/backend/migrations/0006_pulse_run_history_and_artifact_publication_indexes.py

--
-- Concurrently create index sub_pulse_run_history_idx on field(s) team, subscription_id, -created_at of model pulserun
--
SET lock_timeout = 0;
SET statement_timeout = 0;
CREATE INDEX CONCURRENTLY "sub_pulse_run_history_idx" ON "subscriptions_pulserun" ("team_id", "subscription_id", "created_at" DESC);
--
-- Concurrently create index sub_pulse_art_pub_unknown_idx on field(s) updated_at of model artifact
--
SET lock_timeout = 0;
SET statement_timeout = 0;
CREATE INDEX CONCURRENTLY "sub_pulse_art_pub_unknown_idx" ON "subscriptions_artifact" ("updated_at") WHERE ("kind" = 'draft_pr' AND "status" = 'publication_unknown');

products/tasks/backend/migrations/0108_taskstagedruntransition_taskpublicationlease_and_more.py

BEGIN;
--
-- Create model TaskStagedRunTransition
--
CREATE TABLE "posthog_task_staged_run_transition" ("id" uuid NOT NULL PRIMARY KEY, "caller_id" uuid NOT NULL, "source_workspace_snapshot_ref" varchar(512) NOT NULL, "requested_capability_manifest" jsonb NOT NULL, "status" varchar(16) NOT NULL, "idempotency_key" varchar(255) NOT NULL, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "source_task_run_id" uuid NOT NULL, "successor_task_run_id" uuid NULL, "task_id" uuid NOT NULL, "team_id" integer NOT NULL);
--
-- Create model TaskPublicationLease
--
CREATE TABLE "posthog_task_publication_lease" ("id" uuid NOT NULL PRIMARY KEY, "caller_id" uuid NOT NULL, "logical_artifact_key" varchar(255) NOT NULL, "idempotency_key" varchar(255) NOT NULL, "repository" varchar(255) NOT NULL, "base_sha" varchar(64) NOT NULL, "base_branch" varchar(255) NULL, "commit_message" varchar(500) NULL, "pr_title" varchar(256) NULL, "pr_body" text NULL, "commit_author_name" varchar(160) NULL, "commit_author_email" varchar(254) NULL, "commit_timestamp" bigint NULL, "head_branch" varchar(255) NOT NULL, "expected_github_app_slug" varchar(255) NULL, "expected_github_app_login" varchar(255) NULL, "github_integration_id" bigint NULL, "github_installation_id" varchar(255) NULL, "action_key" varchar(255) NOT NULL, "grant_version" varchar(128) NOT NULL, "publication_mode" varchar(16) NOT NULL, "status" varchar(16) NOT NULL, "starts_before" timestamp with time zone NOT NULL, "expires_at" timestamp with time zone NOT NULL, "consumed_at" timestamp with time zone NULL, "revoked_at" timestamp with time zone NULL, "expired_at" timestamp with time zone NULL, "finalized_at" timestamp with time zone NULL, "final_artifact_ref" varchar(512) NULL, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "task_id" uuid NOT NULL, "task_run_id" uuid NOT NULL, "team_id" integer NOT NULL, "staged_run_transition_id" uuid NOT NULL);
--
-- Create constraint task_staged_transition_task_uniq on model taskstagedruntransition
--
ALTER TABLE "posthog_task_staged_run_transition" ADD CONSTRAINT "task_staged_transition_task_uniq" UNIQUE ("task_id");
--
-- Create constraint task_staged_transition_source_uniq on model taskstagedruntransition
--
ALTER TABLE "posthog_task_staged_run_transition" ADD CONSTRAINT "task_staged_transition_source_uniq" UNIQUE ("source_task_run_id");
--
-- Create constraint task_staged_transition_successor_uniq on model taskstagedruntransition
--
CREATE UNIQUE INDEX "task_staged_transition_successor_uniq" ON "posthog_task_staged_run_transition" ("successor_task_run_id") WHERE "successor_task_run_id" IS NOT NULL;
--
-- Create constraint task_staged_transition_caller_idemp_uniq on model taskstagedruntransition
--
ALTER TABLE "posthog_task_staged_run_transition" ADD CONSTRAINT "task_staged_transition_caller_idemp_uniq" UNIQUE ("team_id", "caller_id", "idempotency_key");
--
-- Create constraint task_staged_transition_snapshot_present on model taskstagedruntransition
--
ALTER TABLE "posthog_task_staged_run_transition" ADD CONSTRAINT "task_staged_transition_snapshot_present" CHECK ("source_workspace_snapshot_ref" > '');
--
-- Create constraint task_staged_transition_distinct_runs on model taskstagedruntransition
--
ALTER TABLE "posthog_task_staged_run_transition" ADD CONSTRAINT "task_staged_transition_distinct_runs" CHECK (("successor_task_run_id" IS NULL OR NOT ("source_task_run_id" = ("successor_task_run_id") AND "successor_task_run_id" IS NOT NULL)));
--
-- Create index task_pub_lease_active_exp_idx on field(s) expires_at of model taskpublicationlease
--
CREATE INDEX "task_pub_lease_active_exp_idx" ON "posthog_task_publication_lease" ("expires_at") WHERE "status" = 'active';
--
-- Create constraint task_pub_lease_idempotency_uniq on model taskpublicationlease
--
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "task_pub_lease_idempotency_uniq" UNIQUE ("team_id", "idempotency_key");
--
-- Create constraint task_pub_lease_transition_uniq on model taskpublicationlease
--
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "task_pub_lease_transition_uniq" UNIQUE ("staged_run_transition_id");
--
-- Create constraint task_pub_lease_live_artifact_uniq on model taskpublicationlease
--
CREATE UNIQUE INDEX "task_pub_lease_live_artifact_uniq" ON "posthog_task_publication_lease" ("team_id", "logical_artifact_key") WHERE "status" IN ('active', 'finalized');
--
-- Create constraint task_pub_lease_draft_only on model taskpublicationlease
--
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "task_pub_lease_draft_only" CHECK ("publication_mode" = 'draft');
--
-- Create constraint task_pub_lease_start_after_create on model taskpublicationlease
--
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "task_pub_lease_start_after_create" CHECK ("starts_before" > ("created_at"));
--
-- Create constraint task_pub_lease_expiry_after_start on model taskpublicationlease
--
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "task_pub_lease_expiry_after_start" CHECK ("expires_at" > ("starts_before"));
--
-- Create constraint task_pub_lease_expiry_after_create on model taskpublicationlease
--
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "task_pub_lease_expiry_after_create" CHECK ("expires_at" > ("created_at"));
--
-- Create constraint task_pub_lease_github_binding_paired on model taskpublicationlease
--
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "task_pub_lease_github_binding_paired" CHECK ((("github_installation_id" IS NOT NULL AND "github_integration_id" IS NOT NULL) OR ("github_installation_id" IS NULL AND "github_integration_id" IS NULL)));
--
-- Create constraint task_pub_lease_state_valid on model taskpublicationlease
--
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "task_pub_lease_state_valid" CHECK ((("consumed_at" IS NULL AND "expired_at" IS NULL AND "final_artifact_ref" IS NULL AND "finalized_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'active') OR ("consumed_at" IS NOT NULL AND "expired_at" IS NULL AND "final_artifact_ref" IS NULL AND "finalized_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'consumed') OR ("consumed_at" IS NULL AND "expired_at" IS NULL AND "final_artifact_ref" IS NULL AND "finalized_at" IS NULL AND "revoked_at" IS NOT NULL AND "status" = 'revoked') OR ("consumed_at" IS NULL AND "expired_at" IS NOT NULL AND "final_artifact_ref" IS NULL AND "finalized_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'expired') OR ("consumed_at" IS NOT NULL AND "expired_at" IS NULL AND "final_artifact_ref" IS NOT NULL AND "finalized_at" IS NOT NULL AND "revoked_at" IS NULL AND "status" = 'finalized')));
CREATE INDEX "posthog_task_staged_run_transition_source_task_run_id_9ab6ced3" ON "posthog_task_staged_run_transition" ("source_task_run_id");
CREATE INDEX "posthog_task_staged_run_tr_successor_task_run_id_3ff96545" ON "posthog_task_staged_run_transition" ("successor_task_run_id");
CREATE INDEX "posthog_task_staged_run_transition_task_id_cf1b272f" ON "posthog_task_staged_run_transition" ("task_id");
CREATE INDEX "posthog_task_staged_run_transition_team_id_2661170c" ON "posthog_task_staged_run_transition" ("team_id");
ALTER TABLE "posthog_task_publication_lease" ADD CONSTRAINT "posthog_task_publica_staged_run_transitio_a9cd81f3_fk_posthog_t" FOREIGN KEY ("staged_run_transition_id") REFERENCES "posthog_task_staged_run_transition" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "posthog_task_publication_lease_task_id_2ce98cdf" ON "posthog_task_publication_lease" ("task_id");
CREATE INDEX "posthog_task_publication_lease_task_run_id_f94f5c34" ON "posthog_task_publication_lease" ("task_run_id");
CREATE INDEX "posthog_task_publication_lease_team_id_f1d1f1df" ON "posthog_task_publication_lease" ("team_id");
CREATE INDEX "posthog_task_publication_l_staged_run_transition_id_a9cd81f3" ON "posthog_task_publication_lease" ("staged_run_transition_id");
COMMIT;

products/tasks/backend/migrations/0109_task_draft_publication_broker.py

BEGIN;
--
-- Create model TaskDraftPublication
--
CREATE TABLE "posthog_task_draft_publication" ("id" uuid NOT NULL PRIMARY KEY, "repository" varchar(255) NOT NULL, "base_sha" varchar(64) NOT NULL, "base_branch" varchar(255) NOT NULL, "commit_message" varchar(500) NOT NULL, "pr_title" varchar(256) NOT NULL, "pr_body" text NOT NULL, "commit_author_name" varchar(160) NOT NULL, "commit_author_email" varchar(254) NOT NULL, "commit_timestamp" bigint NOT NULL, "branch" varchar(255) NOT NULL, "expected_github_app_slug" varchar(255) NOT NULL, "expected_github_app_login" varchar(255) NOT NULL, "status" varchar(32) NOT NULL, "bundle_storage_path" varchar(512) NULL, "bundle_head_sha" varchar(64) NULL, "bundle_sha256" varchar(64) NULL, "bundle_byte_count" integer NULL CHECK ("bundle_byte_count" >= 0), "github_commit_sha" varchar(64) NULL, "pr_number" integer NULL CHECK ("pr_number" >= 0), "pr_url" varchar(512) NULL, "is_draft" boolean NOT NULL, "reserved_at" timestamp with time zone NOT NULL, "uploaded_at" timestamp with time zone NULL, "commit_created_at" timestamp with time zone NULL, "branch_created_at" timestamp with time zone NULL, "pr_creation_started_at" timestamp with time zone NULL, "published_at" timestamp with time zone NULL, "finalized_at" timestamp with time zone NULL, "reconciled_at" timestamp with time zone NULL, "blocked_at" timestamp with time zone NULL, "publication_unknown_at" timestamp with time zone NULL, "revoked_at" timestamp with time zone NULL, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "lease_id" uuid NOT NULL UNIQUE, "team_id" integer NOT NULL);
--
-- Create constraint task_draft_publication_draft_only on model taskdraftpublication
--
ALTER TABLE "posthog_task_draft_publication" ADD CONSTRAINT "task_draft_publication_draft_only" CHECK ("is_draft");
--
-- Create constraint task_draft_publication_server_branch on model taskdraftpublication
--
ALTER TABLE "posthog_task_draft_publication" ADD CONSTRAINT "task_draft_publication_server_branch" CHECK ("branch"::text ~ '^codex/[0-9a-f]{32}$');
--
-- Create constraint task_draft_publication_state_valid on model taskdraftpublication
--
ALTER TABLE "posthog_task_draft_publication" ADD CONSTRAINT "task_draft_publication_state_valid" CHECK ((("blocked_at" IS NULL AND "branch_created_at" IS NULL AND "bundle_byte_count" IS NULL AND "bundle_head_sha" IS NULL AND "bundle_sha256" IS NULL AND "bundle_storage_path" IS NULL AND "commit_created_at" IS NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NULL AND "pr_creation_started_at" IS NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NULL AND "reconciled_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'reserved' AND "uploaded_at" IS NULL) OR ("blocked_at" IS NULL AND "branch_created_at" IS NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NULL AND "pr_creation_started_at" IS NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NULL AND "reconciled_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'uploaded' AND "uploaded_at" IS NOT NULL) OR ("blocked_at" IS NULL AND "branch_created_at" IS NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NOT NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NOT NULL AND "pr_creation_started_at" IS NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'commit_created' AND "uploaded_at" IS NOT NULL) OR ("blocked_at" IS NULL AND "branch_created_at" IS NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NOT NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NOT NULL AND "pr_creation_started_at" IS NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'branch_creating' AND "uploaded_at" IS NOT NULL) OR ("blocked_at" IS NULL AND "branch_created_at" IS NOT NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NOT NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NOT NULL AND "pr_creation_started_at" IS NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'branch_created' AND "uploaded_at" IS NOT NULL) OR ("blocked_at" IS NULL AND "branch_created_at" IS NOT NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NOT NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NOT NULL AND "pr_creation_started_at" IS NOT NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'pr_creating' AND "uploaded_at" IS NOT NULL) OR ("blocked_at" IS NULL AND "branch_created_at" IS NOT NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NOT NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NOT NULL AND "pr_creation_started_at" IS NOT NULL AND "pr_number" IS NOT NULL AND "pr_url" IS NOT NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NOT NULL AND "revoked_at" IS NULL AND "status" = 'published' AND "uploaded_at" IS NOT NULL) OR ("blocked_at" IS NULL AND "branch_created_at" IS NOT NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NOT NULL AND "finalized_at" IS NOT NULL AND "github_commit_sha" IS NOT NULL AND "pr_creation_started_at" IS NOT NULL AND "pr_number" IS NOT NULL AND "pr_url" IS NOT NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NOT NULL AND "revoked_at" IS NULL AND "status" = 'finalized' AND "uploaded_at" IS NOT NULL) OR ("blocked_at" IS NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NOT NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NOT NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "publication_unknown_at" IS NOT NULL AND "published_at" IS NULL AND "reconciled_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'publication_unknown' AND "uploaded_at" IS NOT NULL) OR ("blocked_at" IS NOT NULL AND "branch_created_at" IS NULL AND "bundle_byte_count" IS NOT NULL AND "bundle_head_sha" IS NOT NULL AND "bundle_sha256" IS NOT NULL AND "bundle_storage_path" IS NOT NULL AND "commit_created_at" IS NULL AND "finalized_at" IS NULL AND "github_commit_sha" IS NULL AND "pr_creation_started_at" IS NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "publication_unknown_at" IS NULL AND "published_at" IS NULL AND "revoked_at" IS NULL AND "status" = 'blocked' AND "uploaded_at" IS NOT NULL) OR ("finalized_at" IS NULL AND "pr_number" IS NULL AND "pr_url" IS NULL AND "published_at" IS NULL AND "revoked_at" IS NOT NULL AND "status" = 'revoked')));
--
-- Create constraint task_draft_publication_branch_uniq on model taskdraftpublication
--
ALTER TABLE "posthog_task_draft_publication" ADD CONSTRAINT "task_draft_publication_branch_uniq" UNIQUE ("team_id", "branch");
ALTER TABLE "posthog_task_draft_publication" ADD CONSTRAINT "posthog_task_draft_p_lease_id_16ed6eb3_fk_posthog_t" FOREIGN KEY ("lease_id") REFERENCES "posthog_task_publication_lease" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "posthog_task_draft_publication_team_id_a7c0fd48" ON "posthog_task_draft_publication" ("team_id");
COMMIT;

products/tasks/backend/migrations/0110_publication_gate_ledger.py

BEGIN;
--
-- Create model TaskPublicationGatePolicy
--
CREATE TABLE "posthog_task_publication_gate_policy" ("id" uuid NOT NULL PRIMARY KEY, "repository" varchar(255) NOT NULL, "base_sha" varchar(64) NOT NULL, "source_path" varchar(512) NULL, "source_sha256" varchar(64) NULL, "required_gates" jsonb NOT NULL, "status" varchar(16) NOT NULL, "reason" varchar(128) NULL, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "lease_id" uuid NOT NULL UNIQUE, "team_id" integer NOT NULL);
--
-- Create model TaskPublicationGateLedger
--
CREATE TABLE "posthog_task_publication_gate_ledger" ("id" uuid NOT NULL PRIMARY KEY, "gate_key" varchar(64) NOT NULL, "label" varchar(80) NOT NULL, "argv" jsonb NOT NULL, "status" varchar(16) NOT NULL, "exit_code" integer NULL, "output_storage_path" varchar(512) NULL, "output_sha256" varchar(64) NULL, "output_byte_count" integer NULL CHECK ("output_byte_count" >= 0), "attempts" integer NOT NULL CHECK ("attempts" >= 0), "started_at" timestamp with time zone NULL, "completed_at" timestamp with time zone NULL, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "lease_id" uuid NOT NULL, "team_id" integer NOT NULL, "policy_id" uuid NOT NULL);
--
-- Create constraint task_pub_gate_policy_state_valid on model taskpublicationgatepolicy
--
ALTER TABLE "posthog_task_publication_gate_policy" ADD CONSTRAINT "task_pub_gate_policy_state_valid" CHECK ((("reason" IS NULL AND "source_path" IS NOT NULL AND "source_sha256" IS NOT NULL AND "status" = 'ready') OR ("reason" = 'gate_policy_unavailable' AND "source_path" IS NULL AND "source_sha256" IS NULL AND "status" = 'unavailable')));
--
-- Create index task_pub_gate_lease_state on field(s) team, lease, status of model taskpublicationgateledger
--
CREATE INDEX "task_pub_gate_lease_state" ON "posthog_task_publication_gate_ledger" ("team_id", "lease_id", "status");
--
-- Create constraint task_pub_gate_ledger_policy_key_uniq on model taskpublicationgateledger
--
ALTER TABLE "posthog_task_publication_gate_ledger" ADD CONSTRAINT "task_pub_gate_ledger_policy_key_uniq" UNIQUE ("policy_id", "gate_key");
ALTER TABLE "posthog_task_publication_gate_policy" ADD CONSTRAINT "posthog_task_publica_lease_id_19653485_fk_posthog_t" FOREIGN KEY ("lease_id") REFERENCES "posthog_task_publication_lease" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "posthog_task_publication_gate_policy_team_id_d1db1279" ON "posthog_task_publication_gate_policy" ("team_id");
ALTER TABLE "posthog_task_publication_gate_ledger" ADD CONSTRAINT "posthog_task_publica_lease_id_2449e4d0_fk_posthog_t" FOREIGN KEY ("lease_id") REFERENCES "posthog_task_publication_lease" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "posthog_task_publication_gate_ledger" ADD CONSTRAINT "posthog_task_publica_policy_id_d47bf74e_fk_posthog_t" FOREIGN KEY ("policy_id") REFERENCES "posthog_task_publication_gate_policy" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "posthog_task_publication_gate_ledger_lease_id_2449e4d0" ON "posthog_task_publication_gate_ledger" ("lease_id");
CREATE INDEX "posthog_task_publication_gate_ledger_team_id_142df666" ON "posthog_task_publication_gate_ledger" ("team_id");
CREATE INDEX "posthog_task_publication_gate_ledger_policy_id_d47bf74e" ON "posthog_task_publication_gate_ledger" ("policy_id");
COMMIT;

Last updated: 2026-08-31 06:13 UTC (4699540)

Django migration risk — migration analysis complete

We've analyzed your migrations for potential risks.

Summary: 7 Safe | 3 Needs Review | 0 Blocked

⚠️ Needs Review

May have performance impact

exports.0009_subscription_context
  └─ #1 ⚠️ AddField
     Adding NOT NULL field with callable default (list) - verify it's stable
     model: subscriptiondelivery, field: context_dashboard_ids, default: list
  └─ #2 ⚠️ AddField
     Adding NOT NULL field with callable default (list) - verify it's stable
     model: subscriptiondelivery, field: context_insight_ids, default: list
  └─ #3 ✅ CreateModel
     Creating new table is safe
     model: SubscriptionContext
  └─ #4 ⚠️ AddForeignKeyNotValid: Unknown operation type: AddForeignKeyNotValid
  │
  └──> ℹ️  INFO:
       ℹ️  Skipped operations on newly created tables (empty tables
       don't cause lock contention).
subscriptions.0003_pulserun_orchestration_state
  └─ #1 ✅ AddField
     Adding nullable field requires brief lock
     model: pulserun, field: cancellation_requested_at
  └─ #2 ✅ AddField
     Adding nullable field requires brief lock
     model: pulserun, field: finalization_deadline_at
  └─ #3 ✅ AddField
     Adding nullable field requires brief lock
     model: pulserun, field: wall_clock_deadline_at
  └─ #4 ✅ AddField
     Adding nullable field requires brief lock
     model: pulserun, field: skip_reason
  └─ #5 ✅ AddField
     Adding nullable field requires brief lock
     model: artifact, field: publication_lease_id
  └─ #6 ⚠️ AlterField
     Field alteration may cause table locks or data loss (check if changing type or constraints)
     model: pulserun, field: status, field_type: CharField
  └─ #7 ⚠️ AlterField
     Field alteration may cause table locks or data loss (check if changing type or constraints)
     model: actionproposal, field: kind, field_type: CharField
  └─ #8 ⚠️ AlterField
     Field alteration may cause table locks or data loss (check if changing type or constraints)
     model: runaction, field: kind, field_type: CharField
subscriptions.0005_outcome_observation_immutability
  └─ #1 ⚠️ RunSQL
     RunSQL with UPDATE/DELETE - developer override applied for small table
     override_reason: This defines a trigger; BEFORE UPDATE is event syntax and performs no data mutation.

✅ Safe

Brief or no lock, backwards compatible

subscriptions.0001_initial
  └─ #1 ✅ CreateModel
     Creating new table is safe
     model: Opportunity
  └─ #2 ✅ CreateModel
     Creating new table is safe
     model: ActionProposal
  └─ #3 ✅ CreateModel
     Creating new table is safe
     model: PublicResearchSubject
  └─ #4 ✅ CreateModel
     Creating new table is safe
     model: ProactiveSubscriptionConfig
  └─ #5 ✅ CreateModel
     Creating new table is safe
     model: PulseRun
  └─ #6 ✅ CreateModel
     Creating new table is safe
     model: EvidenceToolCall
  └─ #7 ✅ CreateModel
     Creating new table is safe
     model: EvidenceSet
  └─ #8 ✅ CreateModel
     Creating new table is safe
     model: DeliveryLedger
  └─ #9 ✅ CreateModel
     Creating new table is safe
     model: RepositoryGrant
  └─ #10 ✅ AddField
     Adding nullable field requires brief lock
     model: proactivesubscriptionconfig, field: repository_grant
  └─ #11 ✅ CreateModel
     Creating new table is safe
     model: RunAction
  └─ #12 ✅ CreateModel
     Creating new table is safe
     model: Artifact
  │
  └──> ℹ️  INFO:
       ℹ️  Skipped operations on newly created tables (empty tables
       don't cause lock contention).
subscriptions.0002_evidencerawbody
  └─ #1 ✅ CreateModel
     Creating new table is safe
     model: EvidenceRawBody
  │
  └──> ℹ️  INFO:
       ℹ️  Skipped operations on newly created tables (empty tables
       don't cause lock contention).
subscriptions.0004_pulse_outcome_loop
  └─ #1 ✅ AddField
     Adding nullable field requires brief lock
     model: runaction, field: expected_change_lower
  └─ #2 ✅ AddField
     Adding nullable field requires brief lock
     model: runaction, field: expected_change_type
  └─ #3 ✅ AddField
     Adding nullable field requires brief lock
     model: runaction, field: expected_change_upper
  └─ #4 ✅ AddField
     Adding nullable field requires brief lock
     model: runaction, field: metric_direction
  └─ #5 ✅ AddField
     Adding nullable field requires brief lock
     model: runaction, field: metric_name
  └─ #6 ✅ AddField
     Adding nullable field requires brief lock
     model: runaction, field: metric_unit
  └─ #7 ✅ AddField
     Adding nullable field requires brief lock
     model: runaction, field: readout_after_days
  └─ #8 ✅ AddField
     Adding nullable field requires brief lock
     model: runaction, field: why_now
  └─ #9 ✅ CreateModel
     Creating new table is safe
     model: OutcomePlan
  └─ #10 ✅ CreateModel
     Creating new table is safe
     model: OutcomeObservation
  └─ #11 ✅ AddConstraintNotValid
     ADD CONSTRAINT ... NOT VALID is safe (validates new rows only, no table scan)
     model: runaction, constraint: sub_pulse_action_readout_after_days_valid
  │
  └──> ℹ️  INFO:
       ℹ️  Skipped operations on newly created tables (empty tables
       don't cause lock contention).
subscriptions.0006_pulse_run_history_and_artifact_publication_indexes
  └─ #1 ✅ SafeAddIndexConcurrently
     PostHog concurrent-index helper: idempotent (timeout disabling + invalid-leftover recovery)
     model: pulserun, index: sub_pulse_run_history_idx
  └─ #2 ✅ SafeAddIndexConcurrently
     PostHog concurrent-index helper: idempotent (timeout disabling + invalid-leftover recovery)
     model: artifact, index: sub_pulse_art_pub_unknown_idx
tasks.0108_taskstagedruntransition_taskpublicationlease_and_more
  └─ #1 ✅ CreateModel
     Creating new table is safe
     model: TaskStagedRunTransition
  └─ #2 ✅ CreateModel
     Creating new table is safe
     model: TaskPublicationLease
  │
  └──> ℹ️  INFO:
       ℹ️  Skipped operations on newly created tables (empty tables
       don't cause lock contention).
tasks.0109_task_draft_publication_broker
  └─ #1 ✅ CreateModel
     Creating new table is safe
     model: TaskDraftPublication
  │
  └──> ℹ️  INFO:
       ℹ️  Skipped operations on newly created tables (empty tables
       don't cause lock contention).
tasks.0110_publication_gate_ledger
  └─ #1 ✅ CreateModel
     Creating new table is safe
     model: TaskPublicationGatePolicy
  └─ #2 ✅ CreateModel
     Creating new table is safe
     model: TaskPublicationGateLedger
  │
  └──> ℹ️  INFO:
       ℹ️  Skipped operations on newly created tables (empty tables
       don't cause lock contention).

📚 How to Deploy These Changes Safely

AddConstraintNotValid:

Follow up with ValidateConstraint in a later migration to check existing rows.

AddField:

This operation acquires a brief lock but doesn't rewrite the table.

Deployment uses lock timeouts with automatic retries, so lock contention will cause retries rather than connection pile-up.

RunSQL:

Developer override applied:
Justification: This defines a trigger; BEFORE UPDATE is event syntax and performs no data mutation.

Reviewer checklist:

  • Verify table is actually small (<1000 rows typical)
  • Confirm justification is valid
  • Check no indexes will cause lock contention
  • Ensure WHERE clause limits scope appropriately

If this override is incorrect, request batching:

  • Batch size: 1,000-10,000 rows per batch
  • Add pauses between batches
  • Use WHERE clauses to limit scope
  • Consider background jobs for very large updates (millions of rows)

See the migration safety guide

Last updated: 2026-08-31 06:13 UTC (4699540)

ℹ️ Docs preview — preview build triggered

Docs from this PR will be published at posthog.com.

Project Preview Updated (UTC)
posthog.com Open preview Aug 31, 2026, 6:08 AM

The preview should be ready in about 10 minutes. Open the preview at /handbook/engineering/.

@github-actions

Copy link
Copy Markdown
Contributor

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit b80b8b7.

@posthog

posthog Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

👋 Visual changes detected for this PR.

Review and approve in PostHog Visual Review

If these changes are unexpected, they may be caused by a flaky test or a broken snapshot on master. Don't approve — rerun the job or wait for a fix.

@vdekrijger
vdekrijger force-pushed the codex/proactive-prompt-subscriptions-core-v1 branch from b80b8b7 to 6f3d326 Compare August 31, 2026 00:45
@trunk-io

trunk-io Bot commented Aug 31, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@vdekrijger
vdekrijger force-pushed the codex/proactive-prompt-subscriptions-core-v1 branch from 6f3d326 to 010a6f9 Compare August 31, 2026 00:51
@vdekrijger
vdekrijger force-pushed the codex/proactive-prompt-subscriptions-core-v1 branch from 010a6f9 to 933f103 Compare August 31, 2026 01:01
@vdekrijger
vdekrijger force-pushed the codex/proactive-prompt-subscriptions-core-v1 branch from 933f103 to ba398a1 Compare August 31, 2026 01:34
@vdekrijger
vdekrijger force-pushed the codex/proactive-prompt-subscriptions-core-v1 branch 4 times, most recently from 7b75461 to 98af841 Compare August 31, 2026 03:36
posthog Bot and others added 2 commits August 31, 2026 06:01
6 updated
Run: 1591aea0-0d04-47bf-8fa0-6ae574198dfd

Co-authored-by: vdekrijger <66254686+vdekrijger@users.noreply.github.com>
@vdekrijger
vdekrijger force-pushed the codex/proactive-prompt-subscriptions-core-v1 branch from 98af841 to 4699540 Compare August 31, 2026 06:08
@scheduled-actions-posthog

Copy link
Copy Markdown
Contributor

This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the stale label – otherwise this will be closed in another week. If you want to permanently keep it open, use the waiting label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant