Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
211 commits
Select commit Hold shift + click to select a range
0b9019d
v0.6.23: MCP fixes, remove local state in favor of server state, moth…
waleedlatif1 Apr 4, 2026
a54dcbe
v0.6.24: copilot feedback wiring, captcha fixes
waleedlatif1 Apr 4, 2026
28af223
v0.6.25: cloudwatch, cloudformation, live kb sync, linear fixes, post…
waleedlatif1 Apr 5, 2026
d889f32
v0.6.26: ui improvements, multiple response blocks, docx previews, ol…
waleedlatif1 Apr 5, 2026
316bc8c
v0.6.27: new triggers, mothership improvements, files archive, queuei…
waleedlatif1 Apr 7, 2026
3f508e4
v0.6.28: new docs, delete confirmation standardization, dagster integ…
waleedlatif1 Apr 7, 2026
d6ec115
v0.6.29: login improvements, posthog telemetry (#4026)
TheodoreSpeaks Apr 7, 2026
d7da35b
v0.6.30: slack trigger enhancements, connectors performance improveme…
waleedlatif1 Apr 8, 2026
cf233bb
v0.6.31: elevenlabs voice, trigger.dev fixes, cloud whitelabeling for…
waleedlatif1 Apr 8, 2026
f8f3758
v0.6.32: BYOK fixes, ui improvements, cloudwatch tools, jsm tools ext…
waleedlatif1 Apr 9, 2026
3c8bb40
v0.6.33: polling improvements, jsm forms tools, credentials reactquer…
waleedlatif1 Apr 9, 2026
d33acf4
v0.6.34: trigger.dev fixes, CI speedup, atlassian error extractor
waleedlatif1 Apr 9, 2026
4f40c4c
v0.6.35: additional jira fields, HITL docs, logs cleanup efficiency
waleedlatif1 Apr 10, 2026
cbfab1c
v0.6.36: new chunkers, sockets state machine, google sheets/drive/cal…
waleedlatif1 Apr 11, 2026
4309d06
v0.6.37: audit logs page, isolated-vm worker rotation, permission gro…
waleedlatif1 Apr 12, 2026
8b57476
v0.6.38: models page
waleedlatif1 Apr 12, 2026
e3d0e74
v0.6.39: billing fixes, tools audit, landing fix
waleedlatif1 Apr 13, 2026
0ac0539
v0.6.40: mothership tool loop, new skills, agiloft, STS, IAM integrat…
waleedlatif1 Apr 14, 2026
3838b6e
v0.6.41: webhooks fix, workers removal
waleedlatif1 Apr 14, 2026
fc07922
v0.6.42: mothership nested file reads, search modal improvements
waleedlatif1 Apr 14, 2026
3a1b1a8
v0.6.43: mothership billing idempotency, env var resolution fixes
waleedlatif1 Apr 14, 2026
46ffc49
v0.6.44: streamdown, mothership intelligence, excel extension
waleedlatif1 Apr 15, 2026
010435c
v0.6.45: superagent, csp, brightdata integration, gemini response for…
Sg312 Apr 15, 2026
c0bc62c
Merge pull request #4190 from simstudioai/staging
icecrasher321 Apr 16, 2026
387cc97
v0.6.46: mothership queueing, web vitals
waleedlatif1 Apr 16, 2026
2dbc7fd
v0.6.47: files focusing, documentation, opus 4.7
waleedlatif1 Apr 16, 2026
8a50f18
v0.6.48: import csv into tables, subflow fixes, CSP updates
waleedlatif1 Apr 16, 2026
dcf3302
v0.6.49: deploy sockets event, resolver, logs improvements, monday.co…
waleedlatif1 Apr 17, 2026
bc09865
v0.6.50: ppt/doc/pdf worker isolation, docs, chat, sidebar improvements
icecrasher321 Apr 18, 2026
5f56e46
v0.6.51: tables improvements, billing fixes, 404 pages, code hygiene
waleedlatif1 Apr 20, 2026
ca3bbf1
v0.6.52: data retention, docs updates, slack manifest generator, secu…
waleedlatif1 Apr 22, 2026
bbf400f
v0.6.53: permissions groups migration, docs updates
waleedlatif1 Apr 22, 2026
7c619e7
Merge pull request #4261 from simstudioai/staging
icecrasher321 Apr 22, 2026
64cfda5
v0.6.54: mothership tracing, db pool size increase
icecrasher321 Apr 22, 2026
7ca736a
v0.6.55: standardize monorepo conventions, api key hash, thinking tex…
waleedlatif1 Apr 23, 2026
6066fc1
v0.6.56: data retention improvements, tables column double click resi…
waleedlatif1 Apr 24, 2026
3422f64
Merge pull request #4285 from simstudioai/staging
waleedlatif1 Apr 24, 2026
595c4c3
Merge pull request #4293 from simstudioai/staging
TheodoreSpeaks Apr 24, 2026
d6c1bc2
v0.6.58: queue abort state machine improvement, contributing guide
icecrasher321 Apr 25, 2026
58a3ae2
v0.6.59: gpt 5.5, security hardening, parallel subagents rendering
icecrasher321 Apr 27, 2026
489f2d3
v0.6.60: copilot security improvements, slack canvas ops, retention j…
icecrasher321 Apr 27, 2026
6aa3fe3
v0.6.61: SAP integration, live URLs for browser use, 5xx error catego…
icecrasher321 Apr 29, 2026
ecbf5e5
Merge pull request #4342 from simstudioai/staging
TheodoreSpeaks Apr 29, 2026
2aaf2b7
v0.6.62: firecrawl parse, new gmail tools, trace improvements, tool f…
waleedlatif1 May 2, 2026
d445b9c
v0.6.63: knowledgebase UI, folder search in mothership
waleedlatif1 May 2, 2026
4bc6a17
v0.6.64: table limits env vars, workspace files improvements, integra…
waleedlatif1 May 3, 2026
5be12f8
v0.6.65: memory fix, image uploads in files
waleedlatif1 May 3, 2026
4253e57
v0.6.66: child trace spans, reranker controls, attachment previews, l…
waleedlatif1 May 5, 2026
8d6b615
v0.6.67: VFS upload fix, posthog/copilot correlation, exa date filter…
TheodoreSpeaks May 5, 2026
efcd51a
v0.6.68: atlassian service accounts, 30 day wait block, markdown rend…
waleedlatif1 May 6, 2026
8d934f3
v0.6.69: security hardening, nextjs upgrade, SAP Concur, Emailbison i…
waleedlatif1 May 7, 2026
5ea80a8
v0.6.70: legacy workflow sanitization
icecrasher321 May 7, 2026
3cc581e
v0.6.71: build error fix
icecrasher321 May 7, 2026
273e608
Merge pull request #4496 from simstudioai/staging
TheodoreSpeaks May 7, 2026
07b8f1b
v0.6.72: tables improvements, search and replace, logs with files, im…
waleedlatif1 May 9, 2026
dcaf3e9
v0.6.73: zustand v5 migration fix
icecrasher321 May 9, 2026
6aeb981
v0.6.74: security hardening, workers recycling, next-mdx-remote and o…
waleedlatif1 May 12, 2026
3e9849b
v0.6.75: scheduler claim-budget drain, helm chart hardening, mothersh…
TheodoreSpeaks May 12, 2026
64d855a
v0.6.76: helm updates, media centering, lazy loading, security hardening
waleedlatif1 May 13, 2026
ab156b5
v0.6.77: mothership improvements, trigger.dev telemetry
icecrasher321 May 14, 2026
c09a2c9
v0.6.78: file block get
Sg312 May 14, 2026
6a5eebc
v0.6.79: rate limits, tables checkboxes, drizzle config changes, bill…
waleedlatif1 May 14, 2026
4efe999
v0.6.80: security hardening, nextjs minor version bump, cloudwatch to…
waleedlatif1 May 15, 2026
f69a9a0
v0.6.81: files in agent block, file block update, mermaid version upd…
waleedlatif1 May 16, 2026
db7f1c1
v0.6.82: fix duplicate migration
Sg312 May 16, 2026
dbe8e51
v0.6.83: redis TLS SNI override for IP-based REDIS_URL, zod schema fixes
TheodoreSpeaks May 17, 2026
11bcb8f
v0.6.84: redis pub/sub SNI override, security hardening, copilot read…
TheodoreSpeaks May 17, 2026
d14af04
v0.6.85: mothership stream, resource column spacing, prospeo, findyma…
waleedlatif1 May 19, 2026
e6b3cce
v0.6.86: gemini 3.5 flash, wiza integration, CORS cleanup, railway an…
waleedlatif1 May 20, 2026
97a609a
v0.6.86: CORS updates, OAuth MCP, navigation pinning dynamic pages, g…
waleedlatif1 May 21, 2026
fde70e2
v0.6.87: performance improvements
icecrasher321 May 21, 2026
e9ee351
v0.6.88: mutex lock on oauth refresh, files export fix, hubspot trigg…
waleedlatif1 May 22, 2026
b5b2d83
v0.6.89: connectors ui, perf improvements, mcp hardening, og image
waleedlatif1 May 23, 2026
f6c9998
v0.6.90: resource breadcrumb flash fix, dedupe external URL fetches, …
icecrasher321 May 23, 2026
e532e0a
v0.6.91: file zoom, Zoom KB connector, error classifications, LiteLLM…
waleedlatif1 May 26, 2026
fd19470
v0.6.92: enrichment table column type, table run fixes, scheduled jit…
TheodoreSpeaks May 27, 2026
856182b
v0.6.93: schedules/mcp performance improvements, integration bugfixes
icecrasher321 May 28, 2026
6bf9e96
v0.6.94: 4.8 opus, better auth upgrade, zoominfo integration, copilot…
waleedlatif1 May 29, 2026
503432c
v0.6.95: data enrichment block, nullable workflow description fix
TheodoreSpeaks May 29, 2026
a8dcdd5
v0.6.96: pinned table columns, sequence number in copilot messages, t…
waleedlatif1 May 30, 2026
2f1f633
v0.6.97: migration fix for copilot_messages
icecrasher321 May 30, 2026
e32699d
v0.6.98: redundant index, security hardening, new copilot messages ta…
waleedlatif1 May 31, 2026
12ada0c
v0.6.99: tables filter operators, copilot chat persistence consolidat…
waleedlatif1 Jun 2, 2026
e8f09ae
v0.6.100: auth, mothership, scopes improvements, new apify tools
icecrasher321 Jun 2, 2026
3ba8668
v0.6.101: 11 new knowledgebase connectors, slack scopes update, login…
waleedlatif1 Jun 3, 2026
1192e20
v0.6.102: support S3-compatible in object storage, GitLab code knowle…
waleedlatif1 Jun 3, 2026
1ce8e92
v0.6.103: readme updates, tables lifecycle improvements, new connecto…
waleedlatif1 Jun 6, 2026
0c2df1e
v0.7.0: vibes improvement, new UI, new tools, chat-first, mothership …
waleedlatif1 Jun 10, 2026
7ffc495
v0.7.1: chat voice mode model update, sim trigger, codepipeline integ…
waleedlatif1 Jun 10, 2026
d4722f9
v0.7.2: logs export security, code hygiene, mship cost attribution
icecrasher321 Jun 10, 2026
f4d22ff
v0.7.3: jira oauth scope fix, read-replica client, table wire data fi…
TheodoreSpeaks Jun 11, 2026
a48b4a1
v0.7.4: round-robin byok support, table block fix, db read replica ro…
waleedlatif1 Jun 12, 2026
79d98b3
v0.7.5: deployments API and block, vanta integration, performance imp…
waleedlatif1 Jun 13, 2026
e6587ca
v0.7.6: calendar scheduled tasks, new hubspot tools, virtualized chat…
waleedlatif1 Jun 15, 2026
8c3706e
v0.7.7: square, context.dev integrations, scheduled tasks styling cha…
waleedlatif1 Jun 15, 2026
59d9496
v0.7.8: security hardening, code hygiene, MSFT oauth provider, new at…
waleedlatif1 Jun 16, 2026
56a88a2
v0.7.9: agent file attachments, chat autoscroll, knowledge base uploa…
icecrasher321 Jun 16, 2026
db47da5
v0.7.10: models sorting, compress/decompress file block tools, new en…
icecrasher321 Jun 18, 2026
8df34a3
v0.7.11: parallel subagents, new tools, rich markdown editor, governa…
waleedlatif1 Jun 21, 2026
aaca750
v0.7.12: mcp servers ui/ux fixes, nuqs for query param management
icecrasher321 Jun 22, 2026
ad0b867
v0.7.13: pii redaction, react query frontend refactor, pi coding agen…
TheodoreSpeaks Jun 24, 2026
11168f9
v0.7.14: perf improvements, code hygiene, GitLab private host support
waleedlatif1 Jun 25, 2026
613e8ea
v0.7.15: academy, perf improvements, db attribution, kb tags filter f…
waleedlatif1 Jun 27, 2026
38c088a
v0.7.16: security hardening, db o11y and profiling, settings UI
waleedlatif1 Jun 28, 2026
0371856
v0.7.17: emcn and workflow renderer isolated package, popular blocks …
waleedlatif1 Jun 30, 2026
fabf696
v0.7.18: landing, tools depth, media embeds in md editor, sonnet 5
waleedlatif1 Jul 1, 2026
6e426f8
v0.7.19: newsletter, contact, careers
waleedlatif1 Jul 1, 2026
62ef96f
v0.7.20: perf, ui improvements, fable 5, tables row size bump, blogs
waleedlatif1 Jul 2, 2026
4fcfe76
v0.7.21: tools audits and extensions, appsheet integration, hitl fix
waleedlatif1 Jul 2, 2026
8afc4f1
v0.7.22: competitor pages, seo fixes, billing usage table
waleedlatif1 Jul 4, 2026
616a71d
v0.7.23: mobile comparison table, security hardening, subflow & strea…
waleedlatif1 Jul 5, 2026
62d3140
v0.7.24: frontend perf improvements, md editor fixes, mailer fix, too…
waleedlatif1 Jul 7, 2026
21ee602
v0.7.25: new tools, gmail send fixes, email lifecycle adjustments
waleedlatif1 Jul 7, 2026
9d44d42
v0.7.26: API block default headers fix, tables perf and correctness, …
waleedlatif1 Jul 8, 2026
067d3b0
v0.7.27: document tags improvements, grok 4.5, forking, custom blocks…
waleedlatif1 Jul 9, 2026
e2fecc8
v0.7.28: rich markdown editor improvements, autosave in files, helm i…
waleedlatif1 Jul 10, 2026
6c3d11b
v0.7.29: GLM, ui and ux improvements, tables improvements, enterprise…
waleedlatif1 Jul 11, 2026
207785c
v0.7.30: md editor improvements, og image updates, spacexai hosted ke…
waleedlatif1 Jul 12, 2026
e01bfb1
v0.7.31: sidebar shortcut, env var expansion in lifecycle triggers, a…
waleedlatif1 Jul 12, 2026
dc81795
v0.7.32: concurrency control, seo/geo, new lib, buffer & flint integr…
waleedlatif1 Jul 13, 2026
377f963
v0.7.33: workflow/custom block fix, community pages improvements, cha…
waleedlatif1 Jul 14, 2026
a403d05
v0.7.34: billing page fixes, chat rendering improvements
waleedlatif1 Jul 14, 2026
91b29b3
v0.7.35: sidebar styling, custom blocks reserved names fix, client en…
icecrasher321 Jul 14, 2026
50364ed
Merge pull request #5677 from simstudioai/staging
TheodoreSpeaks Jul 14, 2026
874c998
v0.7.36: mothership secrets resolution, SEO, service accounts for var…
waleedlatif1 Jul 15, 2026
9d23e25
v0.7.37: impersonation fixes, instagram/tiktok, landing improvements,…
icecrasher321 Jul 15, 2026
28b56d7
v0.7.38: mothership improvements, clickup, rocketlane integrations, g…
waleedlatif1 Jul 17, 2026
4ddad74
v0.7.39: mcp oauth hardening, chat perf improvements
waleedlatif1 Jul 18, 2026
3d12a6d
v0.7.40: library, mcp hardening, pi code reviewer, gmail fix
waleedlatif1 Jul 21, 2026
3ca1b65
v0.7.41: email fixes, MCP fixes, gemini 3.6 flash, GitHub runners fal…
waleedlatif1 Jul 22, 2026
31b81ca
v0.7.42: event-driven discovery for MCP, nuqs adoption for deeplinks,…
waleedlatif1 Jul 23, 2026
578d9dd
v0.7.43: ci and connector fixes
waleedlatif1 Jul 23, 2026
41bf1c4
v0.7.44: nextjs upgrade, CSV imports to tables fixes, tombstone for K…
waleedlatif1 Jul 23, 2026
6f514c1
v0.7.45: access control groups enhancements, sso domain verification,…
waleedlatif1 Jul 25, 2026
fe2241e
v0.7.46: settings consolidation, setup wizard, table column types and…
waleedlatif1 Jul 28, 2026
5e14639
v0.7.47: seo, import/export api, trigger task sizes
waleedlatif1 Jul 28, 2026
c3ed1e9
v0.7.48: perf improvements, confluence and sharepoint fixes, dependen…
waleedlatif1 Jul 29, 2026
2a62673
v0.7.49: outlook calendar, logfire tools, shared folders, desktop sec…
waleedlatif1 Jul 30, 2026
19d929b
v0.7.50: custom sandboxes, auto model, tables currency type, perf imp…
waleedlatif1 Jul 31, 2026
86486d5
v0.7.51: hybrid search for knowledgebases, ui improvements, db perf i…
waleedlatif1 Aug 1, 2026
13a9119
v0.7.52: span sanitization, highlight tables and files text to chat, …
waleedlatif1 Aug 2, 2026
7d889c3
v0.7.53: sandboxes, settings consolidation, code hygiene
waleedlatif1 Aug 3, 2026
9907640
v0.7.54: resource sorting, deployed chat improvements, self hosting d…
waleedlatif1 Aug 4, 2026
1b9e0f2
v0.7.55: revert nextjs version bump
waleedlatif1 Aug 4, 2026
404bc6a
v0.7.56: ui improvements, docs update, emcn consolidation
waleedlatif1 Aug 5, 2026
5dbb9a5
v0.7.57: forking webhook URL, markdown editor overflow, fix ui improv…
waleedlatif1 Aug 5, 2026
54a3262
v0.7.58: styling improvements, files dimensions
waleedlatif1 Aug 6, 2026
24114b2
v0.7.59: secrets obfuscation, providers timeout standardization, cli …
waleedlatif1 Aug 6, 2026
348caab
v0.7.60: tables, memory, sso improvements
waleedlatif1 Aug 6, 2026
e1ab24c
v0.7.61: private provenance alignment
icecrasher321 Aug 6, 2026
d5ce247
v0.7.62: file handling, mothership async runs, heic support, file har…
waleedlatif1 Aug 7, 2026
16e0a2b
v0.7.63: chat pins, ui improvements, bump deps, file improvements
waleedlatif1 Aug 7, 2026
e816933
v0.7.64: snowflake, dynatrace, mintlify, files and security hardening…
waleedlatif1 Aug 9, 2026
cb28d14
v0.7.65: email template alignment, logs enrichment, execution files u…
waleedlatif1 Aug 10, 2026
f4cc8d7
v0.7.66: provenance logs, nuqs consolidation
waleedlatif1 Aug 10, 2026
ccdb03d
v0.7.67: provenance ffs, files fixes, library post
waleedlatif1 Aug 10, 2026
ce2dff3
v0.7.68: OpenRouter embeddings, Docker builds, prompt autosizing, ide…
TheodoreSpeaks Aug 11, 2026
48c59c8
v0.8.0: desktop app, workflow UI, v2 endpoints
waleedlatif1 Aug 12, 2026
5e998fc
v0.8.1: grok 4.6, v2 endpoints extension, workflow UI improvements, l…
waleedlatif1 Aug 14, 2026
417ae20
v0.8.2: rabbitmq, code hygiene, new kb connectors
waleedlatif1 Aug 15, 2026
804a452
v0.8.3: sim cli, new tools, credential groups, splunk, finder-like fi…
waleedlatif1 Aug 17, 2026
e741923
v0.8.4: nextjs upgrade, cli updates, jotform triggers, secrets descri…
waleedlatif1 Aug 18, 2026
210d099
v0.8.5: cookie banner, cli updates, knowledgebase connectors improvem…
waleedlatif1 Aug 19, 2026
8273320
v0.8.6: connectors, local PDF parsing, condition block evaluation par…
waleedlatif1 Aug 19, 2026
ceda457
v0.8.7: perf improvements, custom blocks improvements, crunchbase, pi…
waleedlatif1 Aug 20, 2026
6c2cb92
v0.8.8: mothership improvements, new connectors, knowledgebase harden…
waleedlatif1 Aug 21, 2026
0b232eb
v0.8.9: webhooks fix, file writing improvements, kb fixes
waleedlatif1 Aug 21, 2026
f1a3456
v0.8.10: security hardening, file sharing, Bitbucket triggers, UI per…
TheodoreSpeaks Aug 23, 2026
981be93
v0.8.11: perf improvements, instant chat navigation, code hygiene
waleedlatif1 Aug 25, 2026
92ab46e
v0.8.12: memory improvements, cli expansion, semrush, microsoft word,…
waleedlatif1 Aug 26, 2026
a042b8f
v0.8.13: cli enhancements, desktop app selfhosting, isolated vm versi…
waleedlatif1 Aug 27, 2026
d59b02c
v0.8.14: files finder, internal routes removal, tools audit, usage tab
waleedlatif1 Aug 28, 2026
336ec07
v0.8.15: ci improvements, trigger machine resizing, connectors fixes
waleedlatif1 Aug 28, 2026
e1d5978
v0.8.16: aws lambda, dynamics 365, actorless run fixes, usage chart
TheodoreSpeaks Aug 29, 2026
c20cc1d
v0.8.17: ci improvements, outdated docs i18n removal, desktop improve…
waleedlatif1 Aug 29, 2026
5fb3c07
v0.8.18: table ttls, perf improvements, egress management, desktop ap…
waleedlatif1 Sep 2, 2026
82ee2e6
v0.8.19: desktop app upgrade flow improvements, monday.com OAuth fix
waleedlatif1 Sep 2, 2026
4824c90
v0.8.20: library, privacy policy updates, slack docs disclaimer
waleedlatif1 Sep 2, 2026
21be50f
v0.8.21: additional self hosting documentation, workspace fork improv…
waleedlatif1 Sep 3, 2026
7945b29
v0.8.22: ui fixes, library, env resolution hardening
waleedlatif1 Sep 4, 2026
1bbd577
v0.8.23: bun upgrade, infra resilience improvements, gpt-6, browser u…
waleedlatif1 Sep 5, 2026
477dcb2
v0.8.24: security hardening, model capability advertisement, browser …
waleedlatif1 Sep 6, 2026
4dbf0a0
v0.8.25: Sim Search, SCIM, Sim OAuth
waleedlatif1 Sep 8, 2026
c036db1
v0.8.26: knowledge base backfill, landing, files improvements
icecrasher321 Sep 8, 2026
bc540db
v0.8.27: search MCP, landing, file editor improvements
waleedlatif1 Sep 9, 2026
6fc5a37
v0.8.28: mobile landing fix, docs updates
waleedlatif1 Sep 9, 2026
5552aaf
v0.8.29: slack search, landing optimizations, search optimizations, s…
waleedlatif1 Sep 9, 2026
392910b
v0.8.30: workflow preprocessing and redis fault tolerance
waleedlatif1 Sep 9, 2026
03dffb4
v0.8.31: search connector hardening, document parsers improvements, o…
waleedlatif1 Sep 10, 2026
59cb2d2
v0.8.32: connectors fixes, search optimizations
waleedlatif1 Sep 10, 2026
0171adf
v0.8.33: kb fixes, chat ui cleanup, connectors instructions improvements
waleedlatif1 Sep 11, 2026
329e249
v0.8.34: consent hardening, org images, large ref fixes
waleedlatif1 Sep 11, 2026
4b3a9df
v0.8.35: cli telemetry, security hardening, desktop app improvements,…
waleedlatif1 Sep 14, 2026
2b139e4
v0.8.36: sim search, provenance improvements
waleedlatif1 Sep 14, 2026
6781650
v0.8.37: cli telemetry enhancement, slack search, search speedup, org…
waleedlatif1 Sep 15, 2026
d7ef91a
v0.8.38: concurrent knowledgebase operations, media lightbox for chat
waleedlatif1 Sep 15, 2026
c0f4717
v0.8.39: search and file handling fixes, organization settings, bound…
waleedlatif1 Sep 16, 2026
bd68eeb
feat(ci): promote Trigger.dev tasks in lockstep with the ECS traffic …
TheodoreSpeaks Jul 17, 2026
e1e136d
fix(ci): harden Trigger.dev cutover gate — reject stale executions, v…
TheodoreSpeaks Jul 19, 2026
baf3d0b
fix(ci): widen promote-trigger job timeout above the poll budget
TheodoreSpeaks Jul 19, 2026
d0e117a
fix(ci): hold AWS session for the full poll and skip wait when the ap…
TheodoreSpeaks Jul 20, 2026
8852771
feat(ci): extend lockstep Trigger.dev promotion to dev (preview branch)
TheodoreSpeaks Jul 20, 2026
a431d19
fix(ci): don't block dev task promotion on a non-app build-dev leg fa…
TheodoreSpeaks Jul 20, 2026
99bb55a
chore(ci): use one Trigger.dev PAT for all envs (drop DEV_TRIGGER_ACC…
TheodoreSpeaks Jul 20, 2026
e3ac689
fix(ci): reliable digest reads for no-op detection, robust version pa…
TheodoreSpeaks Jul 20, 2026
67d8224
fix(ci): give dev promote-trigger a 20-min margin over its poll budget
TheodoreSpeaks Jul 20, 2026
81d73da
fix(ci): require promote-images + deploy-trigger success explicitly f…
TheodoreSpeaks Jul 20, 2026
da56970
fix(ci): verify Trigger promotion against the deployed image and cutover
TheodoreSpeaks Sep 16, 2026
ec2f0bc
fix(ci): bind Trigger promotion to the latest app tag move
TheodoreSpeaks Sep 16, 2026
d7dd8f6
test(ci): reject exhausted deployment response fixtures
TheodoreSpeaks Sep 16, 2026
72cea20
test(ci): require cutover checks for every ECS target
TheodoreSpeaks Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/scripts/get-ecr-image-digest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Read one ECR tag. Only ImageNotFound is optional; AWS and response errors fail.
set -euo pipefail
REPOSITORY="${1:?repository required}"
TAG="${2:?tag required}"
ALLOW_MISSING="${3:-}"
if [ -n "$ALLOW_MISSING" ] && [ "$ALLOW_MISSING" != '--allow-missing' ]; then
echo 'ERROR: expected --allow-missing or no third argument' >&2
exit 1
fi
export AWS_PAGER=''
aws ecr batch-get-image --repository-name "$REPOSITORY" --image-ids imageTag="$TAG" --output json |
ALLOW_MISSING="$ALLOW_MISSING" python3 -c '
import json, os, re, sys
response = json.load(sys.stdin)
images, failures = response["images"], response["failures"]
if failures:
if not images and len(failures) == 1 and failures[0]["failureCode"] == "ImageNotFound" and os.environ["ALLOW_MISSING"]:
print("")
sys.exit(0)
raise SystemExit("ERROR: ECR image lookup failed: " + ", ".join(f["failureCode"] for f in failures))
if len(images) != 1:
raise SystemExit("ERROR: expected exactly one ECR image")
digest = images[0]["imageId"]["imageDigest"]
if not re.fullmatch(r"sha256:[0-9a-f]{64}", digest):
raise SystemExit("ERROR: invalid ECR image digest")
print(digest)
'
20 changes: 20 additions & 0 deletions .github/scripts/promote-app-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Capture the cutover lower bound at the app tag move, after the image is built.
set -euo pipefail
REGISTRY="${1:?registry required}"
REPOSITORY="${2:?repository required}"
SOURCE_TAG="${3:?source tag required}"
DEPLOY_TAG="${4:?deploy tag required}"
: "${GITHUB_OUTPUT:?GitHub output file required}"
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
PREVIOUS=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG" --allow-missing)
EPOCH=$(date +%s)
docker buildx imagetools create -t "$REGISTRY/$REPOSITORY:$DEPLOY_TAG" "$REGISTRY/$REPOSITORY:$SOURCE_TAG"
DIGEST=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG")
CHANGED=true
if [ "$DIGEST" = "$PREVIOUS" ]; then CHANGED=false; fi
{
echo "retag_epoch=$EPOCH"
echo "app_image_digest=$DIGEST"
echo "app_image_changed=$CHANGED"
} >> "$GITHUB_OUTPUT"
264 changes: 264 additions & 0 deletions .github/scripts/test-trigger-deploy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
"""Exercise the deployment gates with scripted AWS responses; no live mutations."""
import json
import os
from pathlib import Path
import subprocess
import tempfile
import unittest

SCRIPTS = Path(__file__).resolve().parent
DIGEST = 'sha256:' + 'a' * 64
OTHER_DIGEST = 'sha256:' + 'b' * 64


def execution(start=1000, digest=DIGEST, identifier='execution-current'):
return {
'startTime': start,
'pipelineExecutionId': identifier,
'sourceRevisions': [{'actionName': 'ECR_Source', 'revisionId': digest}],
}


class DeploymentGateTests(unittest.TestCase):
def run_script(self, script, args, responses):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
fixture = root / 'responses.json'
fixture.write_text(json.dumps(responses))
(root / 'aws').write_text('''#!/usr/bin/env python3
import json, os, pathlib, sys
root = pathlib.Path(os.environ['FIXTURE_DIR'])
args = sys.argv[1:]
if args[0] == '--cli-connect-timeout':
args = args[4:]
service, operation = args[:2]
key = operation
if operation == 'get-deployment-target':
key += ':' + args[args.index('--target-id') + 1]
with (root / 'calls').open('a') as stream:
stream.write(' '.join(args) + '\\n')
responses = json.loads((root / 'responses.json').read_text())
if key not in responses:
raise SystemExit('Unexpected AWS call: ' + key)
response = responses[key]
# Objects model steady state; lists are finite, ordered expectations.
if isinstance(response, list):
if not response:
raise SystemExit('Unexpected extra AWS call: ' + key)
next_response = response.pop(0)
(root / 'responses.json').write_text(json.dumps(responses))
response = next_response
if response.get('error'):
sys.stderr.write(response['error'])
sys.exit(254)
if response.get('advance_clock'):
clock = root / 'clock'
value = int(clock.read_text()) if clock.exists() else 1000
clock.write_text(str(value + response['advance_clock']))
print(response.get('text', json.dumps(response.get('json'))))
''')
(root / 'docker').write_text('''#!/usr/bin/env python3
import os, pathlib, sys
root = pathlib.Path(os.environ['FIXTURE_DIR'])
with (root / 'calls').open('a') as stream:
stream.write('docker ' + ' '.join(sys.argv[1:]) + '\\n')
''')
(root / 'date').write_text('''#!/usr/bin/env python3
import os, pathlib
path = pathlib.Path(os.environ['FIXTURE_DIR']) / 'clock'
value = int(path.read_text()) if path.exists() else 1000
path.write_text(str(value + 1))
print(value)
''')
(root / 'sleep').write_text('#!/bin/sh\nexit 0\n')
for name in ('aws', 'date', 'sleep', 'docker'):
(root / name).chmod(0o755)
result = subprocess.run(
['bash', str(SCRIPTS / script), *args],
env={**os.environ, 'PATH': f'{root}:{os.environ["PATH"]}',
'FIXTURE_DIR': str(root), 'POLL_INTERVAL': '1', 'OVERALL_TIMEOUT': '12',
'GITHUB_OUTPUT': str(root / 'outputs')},
capture_output=True, text=True, timeout=10,
)
calls = (root / 'calls').read_text() if (root / 'calls').exists() else ''
result.github_output = (root / 'outputs').read_text() if (root / 'outputs').exists() else ''
return result, calls

def poll(self, updates=None, since='1000'):
responses = {
'list-pipeline-executions': {'json': [execution()]},
'get-pipeline-execution': {'text': 'InProgress'},
'list-action-executions': {'text': 'd-current'},
'get-deployment': {'text': 'InProgress'},
'list-deployment-targets': {'text': 'target-one\ttarget-two'},
'get-deployment-target:target-one': {'text': 'Succeeded'},
'get-deployment-target:target-two': {'text': 'Succeeded'},
}
responses.update(updates or {})
return self.run_script('wait-for-ecs-cutover.sh', ['app-pipeline', DIGEST, since], responses)

def test_waits_for_every_target(self):
targets = ('target-one', 'target-two')
for pending_target in targets:
with self.subTest(pending_target=pending_target):
result, calls = self.poll({f'get-deployment-target:{pending_target}': [
{'text': 'InProgress'}, {'text': 'Succeeded'}]})
self.assertEqual(result.returncode, 0, result.stderr)
for target in targets:
self.assertEqual(calls.count(f'--target-id {target}'), 2)

def test_rejects_stale_execution_inside_former_clock_skew_window(self):
result, calls = self.poll({'list-pipeline-executions': {'json': [execution(start=999)]}})
self.assertNotEqual(result.returncode, 0)
self.assertIn('timed out', result.stdout)
self.assertNotIn('get-pipeline-execution ', calls)

def test_chooses_newest_matching_execution(self):
result, calls = self.poll({'list-pipeline-executions': {'json': [
execution(1000, identifier='execution-old'), execution(1001)]}})
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn('--pipeline-execution-id execution-current', calls)

def test_changed_image_rejects_newer_different_execution(self):
result, calls = self.poll({'list-pipeline-executions': {'json': [
execution(), execution(1001, digest=OTHER_DIGEST, identifier='execution-newer')]}})
self.assertNotEqual(result.returncode, 0)
self.assertIn('deployment was superseded', result.stderr)
self.assertNotIn('get-pipeline-execution ', calls)

def test_rechecks_latest_digest_after_cutover(self):
result, calls = self.poll({'list-pipeline-executions': [
{'json': [execution()]},
{'json': [execution(), execution(1001, digest=OTHER_DIGEST, identifier='execution-newer')]},
]})
self.assertNotEqual(result.returncode, 0)
self.assertIn('deployment was superseded', result.stderr)
self.assertIn('get-deployment-target ', calls)
self.assertNotIn('Traffic cutover complete', result.stdout)

def test_rechecks_execution_identity_for_same_digest_after_cutover(self):
result, _ = self.poll({'list-pipeline-executions': [
{'json': [execution()]},
{'json': [execution(), execution(1001, identifier='execution-newer')]},
]})
self.assertNotEqual(result.returncode, 0)
self.assertIn('newer pipeline execution appeared', result.stdout)
self.assertNotIn('Traffic cutover complete', result.stdout)

def test_iso_timestamps(self):
result, _ = self.poll({'list-pipeline-executions': {'json': [
execution('1970-01-01T00:16:40+00:00')]}})
self.assertEqual(result.returncode, 0, result.stderr)

def test_scripted_responses_reject_unexpected_extra_calls(self):
result, _ = self.poll({'list-pipeline-executions': [{'json': [execution()]}]})
self.assertNotEqual(result.returncode, 0)
self.assertIn('Unexpected extra AWS call: list-pipeline-executions', result.stderr)
self.assertNotIn('Traffic cutover complete', result.stdout)

def test_access_denial_fails_immediately(self):
result, calls = self.poll({'list-pipeline-executions': {'error': 'AccessDeniedException'}})
self.assertNotEqual(result.returncode, 0)
self.assertIn('AccessDeniedException', result.stderr)
self.assertEqual(len(calls.splitlines()), 1)

def test_credentials_expiring_during_target_poll_fail(self):
result, _ = self.poll({'get-deployment-target:target-two': {'error': 'ExpiredToken'}})
self.assertNotEqual(result.returncode, 0)
self.assertIn('ExpiredToken', result.stderr)

def test_failed_and_superseded_pipeline_never_reach_deployment(self):
for status in ('Failed', 'Stopped', 'Superseded'):
with self.subTest(status=status):
result, calls = self.poll({'get-pipeline-execution': {'text': status}})
self.assertNotEqual(result.returncode, 0)
self.assertNotIn('get-deployment ', calls)

def test_waits_for_queued_deploy_action(self):
result, calls = self.poll({'list-action-executions': [{'text': 'None'}, {'text': 'd-current'}]})
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(calls.count('list-action-executions '), 2)

def test_failed_deployment_never_accepts_old_cutover(self):
result, calls = self.poll({'get-deployment': {'text': 'Failed'}})
self.assertNotEqual(result.returncode, 0)
self.assertNotIn('get-deployment-target ', calls)

def test_empty_targets_cannot_satisfy_gate(self):
result, _ = self.poll({'list-deployment-targets': {'text': ''}})
self.assertNotEqual(result.returncode, 0)
self.assertIn('timed out', result.stdout)

def test_failed_target_fails_immediately(self):
result, _ = self.poll({'get-deployment-target:target-two': {'text': 'Failed'}})
self.assertNotEqual(result.returncode, 0)
self.assertIn('cutover status Failed', result.stdout)

def test_unchanged_image_verifies_existing_cutover(self):
result, calls = self.poll({'list-pipeline-executions': {'json': [execution(start=900)]}}, since='0')
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn('get-deployment-target ', calls)

def test_unchanged_image_rejects_latest_different_deploy(self):
result, calls = self.poll({'list-pipeline-executions': {'json': [
execution(start=900), execution(start=999, digest=OTHER_DIGEST)]}}, since='0')
self.assertNotEqual(result.returncode, 0)
self.assertIn('cutover is unverified', result.stderr)
self.assertNotIn('get-deployment ', calls)

def test_unchanged_image_rejects_failed_previous_deploy(self):
result, _ = self.poll({'get-deployment': {'text': 'Failed'}}, since='0')
self.assertNotEqual(result.returncode, 0)

def test_invalid_metadata_fails_before_aws(self):
result, calls = self.poll(since='corrupted')
self.assertNotEqual(result.returncode, 0)
self.assertEqual(calls, '')

def test_ecr_digest_and_missing_tag(self):
result, _ = self.run_script('get-ecr-image-digest.sh', ['app', 'deploy'], {
'batch-get-image': {'json': {'images': [{'imageId': {'imageDigest': DIGEST}}], 'failures': []}}})
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stdout.strip(), DIGEST)
missing = {'batch-get-image': {'json': {'images': [], 'failures': [{'failureCode': 'ImageNotFound'}]}}}
result, _ = self.run_script('get-ecr-image-digest.sh', ['app', 'deploy', '--allow-missing'], missing)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stdout.strip(), '')
result, _ = self.run_script('get-ecr-image-digest.sh', ['app', 'deploy'], missing)
self.assertNotEqual(result.returncode, 0)

def test_ecr_response_failures_are_not_missing_images(self):
for response in ({'error': 'AccessDeniedException'}, {'json': {'images': [], 'failures': [{'failureCode': 'KmsError'}]}}, {'json': {'images': [], 'failures': []}}):
with self.subTest(response=response):
result, _ = self.run_script('get-ecr-image-digest.sh', ['app', 'deploy', '--allow-missing'], {'batch-get-image': response})
self.assertNotEqual(result.returncode, 0)

def test_tag_move_uses_push_boundary_and_final_manifest_digest(self):
result, calls = self.run_script('promote-app-image.sh', ['registry', 'app', 'commit-dev', 'dev'], {
'batch-get-image': [
{'advance_clock': 30, 'json': {'images': [{'imageId': {'imageDigest': DIGEST}}], 'failures': []}},
{'json': {'images': [{'imageId': {'imageDigest': OTHER_DIGEST}}], 'failures': []}},
]})
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn('retag_epoch=1030', result.github_output)
self.assertIn(f'app_image_digest={OTHER_DIGEST}', result.github_output)
self.assertIn('app_image_changed=true', result.github_output)
self.assertEqual([line.split()[0] for line in calls.splitlines()], ['ecr', 'docker', 'ecr'])
self.assertIn('registry/app:commit-dev', calls)

def test_tag_move_aborts_before_docker_when_ecr_read_fails(self):
result, calls = self.run_script('promote-app-image.sh', ['registry', 'app', 'commit', 'deploy'], {
'batch-get-image': {'error': 'AccessDeniedException'}})
self.assertNotEqual(result.returncode, 0)
self.assertNotIn('docker', calls)
self.assertEqual(result.github_output, '')

def test_same_digest_tag_move_reports_unchanged(self):
result, _ = self.run_script('promote-app-image.sh', ['registry', 'app', 'commit', 'deploy'], {
'batch-get-image': {'json': {'images': [{'imageId': {'imageDigest': DIGEST}}], 'failures': []}}})
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn('app_image_changed=false', result.github_output)


if __name__ == '__main__':
unittest.main()
Loading
Loading