Skip to content

Commit b4791a8

Browse files
authored
Merge pull request #972 from nteract/ai-telemetry
Add AI telemetry/provenance and prerender SEO
2 parents ac76930 + 00e3d19 commit b4791a8

28 files changed

Lines changed: 3344 additions & 51 deletions

.clinerules

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,49 @@ function SuggestedChart({ data, intent }) {
325325
}
326326
```
327327

328+
### Conversation-arc telemetry (`semiotic/ai`)
329+
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
330+
- **`enableConversationArc({ capacity?, sessionId? })`** → enables recording. Bounded ring buffer (default 1000 events). Safe to call multiple times; reuses the existing session unless `sessionId` is overridden.
331+
- **`disableConversationArc()`** → stops recording without dropping buffered events.
332+
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op).
333+
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`).
334+
335+
```ts
336+
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
337+
338+
enableConversationArc()
339+
const store = getConversationArcStore()
340+
const unsub = store.subscribe((event) => console.log(event.type, event))
341+
store.record({ type: "suggestion-shown", components: ["LineChart"], intent: "trend" })
342+
```
343+
344+
### 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.
346+
- **`provenance`**: `{ author?, source?, confidence?, createdAt?, stableId? }`. `source` is an open string union (`"user" | "ai" | "agent" | "import" | "computed" | "system" | (string & {})`).
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+
- **`withProvenance(annotation, { provenance?, lifecycle? })`** → returns a new annotation with the blocks attached. Pure, SSR-safe.
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.
351+
352+
```ts
353+
import { withProvenance } from "semiotic/ai"
354+
355+
const ann = withProvenance(
356+
{ type: "y-threshold", value: 100, label: "SLA breach" },
357+
{
358+
provenance: { author: "alice", source: "user", createdAt: "2026-05-20T14:00:00Z" },
359+
lifecycle: { ttlHint: "P30D", anchor: "semantic" },
360+
},
361+
)
362+
```
363+
364+
### Variant discovery (`semiotic/ai`)
365+
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.
366+
- **`VariantProposal`**: `{ id, baseComponent, intentDeltas?, rubricDeltas?, buildProps?, rationale?, source: "manual" | "heuristic" | "model", variantKey?, tags? }`.
367+
- **`VariantScore`**: `{ proposalId, fit (0–5), novelty (0–1), risk (0–1), reasons }`. `fit` mixes with `suggestCharts` composite scores in unified rankings.
368+
- **`proposeVariant(component, capability, context)`** → `VariantProposal[]`. M1 stub returns `[]`.
369+
- **`evaluateVariantProposal(proposal, profile, audience?)`** → `VariantScore`. M1 stub returns a neutral baseline with a reason pointing back at the design doc.
370+
- **`registerVariantDiscovery(fn)`** → registers an external proposer. `proposeVariant` dispatches through every registered function and deduplicates by `proposal.id`. Returns an unregister callback. Pair with `getRegisteredVariantDiscovery()` / `clearVariantDiscovery()` for inspection and teardown.
328371

329372
## AI Behavior Contracts
330373

.cursorrules

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,49 @@ function SuggestedChart({ data, intent }) {
325325
}
326326
```
327327

328+
### Conversation-arc telemetry (`semiotic/ai`)
329+
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
330+
- **`enableConversationArc({ capacity?, sessionId? })`** → enables recording. Bounded ring buffer (default 1000 events). Safe to call multiple times; reuses the existing session unless `sessionId` is overridden.
331+
- **`disableConversationArc()`** → stops recording without dropping buffered events.
332+
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op).
333+
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`).
334+
335+
```ts
336+
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
337+
338+
enableConversationArc()
339+
const store = getConversationArcStore()
340+
const unsub = store.subscribe((event) => console.log(event.type, event))
341+
store.record({ type: "suggestion-shown", components: ["LineChart"], intent: "trend" })
342+
```
343+
344+
### 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.
346+
- **`provenance`**: `{ author?, source?, confidence?, createdAt?, stableId? }`. `source` is an open string union (`"user" | "ai" | "agent" | "import" | "computed" | "system" | (string & {})`).
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+
- **`withProvenance(annotation, { provenance?, lifecycle? })`** → returns a new annotation with the blocks attached. Pure, SSR-safe.
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.
351+
352+
```ts
353+
import { withProvenance } from "semiotic/ai"
354+
355+
const ann = withProvenance(
356+
{ type: "y-threshold", value: 100, label: "SLA breach" },
357+
{
358+
provenance: { author: "alice", source: "user", createdAt: "2026-05-20T14:00:00Z" },
359+
lifecycle: { ttlHint: "P30D", anchor: "semantic" },
360+
},
361+
)
362+
```
363+
364+
### Variant discovery (`semiotic/ai`)
365+
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.
366+
- **`VariantProposal`**: `{ id, baseComponent, intentDeltas?, rubricDeltas?, buildProps?, rationale?, source: "manual" | "heuristic" | "model", variantKey?, tags? }`.
367+
- **`VariantScore`**: `{ proposalId, fit (0–5), novelty (0–1), risk (0–1), reasons }`. `fit` mixes with `suggestCharts` composite scores in unified rankings.
368+
- **`proposeVariant(component, capability, context)`** → `VariantProposal[]`. M1 stub returns `[]`.
369+
- **`evaluateVariantProposal(proposal, profile, audience?)`** → `VariantScore`. M1 stub returns a neutral baseline with a reason pointing back at the design doc.
370+
- **`registerVariantDiscovery(fn)`** → registers an external proposer. `proposeVariant` dispatches through every registered function and deduplicates by `proposal.id`. Returns an unregister callback. Pair with `getRegisteredVariantDiscovery()` / `clearVariantDiscovery()` for inspection and teardown.
328371

329372
## AI Behavior Contracts
330373

.github/copilot-instructions.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,49 @@ function SuggestedChart({ data, intent }) {
325325
}
326326
```
327327

328+
### Conversation-arc telemetry (`semiotic/ai`)
329+
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
330+
- **`enableConversationArc({ capacity?, sessionId? })`** → enables recording. Bounded ring buffer (default 1000 events). Safe to call multiple times; reuses the existing session unless `sessionId` is overridden.
331+
- **`disableConversationArc()`** → stops recording without dropping buffered events.
332+
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op).
333+
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`).
334+
335+
```ts
336+
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
337+
338+
enableConversationArc()
339+
const store = getConversationArcStore()
340+
const unsub = store.subscribe((event) => console.log(event.type, event))
341+
store.record({ type: "suggestion-shown", components: ["LineChart"], intent: "trend" })
342+
```
343+
344+
### 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.
346+
- **`provenance`**: `{ author?, source?, confidence?, createdAt?, stableId? }`. `source` is an open string union (`"user" | "ai" | "agent" | "import" | "computed" | "system" | (string & {})`).
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+
- **`withProvenance(annotation, { provenance?, lifecycle? })`** → returns a new annotation with the blocks attached. Pure, SSR-safe.
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.
351+
352+
```ts
353+
import { withProvenance } from "semiotic/ai"
354+
355+
const ann = withProvenance(
356+
{ type: "y-threshold", value: 100, label: "SLA breach" },
357+
{
358+
provenance: { author: "alice", source: "user", createdAt: "2026-05-20T14:00:00Z" },
359+
lifecycle: { ttlHint: "P30D", anchor: "semantic" },
360+
},
361+
)
362+
```
363+
364+
### Variant discovery (`semiotic/ai`)
365+
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.
366+
- **`VariantProposal`**: `{ id, baseComponent, intentDeltas?, rubricDeltas?, buildProps?, rationale?, source: "manual" | "heuristic" | "model", variantKey?, tags? }`.
367+
- **`VariantScore`**: `{ proposalId, fit (0–5), novelty (0–1), risk (0–1), reasons }`. `fit` mixes with `suggestCharts` composite scores in unified rankings.
368+
- **`proposeVariant(component, capability, context)`**`VariantProposal[]`. M1 stub returns `[]`.
369+
- **`evaluateVariantProposal(proposal, profile, audience?)`**`VariantScore`. M1 stub returns a neutral baseline with a reason pointing back at the design doc.
370+
- **`registerVariantDiscovery(fn)`** → registers an external proposer. `proposeVariant` dispatches through every registered function and deduplicates by `proposal.id`. Returns an unregister callback. Pair with `getRegisteredVariantDiscovery()` / `clearVariantDiscovery()` for inspection and teardown.
328371

329372
## AI Behavior Contracts
330373

.windsurfrules

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,49 @@ function SuggestedChart({ data, intent }) {
325325
}
326326
```
327327

328+
### Conversation-arc telemetry (`semiotic/ai`)
329+
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
330+
- **`enableConversationArc({ capacity?, sessionId? })`** → enables recording. Bounded ring buffer (default 1000 events). Safe to call multiple times; reuses the existing session unless `sessionId` is overridden.
331+
- **`disableConversationArc()`** → stops recording without dropping buffered events.
332+
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op).
333+
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`).
334+
335+
```ts
336+
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
337+
338+
enableConversationArc()
339+
const store = getConversationArcStore()
340+
const unsub = store.subscribe((event) => console.log(event.type, event))
341+
store.record({ type: "suggestion-shown", components: ["LineChart"], intent: "trend" })
342+
```
343+
344+
### 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.
346+
- **`provenance`**: `{ author?, source?, confidence?, createdAt?, stableId? }`. `source` is an open string union (`"user" | "ai" | "agent" | "import" | "computed" | "system" | (string & {})`).
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+
- **`withProvenance(annotation, { provenance?, lifecycle? })`** → returns a new annotation with the blocks attached. Pure, SSR-safe.
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.
351+
352+
```ts
353+
import { withProvenance } from "semiotic/ai"
354+
355+
const ann = withProvenance(
356+
{ type: "y-threshold", value: 100, label: "SLA breach" },
357+
{
358+
provenance: { author: "alice", source: "user", createdAt: "2026-05-20T14:00:00Z" },
359+
lifecycle: { ttlHint: "P30D", anchor: "semantic" },
360+
},
361+
)
362+
```
363+
364+
### Variant discovery (`semiotic/ai`)
365+
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.
366+
- **`VariantProposal`**: `{ id, baseComponent, intentDeltas?, rubricDeltas?, buildProps?, rationale?, source: "manual" | "heuristic" | "model", variantKey?, tags? }`.
367+
- **`VariantScore`**: `{ proposalId, fit (0–5), novelty (0–1), risk (0–1), reasons }`. `fit` mixes with `suggestCharts` composite scores in unified rankings.
368+
- **`proposeVariant(component, capability, context)`** → `VariantProposal[]`. M1 stub returns `[]`.
369+
- **`evaluateVariantProposal(proposal, profile, audience?)`** → `VariantScore`. M1 stub returns a neutral baseline with a reason pointing back at the design doc.
370+
- **`registerVariantDiscovery(fn)`** → registers an external proposer. `proposeVariant` dispatches through every registered function and deduplicates by `proposal.id`. Returns an unregister callback. Pair with `getRegisteredVariantDiscovery()` / `clearVariantDiscovery()` for inspection and teardown.
328371

329372
## AI Behavior Contracts
330373

0 commit comments

Comments
 (0)