Skip to content

Commit 788a2ff

Browse files
committed
Merge remote-tracking branch 'origin/main' into mixolydian-cockatoo
2 parents 1ff427b + 7812813 commit 788a2ff

580 files changed

Lines changed: 40143 additions & 1804 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,6 @@ OPENAI_API_KEY=
184184
# The GitHub App this deployment mints installation tokens for. Must match
185185
# GH_APP_ID — a mismatch surfaces much later as a 404 on token creation.
186186
GH_APP_SLUG=superset-app
187+
188+
# Read-only support account lookup (apps/api /api/support/lookup); unset = endpoint off
189+
SUPPORT_LOOKUP_TOKEN=

.github/prompts/beautify-screenshot.ts

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
// welcome cards use), rounded corners, shadow, optional feature highlight.
66
// Fully local: no network, no upload (safe for shots with internal data).
77
//
8-
// Usage: bun beautify-screenshot.ts <in.png> <out.png> [tilt|flat] [x,y,w,h] [flags]
8+
// Usage: bun beautify-screenshot.ts <in.png> <out.png> [card|flat|tilt] [x,y,w,h] [flags]
9+
// card the changelog default (Linear-style): flat product card
10+
// with a hairline border on a near-black backdrop, no
11+
// dither, no perspective. Pair with --bleed for heroes.
12+
// flat / tilt the older dithered-backdrop looks; tilt is retired for
13+
// heroes (Kiet, 2026-08-23: no slanted heroes).
914
// x,y,w,h optional crop rectangle in source pixels — zoom into the
1015
// feature instead of framing the whole window.
1116
//
@@ -27,6 +32,9 @@
2732
// the frame — points the reader at the feature.
2833
// --hl-dim <n> how much to dim outside the highlight, 0-1 (default
2934
// 0.42; use ~0.2 when the dimmed area must stay readable)
35+
// --bleed (card only) anchor the card near the top and let it run
36+
// off the bottom edge under a fade, the way Linear frames
37+
// a tall surface; the crop should be taller than 16:10
3038
//
3139
// Needs: Google Chrome (or set CHROME=/path/to/chrome). Compress the output
3240
// afterwards, e.g. `pngquant --quality=58-84 out.png`.
@@ -45,10 +53,12 @@ for (let i = 0; i < args.length; i++) {
4553
positional.push(a);
4654
}
4755
}
48-
const [inPath, outPathArg, style = "tilt", cropArg] = positional;
56+
const [inPath, outPathArg, style = "card", cropArg] = positional;
57+
const isCard = style === "card";
58+
const bleed = isCard && flags.has("bleed");
4959
if (!inPath || !outPathArg) {
5060
console.error(
51-
"usage: bun beautify-screenshot.ts <in.png> <out.png> [tilt|flat] [x,y,w,h] [--bg preset] [--seed n] [--highlight x,y,w,h]",
61+
"usage: bun beautify-screenshot.ts <in.png> <out.png> [card|flat|tilt] [x,y,w,h] [--bleed] [--bg preset] [--seed n] [--highlight x,y,w,h]",
5262
);
5363
process.exit(2);
5464
}
@@ -59,8 +69,8 @@ const CHROME =
5969
const SCALE = 2;
6070
// Bigger frame + tighter canvas = the UI reads larger, less dead background.
6171
const CANVAS_W = 1600;
62-
const CANVAS_H = style === "tilt" ? 1040 : 980;
63-
const FRAME_FRAC = style === "tilt" ? 0.86 : 0.92;
72+
const CANVAS_H = isCard ? 925 : style === "tilt" ? 1040 : 980;
73+
const FRAME_FRAC = isCard ? 0.84 : style === "tilt" ? 0.86 : 0.92;
6474
const tilt =
6575
style === "tilt"
6676
? "perspective(2600px) rotateY(-8deg) rotateX(3deg) rotateZ(-0.6deg)"
@@ -89,7 +99,7 @@ if (bgName === "random") {
8999
const names = Object.keys(PRESETS);
90100
bgName = names[Math.floor(rand() * names.length)];
91101
}
92-
const usePlain = bgName === "plain";
102+
const usePlain = bgName === "plain" || isCard;
93103
if (!usePlain && !PRESETS[bgName]) {
94104
console.error(
95105
`unknown --bg "${bgName}" — use ${Object.keys(PRESETS).join("|")}|plain|random`,
@@ -147,7 +157,9 @@ if (cropArg) {
147157
// crops float as a bordered card on the backdrop instead of overflowing the
148158
// canvas and getting cut off top/bottom.
149159
const frameW = Math.round(
150-
Math.min(CANVAS_W * FRAME_FRAC, CANVAS_H * FRAME_FRAC * aspect),
160+
bleed
161+
? CANVAS_W * FRAME_FRAC
162+
: Math.min(CANVAS_W * FRAME_FRAC, CANVAS_H * FRAME_FRAC * aspect),
151163
);
152164

153165
// Optional highlight: accent ring + dim outside, in source-pixel coordinates.
@@ -246,6 +258,24 @@ const html = `<!doctype html><html><head><meta charset="utf8"><style>
246258
0 80px 160px -40px rgba(0,0,0,.65),
247259
0 0 0 1px rgba(255,255,255,.06) inset;
248260
}
261+
/* card: Linear-style flat product card. Near-black stage with a faint top
262+
glow, hairline border, quiet shadow. --bleed pins it high and fades the
263+
bottom edge into the backdrop. */
264+
body.card .stage { background:#0a0a0b; align-items:${bleed ? "flex-start" : "center"}; }
265+
body.card .glow, body.card .tint, body.card .vignette, body.card #dither { display:none; }
266+
body.card .sheen { position:absolute; inset:0; background:
267+
radial-gradient(ellipse 70% 55% at 50% 0%, rgba(255,255,255,.045), transparent 70%); }
268+
body.card .frame {
269+
margin-top:${bleed ? Math.round(CANVAS_H * 0.09) : 0}px;
270+
border-radius:14px; background:#111113;
271+
border:1px solid rgba(255,255,255,.09);
272+
box-shadow: 0 1px 0 rgba(255,255,255,.04) inset,
273+
0 24px 60px -24px rgba(0,0,0,.8),
274+
0 0 0 1px rgba(0,0,0,.6);
275+
}
276+
body.card .fade { position:absolute; left:0; right:0; bottom:0; height:${Math.round(CANVAS_H * 0.32)}px;
277+
display:${bleed ? "block" : "none"};
278+
background:linear-gradient(to bottom, rgba(10,10,11,0), rgba(10,10,11,.92) 70%, #0a0a0b); }
249279
.frame img, .frame .shot { display:block; width:100%; height:auto; }
250280
/* highlight ring: dim everything outside via a huge spread shadow (clipped
251281
by the frame's overflow:hidden), accent ring + soft glow on the region */
@@ -256,14 +286,16 @@ const html = `<!doctype html><html><head><meta charset="utf8"><style>
256286
0 0 22px 2px ${accent}66,
257287
inset 0 0 14px ${accent}22;
258288
}
259-
</style></head><body>
289+
</style></head><body class="${isCard ? "card" : ""}">
260290
<div class="stage">
291+
<div class="sheen"></div>
261292
<div id="dither"></div>
262293
<div class="tint"></div>
263294
<div class="vignette"></div>
264295
<div class="glow g1"></div><div class="glow g2"></div>
265296
<div class="glow g3"></div><div class="glow g4"></div>
266297
<div class="frame">${shot}${highlight}</div>
298+
<div class="fade"></div>
267299
</div>
268300
${shaderScript}
269301
</body></html>`;
@@ -305,5 +337,7 @@ if (proc.exitCode !== 0) {
305337
console.log(
306338
"wrote",
307339
outPath,
308-
`(bg=${usePlain ? "plain" : bgName}, seed=${seed})`,
340+
isCard
341+
? `(card${bleed ? ", bleed" : ""})`
342+
: `(bg=${usePlain ? "plain" : bgName}, seed=${seed})`,
309343
);

.github/prompts/changelog-film.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Weekly changelog film SOP
2+
3+
How to produce the short demo film that rides with a weekly changelog entry: one clip
4+
that walks the week's headline features in the real app. Companion to the Weekly
5+
Changelog automation (Notion SOP is the source of truth for the entry itself) and to
6+
`readme-hero-film.md`, which this adapts from a one-off marketing hero to a weekly cadence.
7+
8+
## What it is
9+
10+
A 20-40s captioned mp4 (not GIF: changelog MDX has a `<Video>` component with controls),
11+
one beat per headline feature, 3-5 beats total, in changelog order. Every frame is real
12+
app state driven over CDP; no mockups. Landscape 1280px wide, 30fps, silent.
13+
14+
## Cadence and scope
15+
16+
- Produced with the weekly changelog when the week has 2+ features that read better in
17+
motion (flows, dropdowns, live status). A slow week skips the film; never pad beats.
18+
- The film demos the entry's own sections, top to bottom. One idea per beat, captioned
19+
in the entry's words, so the film and the entry never disagree.
20+
21+
## Production (adapts readme-hero-film.md; read that file for the deep recipes)
22+
23+
1. **Stage** the dev desktop from the worktree: `SUPERSET_HOME_DIR="$PWD/superset-dev-data"
24+
RENDERER_REMOTE_DEBUG_PORT=9222 bun run dev:desktop`, wait for `localhost:9222/json`.
25+
2. **Hard rules carried over**: no real data in frame (demo projects only, hide Sessions,
26+
restore all state after), no PII, quit the dev stack when done.
27+
3. **Frames over CDP**: `Emulation.setDeviceMetricsOverride` (1280x800, DPR 2), drive each
28+
beat with real interactions, `Page.captureScreenshot` at 8-12 fps bursts for motion
29+
(`Page.startScreencast` where the flow animates).
30+
4. **Captions + backdrop**: composite each beat on the flat card backdrop (`card` mode
31+
look from `beautify-screenshot.ts`; heroes' bleed/fade not needed in motion), caption
32+
in Inter Medium, no em dashes, prose rules apply.
33+
5. **Encode**: `ffmpeg -an -vf "scale=1280:-2,fps=30" -c:v libx264 -pix_fmt yuv420p
34+
-crf 28 -preset slow -movflags +faststart` into
35+
`apps/marketing/public/changelog/YYYY-MM-DD-week.mp4`; target under 2MB.
36+
6. **Embed** at the top of the entry, right under the frontmatter:
37+
`<Video src="/changelog/YYYY-MM-DD-week.mp4" title="Everything in this week's release" />`
38+
7. The launch thread's post 2 attaches this film.
39+
40+
## Status
41+
42+
Written 2026-08-24 on Kiet's ask ("the weekly video ... demoing everything"). Not yet run;
43+
first production week should time-box beats to ~30 min each and cut what overruns.

.github/workflows/ci.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ jobs:
3535
vale:
3636
name: Vale prose lint
3737
runs-on: ubuntu-latest
38+
permissions:
39+
contents: read
3840
steps:
3941
- name: Check out code
4042
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
@@ -45,9 +47,14 @@ jobs:
4547
echo "db947f89f2292e6a0381a61de155f6a5f5cb4cb460ca178ea412ef605559cefd /tmp/vale.tar.gz" | sha256sum -c -
4648
sudo tar -xzf /tmp/vale.tar.gz -C /usr/local/bin vale
4749
50+
- name: Setup Bun
51+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
52+
with:
53+
bun-version-file: .bun-version
54+
4855
- name: Lint marketing content
4956
working-directory: apps/marketing
50-
run: vale --config=vale.ini content
57+
run: bun run lint:prose
5158

5259
version-sync:
5360
name: Version Sync

.github/workflows/deploy-production.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ jobs:
8686
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
8787
GOOGLE_PUBSUB_TOPIC: ${{ secrets.GOOGLE_PUBSUB_TOPIC }}
8888
GOOGLE_PUBSUB_PUSH_TOKEN: ${{ secrets.GOOGLE_PUBSUB_PUSH_TOKEN }}
89+
SUPPORT_LOOKUP_TOKEN: ${{ secrets.SUPPORT_LOOKUP_TOKEN }}
8990
GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }}
9091
GH_CLIENT_SECRET: ${{ secrets.GH_CLIENT_SECRET }}
9192
NEXT_PUBLIC_SENTRY_DSN_API: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN_API }}
@@ -154,6 +155,7 @@ jobs:
154155
--env GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET \
155156
--env GOOGLE_PUBSUB_TOPIC=$GOOGLE_PUBSUB_TOPIC \
156157
--env GOOGLE_PUBSUB_PUSH_TOKEN=$GOOGLE_PUBSUB_PUSH_TOKEN \
158+
--env SUPPORT_LOOKUP_TOKEN=$SUPPORT_LOOKUP_TOKEN \
157159
--env GH_CLIENT_ID=$GH_CLIENT_ID \
158160
--env GH_CLIENT_SECRET=$GH_CLIENT_SECRET \
159161
--env NEXT_PUBLIC_SENTRY_DSN_API=$NEXT_PUBLIC_SENTRY_DSN_API \

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,21 @@ Superset works with any CLI-based coding agent, including:
188188
| Agent | Status |
189189
|:------|:-------|
190190
| <img height="16" align="top" alt="Amp Code" src="packages/ui/src/assets/icons/preset-icons/amp.svg" /> &nbsp;[Amp Code](https://ampcode.com/) | Fully supported |
191+
| <img height="16" align="top" alt="Antigravity" src="packages/ui/src/assets/icons/preset-icons/antigravity.svg" /> &nbsp;[Antigravity CLI](https://antigravity.google/) | Fully supported |
191192
| <img height="16" align="top" alt="Claude Code" src="packages/ui/src/assets/icons/preset-icons/claude.svg" /> &nbsp;[Claude Code](https://github.com/anthropics/claude-code) | Fully supported |
192193
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/codex-white.svg" /><img height="16" align="top" alt="OpenAI Codex CLI" src="packages/ui/src/assets/icons/preset-icons/codex.svg" /></picture> &nbsp;[OpenAI Codex CLI](https://github.com/openai/codex) | Fully supported |
193194
| <img height="16" align="top" alt="Cursor Agent" src="packages/ui/src/assets/icons/preset-icons/cursor.svg" /> &nbsp;[Cursor Agent](https://docs.cursor.com/agent) | Fully supported |
194195
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/droid-white.svg" /><img height="16" align="top" alt="Droid" src="packages/ui/src/assets/icons/preset-icons/droid.svg" /></picture> &nbsp;[Droid](https://www.factory.ai/) | Fully supported |
196+
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/fx-white.svg" /><img height="16" align="top" alt="fx" src="packages/ui/src/assets/icons/preset-icons/fx.svg" /></picture> &nbsp;[fx](https://fx.sh/) | Fully supported |
195197
| <img height="16" align="top" alt="Gemini CLI" src="packages/ui/src/assets/icons/preset-icons/gemini.svg" /> &nbsp;[Gemini CLI](https://github.com/google-gemini/gemini-cli) | Fully supported |
196198
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/copilot-white.svg" /><img height="16" align="top" alt="GitHub Copilot" src="packages/ui/src/assets/icons/preset-icons/copilot.svg" /></picture> &nbsp;[GitHub Copilot](https://github.com/features/copilot) | Fully supported |
197199
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/grok-white.svg" /><img height="16" align="top" alt="Grok" src="packages/ui/src/assets/icons/preset-icons/grok.svg" /></picture> &nbsp;[Grok](https://x.ai/) | Fully supported |
200+
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/hermes-white.svg" /><img height="16" align="top" alt="Hermes" src="packages/ui/src/assets/icons/preset-icons/hermes.svg" /></picture> &nbsp;[Hermes](https://github.com/NousResearch/hermes-agent) | Fully supported |
198201
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/kimi-white.svg" /><img height="16" align="top" alt="Kimi Code" src="packages/ui/src/assets/icons/preset-icons/kimi.svg" /></picture> &nbsp;[Kimi Code](https://www.kimi.com/) | Fully supported |
202+
| <img height="16" align="top" alt="Kiro" src="packages/ui/src/assets/icons/preset-icons/kiro.svg" /> &nbsp;[Kiro](https://kiro.dev/cli/) | Fully supported |
199203
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/mastracode-white.svg" /><img height="16" align="top" alt="Mastra Code" src="packages/ui/src/assets/icons/preset-icons/mastracode.svg" /></picture> &nbsp;[Mastra Code](https://mastra.ai/) | Fully supported |
200204
| <img height="16" align="top" alt="Mistral Vibe" src="packages/ui/src/assets/icons/preset-icons/vibe.svg" /> &nbsp;[Mistral Vibe](https://mistral.ai/) | Fully supported |
205+
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/pi-white.svg" /><img height="16" align="top" alt="Oh My Pi" src="packages/ui/src/assets/icons/preset-icons/pi.svg" /></picture> &nbsp;[Oh My Pi](https://github.com/can1357/oh-my-pi) | Fully supported |
201206
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/opencode-white.svg" /><img height="16" align="top" alt="OpenCode" src="packages/ui/src/assets/icons/preset-icons/opencode.svg" /></picture> &nbsp;[OpenCode](https://github.com/opencode-ai/opencode) | Fully supported |
202207
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/pi-white.svg" /><img height="16" align="top" alt="Pi" src="packages/ui/src/assets/icons/preset-icons/pi.svg" /></picture> &nbsp;[Pi](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) | Fully supported |
203208
| <picture><source media="(prefers-color-scheme: dark)" srcset="packages/ui/src/assets/icons/preset-icons/polygraph-white.svg" /><img height="16" align="top" alt="Polygraph" src="packages/ui/src/assets/icons/preset-icons/polygraph.svg" /></picture> &nbsp;[Polygraph](https://trypolygraph.com/) | Fully supported |

apps/api/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@slack/web-api": "7.15.0",
2626
"@superset/auth": "workspace:*",
2727
"@superset/db": "workspace:*",
28+
"@superset/email": "workspace:*",
2829
"@superset/mcp": "workspace:*",
2930
"@superset/shared": "workspace:*",
3031
"@superset/trpc": "workspace:*",
@@ -45,6 +46,7 @@
4546
"react": "19.2.3",
4647
"react-dom": "19.2.3",
4748
"require-in-the-middle": "8.0.1",
49+
"resend": "6.18.0",
4850
"rrule": "2.8.1",
4951
"stripe": "20.4.1",
5052
"zod": "4.4.3"

apps/api/src/app/api/ingest/jobs/enforce-retention/route.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ import { verifyQstashRequest } from "@/lib/verifyQstash";
55

66
export const dynamic = "force-dynamic";
77

8+
/**
9+
* Matches the other long-running job routes. Without it this route took the
10+
* platform default, which is shorter than a single batch now takes, so a run
11+
* was killed mid-loop rather than ending on its own time budget.
12+
*/
13+
export const maxDuration = 300;
14+
815
/**
916
* How long an event record itself is worth keeping, separate from its body.
1017
*
@@ -21,13 +28,25 @@ const BATCH_SIZE = 5_000;
2128

2229
/**
2330
* Ceiling per table per run. Deleting leaves dead tuples, so this is what stops
24-
* a backlog drain outrunning autovacuum. Against a 5-minute schedule it is
25-
* ~14M rows/day, comfortably ahead of the ~1.2M/day these tables take on.
31+
* a backlog drain outrunning autovacuum.
32+
*
33+
* It has never been the binding constraint. TIME_BUDGET_MS is, because a batch
34+
* costs far more than this file originally assumed — see there.
2635
*/
2736
const MAX_ROWS_PER_TABLE = 50_000;
2837

29-
/** Well inside the function timeout, so a run ends by choice rather than by kill. */
30-
const TIME_BUDGET_MS = 20_000;
38+
/**
39+
* Has to exceed the cost of a batch, or the loop can only ever run one.
40+
*
41+
* A batch is `ORDER BY received_at LIMIT 5000` off the left edge of
42+
* webhook_events_received_at_idx, and every prior delete leaves dead index
43+
* entries in exactly that prefix for the next batch to walk. Measured against
44+
* production over 1226 batches: 35s mean, 582s worst. At the previous 20s the
45+
* deadline check after batch one always failed, so a run deleted 5k rows rather
46+
* than the 50k intended — an order of magnitude under what the ceiling implies,
47+
* and roughly break-even against intake once the backlog is drained.
48+
*/
49+
const TIME_BUDGET_MS = 240_000;
3150

3251
interface RetentionTarget {
3352
label: string;

0 commit comments

Comments
 (0)