feat(annotation): SDK factory + pipeline - #94
Open
pl wants to merge 5 commits into
Open
Conversation
Add annotations list/create/retrieve/partial_update to the openapi allowlist
and regenerate src/generated/api.d.ts. No destroy — annotations soft-delete
via PATCH { deleted: true } (the DELETE verb is 405).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the annotations resource: SDK factory, Zod-validated client,
content-marker identity, cross-resource refs, pull codegen, unit tests, two
examples.
Identity: the endpoints/surveys marker pattern, carried in `content`
(iac:annotations:<key>) — the only free-text field. `scope`
project/organization/dashboard_item/dashboard is declarative; `date_marker`,
`emoji`, `creation_type` (USR/GIT), and `hidden` (hidden_in_user_interface)
round-trip as plain fields. Scoped annotations reference an insight
(dashboard_item) or dashboard (dashboard) by key, resolved to ids at execute
time via ApplyContext (dependsOn insights + dashboards); validation enforces
exactly-one-ref-per-scope and same-run declaration.
Marker visibility (the coordinator's UI check): the chart tooltip/overlay
renders content through LemonMarkdown, which strips the trailing HTML comment
— so the marker is invisible on charts, the primary surface. The annotations
management *list* scene renders content as plain JSX, so the marker shows as
literal trailing text there. `hidden: true` (recommended for deploy markers)
removes the annotation from both surfaces entirely. Known cosmetic cost on the
list scene only; annotations are operational metadata (lower bar than alert
names) — greenlit.
Live-verified on project 806: create -> no-op re-apply (hash projection
correct) -> edit -> single update -> cross-resource ref (dashboard_item
annotation resolved a same-run insight to its server id) -> orphan left alone
-> hand-built annotation (no marker) untouched -> kind-scoped prune deletes
managed only. Delete is soft-delete via PATCH {deleted:true} (DELETE -> 405).
Scope: annotation:read / annotation:write (+ insight/dashboard scopes for
scoped annotations).
Stacks on #81 (pl/spec-migration); retarget to main when #81 merges.
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 annotations` (which depends on insights + dashboards) failed unless you also named those kinds, even though pull resolves cross-resource references at render time via the optional-import path. Add a `lenient` option to topoOrder that drops out-of-set edges instead of throwing (and gates readiness only on in-set deps), and use it from pull. Apply still uses strict mode. Exposed by the annotations resource — the first pull-capable resource with dependencies that a user would plausibly pull alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a project-scoped hidden deploy-marker annotation to the smoke seed, plus the SMOKE_KINDS entry and smoke-cleanup wiring. Content == key so pull (which derives an annotation's key by slugifying its content — annotations have no `name`) round-trips the key and the trap cleanup finds the row. Verified in isolation: seed apply -> pull -> tag-back -> apply --dry-run no-op -> cleanup by key. As with the other new resources, the full smoke.sh run stays red at the pull step on the pre-existing `actions` pull gap on pl/spec-migration (lands on pl/pull-actions). 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 annotations as a managed resource (first of the two Wave 2 greenlit resources).
Identity
Content-marker pattern (
iac:annotations:<key>incontent, the only free-text field) — as designed in the identity investigation.Marker visibility (the UI check you asked for)
Checked the frontend render paths:
contentthroughLemonMarkdown, which strips the trailing HTML comment → the marker is invisible there.contentas plain React JSX → the marker shows as literal trailing text there (a secondary admin view).hidden: true(recommended for deploy markers) removes the annotation from both surfaces entirely.So the cosmetic cost is limited to the annotations list scene, and avoidable with
hidden. Annotations are operational metadata (lower bar than alert names) — noted as a known cost per your call.Scope + refs
scopeproject / organization / dashboard_item / dashboard is declarative;date_marker,emoji,creation_type(USR/GIT),hiddenround-trip as plain fields. Scoped annotations reference an insight (dashboard_item) or dashboard (dashboard) by key, resolved to ids at execute time; validation enforces exactly-one-ref-per-scope and same-run declaration.Supporting infra
feat(apply): expose dashboard ids by key in ApplyContext— addsdashboardIdByKey/dashboardKeyByServerId(populated by the dashboard executor + plan pass), mirroringinsightIdByKey. Needed forscope: dashboardrefs; subscriptions (next PR) needs the same field — heads up for a trivial dedupe if both merge.fix(pull): tolerate dependency kinds absent from a --kind subset—pull --kind annotations(deps on insights+dashboards) threw in topoOrder even though pull resolves refs at render time. Added alenientmode to topoOrder (drops out-of-set edges) that pull uses; apply stays strict. Exposed by annotations (first pull-capable resource with deps a user would pull alone).Live verification (project 806)
create → no-op re-apply (hash projection correct) → edit → single update → cross-resource ref (a
dashboard_itemannotation resolved a same-run insight to its server id) → orphan left alone → hand-built annotation (no marker) untouched → kind-scoped prune deletes managed only. Delete is soft-delete via PATCH{deleted:true}(DELETE → 405).Surprise
Pull derives an annotation's key by slugifying
content(annotations have noname), so a pulled annotation's key follows its content. The smoke fixture setscontent == keyso the key round-trips for the trap cleanup.Scope:
annotation:read/annotation:write(+insight:*/dashboard:*for scoped annotations). Gates green (typecheck / typecheck:examples / test 307 / lint). Smoke wiring verified in isolation.Stacks on #81 (base
pl/spec-migration). Retarget tomainwhen #81 merges.🤖 Generated with Claude Code