feat(subscription): SDK factory + pipeline - #95
Open
pl wants to merge 4 commits into
Open
Conversation
Add dashboardIdByKey / dashboardKeyByServerId to ApplyContext, populated by the dashboard executor (create/update/unchanged) and the plan display pass, mirroring the existing insightIdByKey wiring. Lets resources that reference a dashboard by key — annotations (scope: dashboard) and subscriptions — resolve the server id at execute time and render the key in plans. No behavior change for existing resources; the maps are additive. Note: identical to the same commit on the annotations branch (#94). If both merge, the ApplyContext addition dedupes trivially. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull orders the targeted `--kind` subset with topoOrder and its comment already claimed it "tolerates missing edges" — but topoOrder threw when a target's dependsOn pointed outside the input set. So `pull --kind subscriptions` (which depends on insights + dashboards) failed unless you also named those kinds. Add a `lenient` option to topoOrder that drops out-of-set edges instead of throwing, and use it from pull. Apply still uses strict mode. Note: identical to the same commit on the annotations branch (#94); dedupes trivially if both merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add subscriptions list/create/retrieve/partial_update to the openapi allowlist
and regenerate src/generated/api.d.ts. No destroy — subscriptions soft-delete
via PATCH { deleted: true } (the DELETE verb is 405).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the subscriptions resource (scheduled insight/dashboard delivery): SDK
factory, Zod-validated client, title-marker identity, cross-resource refs,
pull codegen, unit tests, two examples.
Identity: the endpoints/surveys marker pattern, carried in `title`. A
subscription references exactly one insight or dashboard by key (resolved to
an id at execute time via ApplyContext; dependsOn insights + dashboards);
validation enforces exactly-one-ref and same-run declaration. Email/Slack
targets, recurrence, and AI-summary fields round-trip as plain fields. Slack
`integrationId` round-trips and is hashed, but is environment-specific
(documented as non-portable).
Delivery safety (per the coordinator's warning): `send_test_now` defaults to
TRUE on the API and fires an immediate delivery on save. The client forces it
false on every create AND update (and the pull tag-back), so apply never
delivers as a side effect. Live-verified the subscription's deliveries list
stayed empty (count 0) across create and update. Note: applying an *email*
subscription still invites its recipients (inherent product behavior) — use
real addresses only when intended; verification used example.com throwaways.
Surprise / constraint: the server caps `title` at 100 chars, and `title` also
carries the identity+hash marker (~53 + key length). Effective budget is
title + key <= ~45. Other free-text fields aren't viable carriers (`prompt` is
mutually exclusive with insight/dashboard; `invite_message` is email-only and
user-facing). Validation now fails fast with an actionable message when
title + marker would exceed 100 (no silent loss; the API 400 is pre-empted).
Because a STAMP-length smoke key can't round-trip through pull within that
budget, subscriptions are excluded from the smoke seed (like singletons) and
verified in isolation instead.
Live-verified on project 806: create -> no-op re-apply (hash projection
correct) -> edit -> single update -> cross-resource ref (insight resolved to
id) -> orphan left alone -> hand-built subscription (no marker) untouched ->
kind-scoped prune deletes managed only. Delete is soft-delete via PATCH
{deleted:true} (DELETE -> 405).
Scope: subscription:read / subscription:write (+ insight/dashboard scopes).
Stacks on #81 (pl/spec-migration); shares the ApplyContext dashboardIdByKey
and lenient-pull commits with #94 (dedupe if both merge). Retarget to main when
#81 merges.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pl
added a commit
that referenced
this pull request
Jul 23, 2026
Records the subscription title-length constraint (100-char cap shared with the marker) and the carrier-investigation lesson it taught. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds subscriptions (scheduled insight/dashboard delivery) — second of the two Wave 2 greenlit resources.
Identity
Title-marker pattern (
iac:subscriptions:<key>intitle) as designed. A subscription references exactly one insight or dashboard by key (resolved to an id at execute time; validation enforces exactly-one + same-run declaration). Email/Slack targets, recurrence, and AI-summary fields round-trip as plain fields.Delivery safety (your warning — handled + verified)
send_test_nowdefaults totrueon the API and delivers immediately on save. The client forces itfalseon every create, update, and the pull tag-back — apply never delivers as a side effect. Live-verified the deliveries list stayed empty (count 0) across create and update. Note: applying an email subscription still invites its recipients (inherent product behavior) — verification usedexample.comthrowaways; documented in the SDK.The server caps
titleat 100 chars, andtitlealso carries the identity+hash marker (~53 + key length). Effective budget:title+key≲ 45. Other free-text fields aren't viable carriers (promptis mutually exclusive with insight/dashboard;invite_messageis email-only and user-facing;summary_prompt_guideis AI-only). I didn't catch this in the identity investigation (used short values). It's not silent loss — validation now fails fast with an actionable message when title+marker would exceed 100 (pre-empting the API 400) — but it's a real usability limit on title length. Flagging in case you'd rather revisit the carrier; the resource is fully functional for normal-length titles.Because a STAMP-length smoke key can't round-trip through pull within that budget, subscriptions are excluded from the smoke seed (like singletons, with a header note) and verified in isolation.
Shared infra (dedupe with #94)
This branch reintroduces two commits identical to the annotations PR (#94):
feat(apply): expose dashboard ids by key in ApplyContext(needed for dashboard refs) andfix(pull): tolerate dependency kinds absent from a --kind subset. If both merge, they dedupe trivially.Live verification (project 806)
create → no-op re-apply (hash projection correct) → edit → single update → cross-resource ref (insight resolved to id) → orphan left alone → hand-built subscription (no marker) untouched → kind-scoped prune deletes managed only. Delete is soft-delete via PATCH
{deleted:true}(DELETE → 405).Scope:
subscription:read/subscription:write(+insight:*/dashboard:*). Gates green (typecheck / typecheck:examples / test 307 / lint).Stacks on #81 (base
pl/spec-migration). Retarget tomainwhen #81 merges.🤖 Generated with Claude Code