You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Introduce a Temporal Lifecycle feature and integrate annotation lifecycle primitives across docs and code. Adds docs/content: new TemporalLifecyclePage, nav entry, route, and expanded CLAUDE.md notes (including the "semantic" anchor and lifecycle helpers). Replace page-local freshness logic in blog and ConversationArc demos with exported helpers (applyAnnotationLifecycle, annotationFreshnessFor) and wire the demo UIs to the shared behavior. Code changes: add realtime lifecycleBands implementation and tests, expose lifecycle-related types/APIs (semiotic-ai / semiotic-realtime), and extend Annotation props to accept opacity and strokeDasharray so lifecycle visual treatments cascade. The changes unify banded freshness classification (bandFromAge) and provide a default visual treatment for aging/stale/expired annotations.
**Streaming anchors**: `"fixed"` | `"latest"` | `"sticky"` | `"semantic"` — also exposed as `lifecycle.anchor` on the `semiotic/ai` annotation lifecycle. Same `AnnotationAnchor` type either way; `"semantic"` re-resolves via `provenance.stableId` (runtime support landing incrementally).
### Annotation provenance + lifecycle (`semiotic/ai`, types also re-exported from `semiotic`)
345
-
Type surface for "where did this annotation come from?" and "is it stale?" Optional blocks attached to any annotation — existing arrays keep working unchanged.
345
+
Tracks "where did this annotation come from?" and "is it stale?" Two optional blocks attach to any annotation — existing arrays keep working unchanged.
346
346
-**`provenance`**: `{ author?, source?, confidence?, createdAt?, stableId? }`. `source` is an open string union (`"user" | "ai" | "agent" | "import" | "computed" | "system" | (string & {})`).
347
347
-**`lifecycle`**: `{ freshness?, ttlHint?, anchor? }`. `freshness` is `"fresh" | "aging" | "stale" | "expired"`. `anchor` is `"fixed" | "latest" | "sticky" | "semantic"`. `ttlHint` accepts an ISO 8601 duration string (`"P30D"`) or milliseconds.
348
348
-**`withProvenance(annotation, { provenance?, lifecycle? })`** → returns a new annotation with the blocks attached. Pure, SSR-safe.
349
349
-**`Annotated<T>`** type alias: `T & { provenance?, lifecycle? }`. Use for explicit typing.
350
-
- Type surface only at this stage. Freshness computation, default visual treatment, and stable-id anchor resolution land later.
350
+
-**`computeAnnotationFreshness(annotations, { now?, dataExtent?, thresholds? })`** → returns annotations with `lifecycle.freshness` populated based on `createdAt` + `ttlHint`. `now` defaults to `dataExtent`'s max, then `Date.now()`. Custom thresholds override the default 1× / 1.5× / 3× TTL breakpoints.
351
+
-**`annotationFreshnessFor(annotation, nowMs, thresholds?)`** → classifies a single annotation. Honors any pre-existing `lifecycle.freshness` (explicit assignment wins).
352
+
-**`applyAnnotationLifecycle(annotations, { now?, dataExtent?, opacity?, strokeDasharray?, labelSuffix?, showExpiredAnnotations?, thresholds? })`** → composes the freshness pass with a default visual treatment: aging dims (opacity 0.55), stale dims more + dashes (strokeDasharray `"4 4"`), expired is filtered out (set `showExpiredAnnotations: true` to keep). Per-band overrides via the options; pass `null` for a band to disable that default. Annotation `opacity` / `strokeDasharray` set explicitly on the annotation win over the treatment. Annotation renderer cascades both as SVG presentation attributes to every stroked / filled child.
353
+
- Still owed (M3): stable-id anchor resolution after data refresh.
### Temporal lifecycle (shared between `semiotic/realtime` + `semiotic/ai`)
371
+
Three Semiotic systems answer "how does this thing look as it ages?" with three different policies on three different time axes. They are *not* interchangeable — pick the one that matches the question.
372
+
373
+
| Policy | Lives in | Time axis | Output | Scope |
374
+
|---|---|---|---|---|
375
+
|`DecayConfig`|`semiotic/realtime`| buffer position | continuous opacity ramp | per-datum |
Shared primitive that bridges them: **`bandFromAge(ageMs, ttlMs, thresholds?)`** → `"fresh" | "aging" | "stale" | "expired"`. Pure function exported from both `semiotic/realtime` and `semiotic/ai`. `DEFAULT_LIFECYCLE_THRESHOLDS = { fresh: 1.0, aging: 1.5, stale: 3.0 }` (multipliers of TTL). Annotation freshness uses this primitive today; future banded-decay or banded-staleness opt-ins can plug into the same classifier.
380
+
381
+
**Anchor mode** (`"fixed" | "latest" | "sticky" | "semantic"`) is also shared — `AnnotationAnchor` from `semiotic/realtime` is the canonical type, re-exported from `semiotic/ai` as `lifecycle.anchor`. `"semantic"` re-resolves via `provenance.stableId` (runtime support landing incrementally).
382
+
383
+
**Streaming chart-time aging**: pass the chart's `dataExtent` to `applyAnnotationLifecycle` and the latest data point becomes the "now" reference. Annotations age against chart-time, not wall-clock — useful when streams pause or run slow. Pair with `withCurrentProvenance(annotation, { author?, source? })` or `currentTimestamp()` to auto-stamp `createdAt` at the moment of creation.
Full survey at `/intelligence/temporal-lifecycle`.
401
+
364
402
### Variant discovery (`semiotic/ai`)
365
403
Interface for proposing and scoring chart variants beyond the hand-curated `capability.variants`. Heuristic and model-based proposers plug in through `registerVariantDiscovery`. M1 ships the type surface + stub implementations; behavior arrives in subsequent milestones.
0 commit comments