diff --git a/.agents/skills/conformance/SKILL.md b/.agents/skills/conformance/SKILL.md new file mode 100644 index 00000000..2acd3479 --- /dev/null +++ b/.agents/skills/conformance/SKILL.md @@ -0,0 +1,61 @@ +--- +name: conformance +description: "Behavioral conformance check across GraphReFly language runtimes (ts/rust/py) for the clean-slate redesign. Replaces the old structural 'parity' diff. Parity = does each runtime satisfy the wave-protocol behavior (conformance scenarios) + dispatcher contract — NOT 'do the symbol sets match'. Use after implementing/changing substrate behavior in any runtime, or when adding a new protocol rule. Authors/runs language-agnostic scenarios and updates conformance.jsonl runtime status. Triggers: 'conformance', 'cross-lang check', 'does rust match', 'parity', 'run the conformance suite', 'is the substrate behavior consistent'." +argument-hint: "[rule-id | scenario-id | 'full'] [optional: runtime ts|rust|py]" +--- + +You are executing **conformance** for the clean-slate GraphReFly redesign. + +**Parity is behavioral, not structural (D24).** There is NO `Impl` symbol-set to diff and NO +cross-track-ledger. Operators / sugar / inspection are **per-language and never in parity** +(D6/D27 — graph-layer wraps everything to `(ctx)=>void` before register). The ONLY parity +surface is: **wave-protocol behavior + dispatcher contract + handle format**. Conformance = +each runtime passes the same language-agnostic scenarios. + +## Authority + +| Source | Role | +|---|---| +| `~/src/graphrefly/spec/conformance.jsonl` | The scenario registry: `{id, name, covers:[rule-id], runtimes:{ts,rust,py}, status, note}`. | +| `~/src/graphrefly/spec/rules.jsonl` | The rules scenarios pin (`covers` must resolve here). | +| `~/src/graphrefly/spec/protocol.proto` | Protocol-contract IDL (DR-2) — the light structural anchor codegen'd into each runtime's interface stub. | +| `~/src/graphrefly/formal/*.tla` | TLA+ model (γ); property tests mirror its invariants. | + +## Scope from $ARGUMENTS + +- **rule-id** (e.g. `R-diamond`) → all scenarios whose `covers` includes it. +- **scenario-id** (e.g. `C-1`) → that scenario. +- **full** → every `status:"required"` scenario. +- optional **runtime** → restrict to one arm. + +## Phase 1 — scenario integrity + +1. Load `conformance.jsonl` + `rules.jsonl`. Verify every `covers` rule-id resolves (else HALT — fix the scenario or add the rule via `/spec-amend`). +2. List the **DR-5 required hard scenarios** and their status: `C-1` cross-graph diamond, `C-2` async-result-at-paused-node, `C-3` INVALIDATE×ctx.state×onInvalidate, `C-4` mixed sync/async diamond, `C-5` PAUSE-lockset multi-source. These are the load-bearing ones — behavioral parity is a blank cheque until they're green on each shipped runtime. + +## Phase 2 — run / verify per runtime + +For each in-scope `(scenario, runtime)`: +1. Locate/author the scenario harness in that runtime's conformance test dir (language-agnostic spec → per-runtime adapter; the scenario describes observable wave behavior, not a symbol call). +2. Run it. Record outcome. +3. Update `conformance.jsonl` `runtimes.`: `"todo" | "poc-pass" | "pass" | "fail"`. +4. Mirror the invariant as a property test (fast-check ts ↔ proptest rust ↔ hypothesis py) where the rule is property-shaped (L5-Q2 / D14). + +## Phase 3 — report + +| scenario | covers | ts | rust | py | verdict | +|---|---|---|---|---|---| + +- **Behavior drift** = same scenario, different observable outcome across runtimes → this is the ONLY kind of parity gap. File it as a substrate bug in the lagging runtime (route fix via `/dev-dispatch` on that package). +- **Missing scenario** for a rule (rule's `covers_by` empty) → author it (this is the real risk under behavioral parity: untested behavior can drift silently — D24 residual). Flag via `/dashboard` Gaps (uncoveredRules). +- **NOT a gap:** a runtime having a different operator set / different sugar / different inspection ergonomics. Those are per-language by design — do not report them. + +## Phase 4 — gate + +Run `node ~/src/graphrefly/dashboard/build.mjs --check` (scenario↔rule links intact). For a runtime +to be declared "conformant", all `status:"required"` scenarios must be `"pass"` on its arm. + +## Boundaries + +Does NOT diff symbol sets (that's the retired structural model). Does NOT touch operators/sugar/inspection +(per-language). New protocol behavior must go through `/spec-amend` FIRST (scenario authored before code). diff --git a/.agents/skills/dashboard/SKILL.md b/.agents/skills/dashboard/SKILL.md new file mode 100644 index 00000000..a2903ef4 --- /dev/null +++ b/.agents/skills/dashboard/SKILL.md @@ -0,0 +1,33 @@ +--- +name: dashboard +description: "Build / check the GraphReFly internal docs dashboard (jsonl single-source -> generated HTML with progress, structure map, gaps, search). Use when the user wants to see global project state, regenerate the dashboard, run the docs consistency gate (broken links / orphans / coverage gaps), or after editing any jsonl in ~/src/graphrefly (decisions/plan/spec/sessions/guide). Triggers: 'build the dashboard', 'check docs consistency', 'what are the gaps', 'show progress', 'regenerate dashboard', 'doc gate'." +argument-hint: "[--check (gate only) | (default: build + report)]" +--- + +You are executing **dashboard** for the clean-slate GraphReFly redesign. + +**Repo:** `~/src/graphrefly` (clean-slate branch). All structured docs are jsonl (single source of truth, decision 2); the dashboard renders them into one searchable HTML view for the maintainer (decision 3). Schema contract: `~/src/graphrefly/dashboard/README.md`. + +## What this skill does + +1. **Run the generator:** + - `node ~/src/graphrefly/dashboard/build.mjs` → writes `dashboard/dashboard.html` + prints counts / gaps / broken-links / orphans report. + - `node ~/src/graphrefly/dashboard/build.mjs --check` → consistency gate only; **non-zero exit on broken links** (use as a pre-commit / CI gate). +2. **Interpret the report** for the user: + - **counts** — per-jsonl row counts (decisions/phases/rules/conformance/...). + - **gaps** — designPhases (status=design|gap) · openDecisions · deferredBacklog · uncoveredRules (no conformance) · todoConformance (runtimes=todo). This answers "哪里还有缺口". + - **broken links** — must be zero (session.locks → decision; phase.sessions → session; conformance.covers → rule; flowchart.explains → rule|D#). Legacy 3-digit D### / R# refs are external (old main), reported separately as OK. + - **orphans** — decisions referenced by no session (informational). +3. **If broken links exist:** locate the offending jsonl row, fix the id reference (or add the missing record), re-run `--check`. + +## When the jsonl changed + +After any edit to `decisions/`, `plan/`, `spec/`, `sessions/`, `guide/` jsonl — run `--check` to catch dangling references immediately (fixes P4 stale-premise + P6 link-rot). The generator is the enforcement mechanism for "single canonical, no broken cross-refs." + +## UI styling + +`build.mjs` emits a **placeholder shell** with the data model embedded. Visual design / interactive search is a separate `/frontend-design` pass — do NOT hand-style the HTML here; keep `build.mjs` focused on the data model + consistency checks. The dogfood endgame (phase CSP-8) rebuilds this dashboard *with GraphReFly itself* (jsonl producer → reactive views → HTML effect). + +## Output + +The counts + gaps + link-health report, a plain-language "where are the gaps / what's the progress" summary, and (unless `--check`) confirmation that `dashboard.html` was written. Flag any broken link as a blocker to fix before commit. diff --git a/.agents/skills/decision-guard/SKILL.md b/.agents/skills/decision-guard/SKILL.md new file mode 100644 index 00000000..78f64884 --- /dev/null +++ b/.agents/skills/decision-guard/SKILL.md @@ -0,0 +1,87 @@ +--- +name: decision-guard +description: "GraphReFly clean-slate decision-consistency check. Loads the user's locked values/principles + the unified D-numbered decision log (decisions.jsonl) + recurring decision-process patterns. Use BEFORE answering any question of the form 'is this consistent with our decisions?', 'should I pick option A/B/C?', 'what about this proposed fix?', 'is X part of our scope?', 'is this a regression on a prior decision?'. Triggers: 'decision check', 'drift check', 'align check', 'is this consistent', 'should I pick', 'what about this', 'is this in scope', 'consistency review'." +argument-hint: "[short context of what you're being asked about — paste the proposal if relevant]" +--- + +# decision-guard — recall and apply locked decisions, values, invariants (clean-slate) + +**Purpose.** Conversations lose context-window state quickly. This is the canonical recall +surface for the **clean-slate** redesign: invoke BEFORE answering decision questions to anchor +against the user's locked positions and prevent silent drift — especially when a chat proposes +a scope expansion mid-implementation, presents A/B/C as a fork, uses "completeness" to justify +expanding a locked slice, or builds on a premise that may be stale. + +> **Clean-slate retired the old port model.** Do NOT reach for `rust-port-decisions.md`, +> `cross-track-ledger.md`, the `Impl` parity contract, `BindingBoundary`, the actor model, or +> 3-digit D### port decisions — those are old-`main` history. The clean-slate decision +> authority is below. + +## Sources (load in order) + +| Source | Role | +|---|---| +| `~/src/graphrefly/decisions/decisions.jsonl` | **Unified D# log** (D1–D33 + DR-*). Canonical record: `{id, layer, question, decision, rationale, supersedes, status}`. | +| `~/src/graphrefly/sessions/active/SESSION-clean-slate-redesign.md` (DS-1) | Full design narrative + 8 forced constraints + spec-amendment list + conformance hard scenarios. | +| `~/src/graphrefly/plan/antipatterns.jsonl` | Lessons / anti-patterns to flag against. | +| `~/src/graphrefly/spec/rules.jsonl` | Protocol rules — for "does the spec already pin this?". | +| Memory `feedback_*` files | The user's durable values/principles (below). | + +## Locked values (durable — cite by name) + +1. **No backward compat** (pre-1.0): structurally cleaner option, no legacy shims. `feedback_no_backward_compat`. +2. **No imperative triggers** in public API: reactive `ctx.up`/signals, not emitters/callbacks/timers+set; remove imperative paths when no caller depends. `feedback_no_imperative`. +3. **Single source of truth**: one canonical per concern; index points, never duplicates. `feedback_single_source_of_truth`. +4. **No autonomous decisions** (hard rule): surface spec↔code conflicts; don't silently pick; file-by-file review for multi-file rewrites. `feedback_no_autonomous_decisions`. +5. **No implement without approval**: decisions locked ≠ implementation approved. `feedback_no_implement_without_approval`. +6. **Verify premise before greenfield**: design tables lag code — grep symbols + check landed markers before a 9Q; stale premise = HALT. `feedback_verify_premise_before_greenfield`. +7. **Latest versions + context7** for current API docs. `feedback_latest_versions_context7`. +8. **Long-command observation discipline** (run-logged + DONE sentinel; no tail; no sleep-poll) and **subagent bg hygiene** (sync-verify or teardown before return). `feedback_long_command_observation`, `feedback_subagent_bg_hygiene`. + +## Clean-slate floor (never violate) + +**Sacred (L0.7):** topology declarative/serializable/inspectable · wave protocol is a public spec · +wave protocol impl is **sync** · all fn go through dispatcher. + +**Forced (F-*):** F-PERF (budget every abstraction) · F-PROTO-SPEC (spec+TLA++property) · +F-SYNC-CORE (dispatcher.invoke sync void) · F-DISPATCH-ALL (no inline-fn bypass) · +F-GRAPH-FIRST-API · F-NO-WEDGE-CUT (every primitive ≥2 segments) · +F-NO-IMPL-DEFINED (spec-locked or explicitly undefined-by-design) · F-NO-LLM-ONLY. + +**Red flags (HALT if proposed):** async in the sync wave core (async lives only in pools/wire-bridge) · +inline-fn bypassing dispatcher · a primitive serving only LLM workflows · a protocol behavior left +"implementation-defined" · user-replaceable onMessage/onSubscribe · adding a 10th tier casually · +graph-level shared mutable state accessed implicitly (must be explicit node + dep). + +## Decision-process patterns (apply in order) + +1. **Identify the governing D#.** Grep `decisions.jsonl` by `layer`/keyword. Is the proposal within a locked D's scope? Mid-implementation scope expansion = anti-pattern unless promoted to a NEW D#. +2. **Check the spec.** Does `rules.jsonl` pin the behavior? If yes, follow it — divergence is a bug, not a design call. If silent/ambiguous → real design HALT. +3. **Verify premise (value 6).** Has the symbol/surface already landed? grep before designing new surface. +4. **Apply values + floor.** Especially: no autonomous decisions, no imperative, single source of truth, sync-core/async-at-boundary, F-* constraints. +5. **Verdict:** `consistent (cite D#)` / `regression on D#` / `out-of-scope` / `needs new decision (don't auto-pick)`. +6. **Routing:** + - New fork, no governing D# → present options + recommend, **do NOT lock** → that's `/design-review` → user approval → append `decisions.jsonl`. + - Changes protocol behavior → `/spec-amend` (spec-first). + - Cross-runtime parity concern → `/conformance` (behavioral scenario, not structural diff). + +## Common decision shapes + +- **A/B/C (fix shape):** default = the option that **structurally extends an existing pattern** beats per-site workarounds. +- **Completeness vs discipline:** if a proposal closes a real semantic gap → formalize as a NEW D# (don't continue under the original D's banner). If speculative scope expansion → revert. +- **Orthogonal sub-decisions:** before locking two "orthogonal" sub-decisions, sketch ONE input exercising BOTH — confirm orthogonality survives the example (antipatterns.jsonl; the 30-second check that catches coupling at design-time). + +## Scope boundaries + +Read-mostly. Loads decisions + values + patterns; produces **decision + reasoning + relay-ready text**. +Does NOT run gates, apply fixes, or author scenarios — those are `/dev-dispatch` / `/qa` / `/conformance` +after a decision locks. Invoke when the question is "what should I decide?", not "what should I do?". + +## Update protocol + +When a new D# locks (after user approval): append to `~/src/graphrefly/decisions/decisions.jsonl` +(`{id, layer, date, question, decision, rationale, supersedes, status:"locked", session}`), update the +session's `locks` in `sessions.jsonl`, and run `node ~/src/graphrefly/dashboard/build.mjs --check`. +When a new anti-pattern recurs: append to `~/src/graphrefly/plan/antipatterns.jsonl` (+ a `feedback_*` +memory if generalizable). When a new durable value surfaces: add a `feedback_.md` memory + a +pointer line here. diff --git a/.agents/skills/design-review/SKILL.md b/.agents/skills/design-review/SKILL.md new file mode 100644 index 00000000..5bc4267f --- /dev/null +++ b/.agents/skills/design-review/SKILL.md @@ -0,0 +1,146 @@ +--- +name: design-review +description: "Validate the design of a new primitive or API surface against the 5-question lens (Q5–Q9 from the per-unit review format). Use BEFORE coding (or right after a sketch lands) when adding a new public API / pattern factory / domain primitive. Triggers: 'design review', 'review the design', 'is this the right shape', 'before I implement'. Different from /qa — that finds bugs in landed code; this validates abstraction + long-term shape + reactive composability + alternatives." +argument-hint: "[ | | --diff] [optional context]" +--- + +You are executing the **design-review** workflow for the **clean-slate GraphReFly** redesign. + +This skill applies the 5 design-review questions (Q5–Q9 of the 9-question per-unit format) to a **single-symbol / single-file / single-diff** review. Use it BEFORE coding a new public API / sugar factory / operator / inspection surface — or right after a sketch lands, before tests. Different from `/qa` (finds bugs in landed code) and `/decision-guard` (recalls locked decisions); this validates abstraction + long-term shape + reactive composability + alternatives, and its output may become a new `D#` (architectural lock → user approval → append `decisions.jsonl`). + +Clean-slate code lives in **`packages/ts/src/`** (`@graphrefly/ts`, D32). The language-neutral authority is **`~/src/graphrefly`** jsonl (branch `clean-slate`) — when this skill and that repo disagree, that repo wins (AGENTS.md). + +> **Stale-infra guard.** Do NOT cite the retired port-model: `packages/pure-ts/**` (frozen read-only reference only, D41), `docs/implementation-plan.md` / `optimizations.md` / `roadmap.md` / `test-guidance.md` / `docs-guidance.md`, `GRAPHREFLY-SPEC.md` / `COMPOSITION-GUIDE.md` (migrated to `spec/rules.jsonl` + `guide/guide.jsonl`, B7), `describe({format})` (D39: renderers are pure fns over the snapshot, NOT a `format` option), the `Impl`/facade/actor model. + +**When to use:** before a new public symbol in `packages/ts/src/graph/` (sugar / operator / inspection) or a new substrate primitive in `packages/ts/src/{node,dispatcher,ctx,protocol,batch}/`; right after sketching a factory; when two implementations exist and you need a principled pick; when `/dev-dispatch` Phase 2 needs to go deeper than its default template. +**When NOT:** bug fixes / pure refactors → `/qa`; already-approved work → proceed; trivial additive changes (a JSDoc field, a docstring). + +User context: $ARGUMENTS + +--- + +## Phase 0: Scope resolution + +Resolve the target(s) from `$ARGUMENTS`: + +1. **`--diff` / no args** — review the new public symbols in the uncommitted diff. Enumerate via `git diff --name-only HEAD` + `git status --short`, filtered to `packages/ts/src/**` files that introduce new exports. +2. **``** — public symbols in that file. +3. **``** — Grep-locate, then review. +4. **Multiple targets** — apply Q5–Q9 to each, then add Phase 2 synthesis. + +Read in parallel before reviewing (clean-slate authority): + +- `~/src/graphrefly/spec/rules.jsonl` — the R-* rules your target touches (the design invariants). +- `~/src/graphrefly/decisions/decisions.jsonl` — the governing D# (or `/decision-guard` to recall the floor/values). +- `~/src/graphrefly/plan/phases.jsonl` — the CSP-* phase the target belongs to (locked vs open-design); if the target isn't sequenced yet, the review's output may need a new phase / backlog entry. +- `~/src/graphrefly/guide/guide.jsonl` (G-composition) — composition patterns (lazy activation, subscription order, SENTINEL/prevData guards, feedback cycles). +- `~/src/graphrefly/sessions/active/SESSION-clean-slate-redesign.md` (DS-1) — the F-* constraints + the why behind locks. +- The target file(s) + 1–2 closest existing primitives in the same dir (precedent). +- **Frozen reference (D41):** `packages/pure-ts/**` + `~/src/callbag-recharge` for analogous prior art (operator behavior, edge cases, test structure) — NOT the authority. + +--- + +## Phase 1: Per-target review (Q5–Q9) + +For each target, produce a structured report covering all five questions. Be specific, quote `file:line`. Cap each answer ~150 words. + +### Q5 — Is this the right abstraction? Could it be more generic? + +- **Layer placement.** Substrate (`node`/`dispatcher`/`ctx`/`protocol`/`batch` — a protocol primitive, must stay thin per R-node-thin) vs graph-layer (`graph/` — sugar / operator / inspection, per-language per D6/D24)? Mismatched layer is the most common drift signal. A new **verb** is a constitutional change (8-verb closed set, D4) — almost certainly the target is sugar, not a verb. +- **Decomposition.** Could the body split into 2+ smaller primitives that compose? (F-NO-WEDGE-CUT: each must serve ≥2 segments.) +- **Generalization.** A 2+ similar primitive nearby hinting at a shared abstraction? Could `T = number` be `T = unknown` without losing safety? +- **Naming.** Does the name describe what it **returns/produces** (composable) or what it does internally (rots)? D6 real factory names show in `describe`. + +> **Layer / Decomposition / Generalization / Naming:** … + +### Q6 — Right long-term solution? Caveats? Maintenance burden? + +- **6-month lens.** What forces this to evolve — spec/conformance changes, rust/py arm parity (D24), F-PERF budget? +- **Hidden invariants** the type system can't express — list each as `INVARIANT: …` (subscribe order before kick; first-run gate; SENTINEL = `prevData === undefined`; sync-vs-async strategy; stable refs; equals identity). +- **Constraint locks** (positional args can't grow options; hardcoded enum can't extend). +- **Doc debt** (contract knowable from JSDoc, or only from the body?). + +> **6-month risk / Hidden invariants / Constraint locks / Doc debt:** … + +### Q7 — Can we simplify it? Reactive, composable, explainable? + +The **explainability check** — a primitive's reactive shape is only as good as its `describe()` snapshot (D39). + +- **Wire a minimal composition** (≥2 sources → target → ≥1 sink). Predict `describe()` — a flat JSON-serializable snapshot; renderers (pretty/mermaid/d2) are pure fns over it (D39), NOT a `describe({format})` option. If you can't predict the output, the topology is too imperative. +- **Island check.** A node with zero in-edges AND zero out-edges (not an entry/exit) is a smell. +- **Imperative escape paths.** Search for: emit/set/callback wiring that bypasses the graph (R-no-imperative); `.cache` reads inside reactive fn bodies (R-data-not-peek — data moves via messages); raw `Promise`/`setTimeout`/`queueMicrotask` outside a source/pool (R-no-raw-async / F-SYNC-CORE); hardcoded `type === "DATA"` instead of `messageTier` (R-tier); an inline fn bypassing the dispatcher (R-dispatch-all). +- **SENTINEL / prevData guards.** Never-emitted detection via `ctx.prevData[i] === undefined` (the canonical detector); fix eager-placeholder upstreams rather than bolting on companions. +- **Feedback cycles.** A fn that re-drives its own dep mid-wave is a wave-level ERROR (D37/R-reentrancy), not iteration; legit accumulation = `ctx.state` (scan), not a topological cycle. + +> **Topology / Imperative leaks / cache reads / Feedback cycles / Simplifications:** … + +### Q8 — Alternative implementations (A / B / C) + +Sketch **≥2** named alternatives. For each: **Shape** (1–3 line pseudo-sig), **Pros** (2–4), **Cons** (2–4), **Precedent** — does the shape exist in the frozen `packages/pure-ts/**` reference (D41), `callbag-recharge`, or RxJS? Cite if so. Don't pick a winner yet — that's Q9. + +> **A. {name}** — sketch / Pros / Cons / Precedent +> **B. {name}** — … + +### Q9 — Recommendation + coverage check + +Pick the recommended alternative; build a coverage matrix (each Q5–Q8 concern → recommended alt covers it? yes / partially / no — because …). For any `partially`/`no`, name the residual risk: accept it (justify), add a `backlog.jsonl` follow-up, or pick a different alternative. End with: + +> **Recommendation:** {alt}, because {2–3 reasons grounded in Q5–Q8}. +> **Residual risks:** {none, OR 1–2}. +> **Implementation guidance:** {next step — usually a draft `D#` for approval, or a sub-decision the user must answer first}. + +If the design is an architectural lock, draft the `D#` (`{id, layer, date, question, decision, rationale, supersedes, status}`) for the user to approve BEFORE append — do NOT auto-lock (no-autonomous-decisions). A wave-protocol behavior change routes to `/spec-amend` (spec-first), not a direct edit. + +--- + +## Phase 2: Cross-cutting synthesis (multi-target only) + +Apply only when reviewing multiple targets in one pass: + +- **Naming consistency** (`extract` vs `select` vs `pick` for the same role = drift). +- **Argument-shape consistency** (options-bag vs positional applied the same way). +- **Composition direction** (do the targets' input/output shapes line up to compose?). +- **Repeated patterns** (the same SENTINEL-gate / subscribe-order / batch-on-write in 2+ places → a shared helper candidate). + +Output a numbered list, each finding with the pattern, where it appears (file:line × N), and a proposed unifying shape. + +--- + +## Phase 3: Decisions log + +- **Architectural lock** (clear) → draft a `D#` for `~/src/graphrefly/decisions/decisions.jsonl`; append only after user approval, then update the DS-1 `locks` in `sessions/sessions.jsonl` and run `node ~/src/graphrefly/dashboard/build.mjs --check`. +- **Deferred / no clear answer** → append to `~/src/graphrefly/plan/backlog.jsonl` (B# + concrete trigger). +- **Recurring anti-pattern** → `~/src/graphrefly/plan/antipatterns.jsonl` (+ a `feedback_*` memory if generalizable). +- **Protocol-behavior change** surfaced → route to `/spec-amend` (spec-first), not a direct code change. + +--- + +## Output discipline + +- Be concrete. Quote `file:line` refs. +- Don't write "this looks good" — say WHICH Q5–Q9 dimensions clear and why. +- Don't pad. If Q6 has no caveats, write `**Hidden invariants:** none surfaced.` and move on. +- Don't second-guess the user's stated intent — Q8 alternatives are options to compare; Q5–Q7 probe the recommended shape. +- Skim-readable: headers per question, bullets within. + +--- + +## Authority hierarchy + +1. `~/src/graphrefly/spec/rules.jsonl` — the protocol 宪法. +2. `~/src/graphrefly/decisions/decisions.jsonl` (+ DS-1 narrative) — locked decisions + F-* floor + durable values. +3. `~/src/graphrefly/plan/phases.jsonl` — the CSP-* sequencer (phase locks). +4. `~/src/graphrefly/guide/guide.jsonl` (G-test / G-composition) — testability + composition shape. +5. Existing patterns in `packages/ts/src/` — only when the above are silent. + +If a finding conflicts with a higher-authority doc, surface it explicitly — DO NOT silently override (no-autonomous-decisions). + +--- + +## What to do AFTER this skill completes + +- Lock approved → `/dev-dispatch` (or `--light`) with the locked design; a protocol-behavior change goes through `/spec-amend` first. +- Decisions deferred → leave them in `backlog.jsonl` and move on. +- Needs more thought → HALT, summarize, let the user think. + +This skill produces a report; it modifies no implementation files (it only appends to `~/src/graphrefly` jsonl after explicit user approval of a `D#`). diff --git a/.agents/skills/dev-dispatch/SKILL.md b/.agents/skills/dev-dispatch/SKILL.md new file mode 100644 index 00000000..9f183e9a --- /dev/null +++ b/.agents/skills/dev-dispatch/SKILL.md @@ -0,0 +1,125 @@ +--- +name: dev-dispatch +description: "Implement feature/fix with planning and self-test. Use when user says 'dispatch', 'dev-dispatch', or provides a task with implementation context. Supports --light flag for bug fixes and small changes. Run /qa afterward for code review and final checks." +argument-hint: "[--light] [task description or context]" +--- + +You are executing the **dev-dispatch** workflow for the **clean-slate GraphReFly** redesign. + +This repo is **`@graphrefly/ts`** — the self-contained TypeScript implementation (D32). Clean-slate code lands in **`packages/ts/src/`**. The language-neutral authority (spec / decisions / plan / conformance / formal) lives in **`~/src/graphrefly`** (branch `clean-slate`) as jsonl — when this skill and that repo disagree, **that repo wins** (AGENTS.md). Sibling impls: `@graphrefly/rust` (`~/src/graphrefly-rs`), `@graphrefly/py` (`~/src/graphrefly-py`) — each self-contained; cross-language = wire bridge, never in-process (D32). + +> **Stale-infra guard.** Do NOT reach for the retired port-model surfaces: `packages/pure-ts/**` (frozen read-only reference only, D41), `docs/implementation-plan.md`, `docs/optimizations.md`, `docs/roadmap.md`, `docs/test-guidance.md`, `docs/docs-guidance.md`, `GRAPHREFLY-SPEC.md`/`COMPOSITION-GUIDE.md` (migrated to `spec/rules.jsonl` + `guide/guide.jsonl`, B7), the `Impl`/facade/actor model / 3-digit D### port decisions. The clean-slate authority is the jsonl below. + +> **Ownership guard.** The current working directory is execution context, not architectural ownership. Before implementing anything that mixes Workspace, Canvas, or product-surface vocabulary, classify every new symbol as protocol, `@graphrefly/ts` library/solution, Canvas product, or docs-only. Canvas-owned lifecycle material such as `CanvasWorkspace*` DTOs/helpers, Canvas slot lifecycle lowering, lifecycle reason vocabularies, retention-pressure policy/status, selector adapters, component registries, and product lifecycle APIs belong in `~/src/graphrefly-canvas` unless an explicit D# says `@graphrefly/ts` owns that public API. Focused subpath exports and subpath smoke tests count as TS library API expansion; do not treat "root index unchanged" as sufficient. + +The user's task/context is: $ARGUMENTS + +### Mode detection +If `$ARGUMENTS` contains `--light`, this is **light mode**. Otherwise **full mode**. Differences are noted inline per phase. + +### Workflow floor (non-negotiable) +- **decision-first**: any architectural lock needs a `D#` in `~/src/graphrefly/decisions/decisions.jsonl` BEFORE code (`/design-review` → user approval → append). Decisions locked ≠ implementation approved — wait for an explicit "implement". +- **spec-first** (F-NO-IMPL-DEFINED): any wave-protocol behavior change amends `spec/rules.jsonl` + `formal/*.tla` + `spec/conformance.jsonl` FIRST (`/spec-amend`), THEN code. Operators/sugar/inspection are per-language (D6/D24) — NOT spec, skip spec-amend. +- **no autonomous decisions**: surface spec↔code conflicts; don't silently pick. File-by-file review for multi-file rewrites. +- **verify premise**: design tables lag code — grep the named symbols + check landed markers (`plan/phases.jsonl` status/notes) before designing new surface; a stale premise is a HALT. +- **consistency gate**: after touching any `~/src/graphrefly` jsonl, run `node ~/src/graphrefly/dashboard/build.mjs --check` (non-zero on broken links / orphans). + +--- + +## Phase 1: Context & Planning + +Load context and plan in a single pass. **Parallelize all reads.** + +Read in parallel (clean-slate authority): +- `~/src/graphrefly/AGENTS.md` — the single-source authority index (read FIRST). +- `~/src/graphrefly/spec/rules.jsonl` — the protocol 宪法 (R-* rules); deep-read the rules your change touches. +- `~/src/graphrefly/decisions/decisions.jsonl` — the unified D# log (or invoke `/decision-guard` to recall the governing D#/values/floor). +- `~/src/graphrefly/plan/phases.jsonl` — the CSP-* sequencer: find the phase this task belongs to, its `status` (done/impl/design), deps, and note. Read this FIRST among the plan files so you know whether you're on a ready phase or one still gated. +- `~/src/graphrefly/plan/backlog.jsonl` + `plan/antipatterns.jsonl` — deferred carries (B#) with triggers; anti-patterns to flag against. +- `~/src/graphrefly/spec/conformance.jsonl` — the behavioral scenarios (C-*) your change must keep green; check the `runtimes` status for the arm you target. +- `~/src/graphrefly/guide/guide.jsonl` — composition / test / docs / contribute guidance (G-composition / G-test / G-docs / G-contribute). +- `~/src/graphrefly/sessions/active/SESSION-clean-slate-redesign.md` (DS-1) — the L0–L6 design narrative + F-* constraints, when you need the why behind a lock. +- Any files the user referenced in $ARGUMENTS. +- The clean-slate source you'll modify: substrate = `packages/ts/src/{node,dispatcher,ctx,protocol,batch}/`; graph-layer = `packages/ts/src/graph/` (Graph + 8-verb sugar + operators + inspection describe/observe/profile). +- Existing tests: `packages/ts/src/__tests__/`. + +**Frozen reference (D41):** `packages/pure-ts/**` and `~/src/callbag-recharge` are READ-ONLY prior art for analogous operator behavior / edge cases / test structure during a re-derive (D40 Catalog-first). Map concepts to the clean-slate substrate (`node`/`ctx.down`/`ctx.depRecords`/`Graph`, D39 `describe`/`observe`) — do NOT 1:1 port; the old substrate API and semantics differ. They are NOT the behavior authority — `spec/rules.jsonl` is. + +While planning, validate proposed changes against the clean-slate floor (cite the rule/D#): +- **Sacred (L0.7):** topology declarative/serializable/inspectable · wave protocol is a public spec · wave protocol impl is **sync** · all fn go through the dispatcher. +- **8 verbs, closed (D4):** `node`/`graph`/`batch`/`state` + `producer`/`derived`/`effect`/`mount`. **Operators are `node` sugar (D6), not verbs** — per-language, never in parity (D24); real factory names show in `describe`. Adding a verb is a constitutional change. +- **Messages** `[[Type, Data?], ...]`; one array to `ctx.down`/`ctx.up` = one wave (R-msg-format). 10-type closed set, no user-defined types (R-msg-closed-set). +- **DIRTY before DATA/RESOLVED** in the same wave (R-dirty-before-data); two-phase glitch-free diamond (R-two-phase); a diamond/fan-in node recomputes exactly once after all changed deps settle (R-diamond). batch defers tier-≥3, not DIRTY. +- **`ctx.up` is control-tier only** (DIRTY/PAUSE/RESUME/INVALIDATE/TEARDOWN); DATA/RESOLVED/COMPLETE/ERROR are down-only (R-ctx-up, D8). A handle is pure data, no methods (D7). +- **No polling** (R-no-polling); **no imperative triggers** (R-no-imperative — reactive `ctx.up`/signals, not emitters/callbacks/timers+set; remove imperative paths when no caller depends); **no raw async** in the sync core (R-no-raw-async / F-SYNC-CORE — async lives only in sources / the pool / the wire bridge). +- **All fn through the dispatcher** (R-dispatch-all / F-DISPATCH-ALL — no inline-fn bypass). `dispatcher.invoke` is sync void (R-sync-core). +- **Data moves via messages** (R-data-not-peek — never peek a dep's `.cache` to seed compute; `.cache` is a read-only accessor for external consumers). SENTINEL = absence-of-DATA (R-sentinel); the canonical never-emitted detector is `ctx.prevData[i] === undefined`. +- **messageTier is a compile-time const table** (D18/D34/R-tier); the clock is **graph-local** (no global singleton, D26/R-clock); `onMessage`/`onSubscribe` are substrate-fixed, not user-replaceable (D19). +- **Primary-API clean** (R-primary-api-clean): protocol internals (DIRTY/RESOLVED/bitmask) never surface in value-level sugar (derived/effect/operator); ctx-level (node/producer) intentionally exposes tier as a power surface (DR-1). Sugar value-fn → ctx-fn wrapping happens in the graph layer (D27); a value-level `throw` becomes `[[ERROR,e]]` down (D30). +- **graph = single-thread causal/concurrency domain (D22 / R-graph-domain):** parallelism via pool callback or multi-graph + wire bridge; rewire is intra-graph only. +- **`ctx.state`** = per-node private cross-wave state (R-ctx-state); shared/observable state must be an explicit node + dep, not `ctx.state` (D23). A synchronous feedback cycle (a fn re-driving its own dep mid-wave) is a wave-level ERROR (D37/R-reentrancy), not iteration. +- **F-NO-WEDGE-CUT:** every primitive serves ≥2 segments (no LLM-only or single-segment wedge; F-NO-LLM-ONLY). **F-PERF:** budget every abstraction (thin node, default-off inspection). + +**Targeting a sibling (py/rust):** if the task targets `@graphrefly/py` (`~/src/graphrefly-py`) or `@graphrefly/rust` (`~/src/graphrefly-rs`), read that package's local layout + its conformance arm status in `spec/conformance.jsonl`. The cross-language contract is **behavioral conformance (D24)**, not symbol parity. PY public APIs are synchronous (return `Node[T]`/`Graph`/value, no `async def`); async lives at the source/pool boundary only (F-SYNC-CORE). + +Do NOT start implementing yet. + +--- + +## Phase 2: Architecture Discussion + +### Full mode — HALT + +**HALT and report before implementing.** Present: + +1. **Architecture assumptions** — how this fits the substrate (`node`/`dispatcher`/`ctx`/`protocol`/`batch`) vs graph-layer (`graph/`) split. +2. **New patterns** — any not yet in `packages/ts/src/`. +3. **Options considered** — alternatives with pros/cons. +4. **Recommendation** — preferred approach + why. + +Prioritize (in order): +1. **Correctness** — matches `~/src/graphrefly/spec/rules.jsonl` + the floor. +2. **Completeness** — edge cases (errors, COMPLETE, reconnect/reactivate, diamonds, SENTINEL gate, PAUSE lockset). +3. **Consistency** — matches patterns already in `packages/ts/src/`. +4. **Simplicity** — minimal solution. + +No backward compatibility (pre-1.0). + +**Escalation routing** (don't silently pick — no-autonomous-decisions): +- Architectural lock → `/design-review` → user approval → append a `D#` to `decisions.jsonl`. +- Wave-protocol behavior change → `/spec-amend` (spec-first: rules + TLA+ + conformance, THEN code). +- Cross-runtime concern → `/conformance` (behavioral scenario, not structural diff). +- Deferred/open question with no answer yet → append to `~/src/graphrefly/plan/backlog.jsonl` (B# + trigger); a recurring anti-pattern → `plan/antipatterns.jsonl` (+ a `feedback_*` memory if generalizable). + +**Wait for user approval before proceeding.** + +### Light mode — Skip unless escalation needed + +Proceed directly to Phase 3 **unless** Phase 1 reveals any of these: +- A change to **wave-protocol behavior** (tiers, wave semantics, diamond/equals/SENTINEL, batch, push-on-subscribe, ctx.up/down contract) → spec-first, escalate. +- A new architectural lock with no governing `D#`. +- Multiple viable approaches with non-obvious trade-offs. + +If any apply: HALT and present findings as in full mode. + +--- + +## Phase 3: Implementation & Self-Test + +After user approves (full mode) or after Phase 1 (light mode, no escalation): + +1. Implement the changes. + - Treat `~/src/graphrefly/spec/rules.jsonl` as non-negotiable for behavior; if code drifts from a rule, align to the rule — or surface the conflict, don't silently pick. + - Cite the governing R-id / D# in test expectations. +2. Create tests (per `guide/guide.jsonl` G-test — unit / property / conformance layering): + - Put tests in the most specific existing file under `packages/ts/src/__tests__/`. + - Use `graph.observe()` for live message assertions; assert at the node + message level otherwise. A behavioral-protocol change ALSO needs a `spec/conformance.jsonl` scenario (`/conformance`) before its rule flips `draft → active`. +3. Run checks: + - **TS:** `pnpm --filter @graphrefly/ts test` (vitest) + `pnpm run lint` (biome + layer/typecheck gates) + `pnpm run build` (tsup) as relevant. + - **PY (if targeted):** the `@graphrefly/py` package's own test/lint/type gates in `~/src/graphrefly-py`. + - **jsonl touched:** `node ~/src/graphrefly/dashboard/build.mjs --check` (consistency gate). +4. Fix any failures. + +If implementation leaves an **open architectural decision** (deferred behavior, parity caveat, "needs spec" item), append it to `~/src/graphrefly/plan/backlog.jsonl` (B# + trigger) — NOT a docs file. If it **lands or advances a CSP-* phase**, update that phase's `status`/`note` in `~/src/graphrefly/plan/phases.jsonl`, flip any conformance-backed `draft` rule to `active` once its scenario is green per arm, then run the consistency gate. + +When done, briefly list files changed and new exports added. Then suggest running `/qa` for adversarial review and final checks. diff --git a/.agents/skills/graph-animation/SKILL.md b/.agents/skills/graph-animation/SKILL.md new file mode 100644 index 00000000..c57191b2 --- /dev/null +++ b/.agents/skills/graph-animation/SKILL.md @@ -0,0 +1,418 @@ +--- +name: graph-animation +description: "Create GraphReFly concept explanation videos using HyperFrames. Use when asked to generate animated diagrams of reactive graph topologies — node activations, START/DIRTY/DATA/COMPLETE message flow, diamond resolution, batch mode, operators, etc. Supports two modes: default = concept-explainer (captioned, beat-structured, 30–90s); `--mode ambient-hero` = silent looping landing-page background (8-stage canonical storyboard, ~42s seamless loop). Invokes /hyperframes for composition authoring and /gsap for deterministic animation. Run /hyperframes-cli for preview/render commands." +argument-hint: "[--mode ambient-hero] [concept to animate, e.g. 'diamond resolution', 'batch mode', 'node lifecycle']" +--- + +You are executing the **graph-animation** workflow for **GraphReFly**. + +The user's request: `$ARGUMENTS` + +This skill produces a HyperFrames HTML composition that animates GraphReFly reactive graph concepts — topology diagrams with animated message flow, node activations, and tier-coded signals. It wraps `/hyperframes` and `/gsap` with GraphReFly-specific knowledge so you don't have to explain the domain each time. + +--- + +## Mode detection + +If `$ARGUMENTS` contains `--mode ambient-hero` (or the user asks for a "hero", "landing-page", "background video", or "loop"), use **ambient-hero mode** — skip directly to the "Mode B: ambient hero" section near the bottom. The 8-stage canonical storyboard is fully specified there; do not re-derive it. + +Otherwise, treat the request as **concept-explainer mode** (default) and follow Steps 1–4 below. + +--- + +## Step 1 — Understand the concept (concept-explainer mode only) + +If `$ARGUMENTS` is vague or empty, ask the user ONE question: which concept or workflow do you want to animate? Give them these options as examples: + +- Node lifecycle (START → DATA → COMPLETE → TEARDOWN) +- Message propagation through a linear chain (A → B → C) +- Diamond resolution (fan-out + fan-in, single recomputation) +- Batch mode (DIRTY cascades, DATA deferred until batch end) +- Reactive timer source feeding a derived node +- Operator pipeline (map → filter → combine) +- Human-in-the-loop gate (promptNode / valve pattern) +- Multi-agent subgraph ownership (L0–L3 staircase) + +If `$ARGUMENTS` names a concept already, proceed directly to Step 2. + +--- + +## Step 2 — GraphReFly visual language + +Use these conventions consistently across all GraphReFly animation videos. + +### Node shapes (canonical sugar names — `graph.state/producer/derived/effect`) + +| Sugar | Role | Shape | Color | +|---|---|---|---| +| `state` | Mutable cell, no deps | Circle | `#14B8A6` (teal) | +| `producer` | Push source (timer, event, async) | Diamond / pill | `#10B981` (emerald) | +| `derived` | Pure fn of deps | Circle | `#6C63FF` (violet) | +| `effect` | Sink / side-effect | Rounded rect | `#F59E0B` (amber) | +| External / human | Out-of-graph input | Hexagon | `#64748B` (slate) | + +Border stroke: `2px solid rgba(255,255,255,0.2)`. Inactive fill: 30% opacity of the node color. Active fill: 100% opacity. The underlying primitive `node()` exists below these four sugars; videos should use sugar names for vocabulary teaching. + +### Message symbols (canonical — `core/messages.ts`) + +The full protocol exports `START / DIRTY / DATA / RESOLVED / COMPLETE / TEARDOWN / INVALIDATE / PAUSE / RESUME / ERROR`. For videos, use the **foundational subset** unless a specific concept requires more: + +| Symbol | Color | Visual | When to show | +|---|---|---|---| +| `START` | `#94A3B8` (slate-light) | Thin pulse from src→dst, edge brightens | First message on a new connection (handshake) | +| `DIRTY` | `#FBBF24` (yellow) | Dashed pulse traveling along edge | Upstream may have changed; dep marks dirty | +| `DATA` | `#34D399` (green) | Solid dot traveling along edge; triggers node fn on arrival | Value flowing | +| `RESOLVED` | `#60A5FA` (blue) | Ring ripple on destination node | Diamond-resolution wave only (skip in beginner videos) | +| `COMPLETE` | `#94A3B8` (slate) | Edge fades, dst node dims | Producer finished emitting | +| `TEARDOWN` | `#A78BFA` (purple) | Edge erases backwards from dst→src | Subscription cancelled / dispose | +| `BATCH_*` | `#A78BFA` (purple) | Bracket sweep across topology | Batch-mode beats | + +Default foundational subset for hero / intro videos: **`START → DIRTY → DATA → COMPLETE`**. Add `RESOLVED` only when teaching diamond resolution; add `TEARDOWN` only when teaching lifecycle. + +### Edge conventions + +- Directed arrows from producer to consumer. +- Edge color matches the tier of the in-flight message. +- Resting state: `rgba(255,255,255,0.15)` gray. +- Animate as SVG `` or a CSS `clip-path` wipe so timing is seekable. + +### Layout + +- Canvas: **1920×1080** (landscape), dark background `#0F172A`. +- Nodes: minimum 80px diameter circles, 24px sans-serif label inside. +- Edges: 3px stroke with arrowhead marker. +- Label overlay: bottom-left, `font-size: 28px`, `color: #E2E8F0`, describes what is happening. + +### Timing budget + +- 2–4 seconds per "beat" (one message hop or one lifecycle phase). +- Total target: **30–90 seconds** for a concept clip. +- Add 1.5s of static "intro frame" showing the graph topology before any animation begins. + +--- + +## Step 3 — Scaffold the composition + +Use `npx hyperframes init ` to scaffold a new project directory, OR create the file inline if the user prefers a single-file drop. + +A minimal starting template (adapt to the specific concept): + +```html + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+
+ + +
+
+ + + + +``` + +--- + +## Step 4 — Mode A: concept-explainer patterns + +### Diamond resolution + +``` + A + / \ + B C + \ / + D (recomputes once) +``` + +Sequence: +1. A emits DATA → DIRTY cascades to B and C simultaneously (two yellow dashes) +2. B and C each emit DIRTY to D +3. D receives both DIRTYs — stays dirty, does NOT recompute yet +4. A emits RESOLVED → B resolves → C resolves → D resolves +5. D recomputes **once** (green DATA dot appears from D) +6. Caption: "Diamond resolved — one recomputation, zero double-fires" + +### Batch mode + +Sequence: +1. `batch(() => ...)` bracket appears — purple "BATCH_START" ring +2. Multiple sources emit DATA — nodes flash DIRTY but DATA dots are held (shown as queued dots stacked at source) +3. batch end — all deferred DATA releases simultaneously +4. Caption: "Batch defers DATA, not DIRTY — downstream sees one coherent update" + +### Node lifecycle + +Sequence: +1. Node dims (initial) +2. Subscription arrives → node brightens +3. Upstream emits DATA → node pulses green +4. RESOLVED ripple out +5. Unsubscribe → teardown ring (purple) → node dims again + +### Operator pipeline + +Show 3–4 nodes in a horizontal chain (map → filter → combine). Animate a value token flowing left to right, label each node with its transform (`×2`, `> 5`, `merge`). + +--- + +## Step 5 — Run the dev loop + +```bash +# from the composition project directory: +npx hyperframes lint # validate timing/structure +npx hyperframes preview # live browser preview with hot reload +npx hyperframes render # output MP4 +``` + +Use `/hyperframes-cli` skill for detailed CLI guidance. +Use `/gsap` skill for advanced GSAP timeline patterns. +Use `/hyperframes-media` skill if you need TTS narration or audio. + +--- + +## Step 6 — Quality checks before render + +- [ ] All nodes have correct shape + color per the visual language table above +- [ ] Message colors match the canonical table (START=slate-light, DIRTY=yellow, DATA=green, COMPLETE=slate, RESOLVED=blue if shown, TEARDOWN=purple if shown) +- [ ] Timeline registers `window.__hfGsapTimeline = tl` (required for seekable render) +- [ ] Caption text is ≤ 80 chars per line and readable at 1080p (concept-explainer mode only) +- [ ] `npx hyperframes lint` passes with no errors +- [ ] Preview scrubbing doesn't stutter (all animations are on the GSAP timeline, not setTimeout) + +--- + +## Mode B: ambient hero (canonical 8-stage landing-page loop) + +This mode produces the **GraphReFly landing-page background video** — a silent, looping, atmospheric reactive-graph composition. Use it whenever the user asks for a "hero", "landing-page", "background video", or passes `--mode ambient-hero`. + +### Mode-B rules (differ from concept-explainer) + +| Rule | Value | Why | +|---|---|---| +| Length | ~42s seamless loop | Hero convention 15–60s; matches the 8-stage plan | +| Audio | none | Plays muted; hero conventions | +| Captions | none over the topology | Headline text overlays the hero on the page; captions would compete | +| Small floating labels | OK, ≤ 4 words, ≤ 22px font | E.g. `graph.describe()`, `graph.observe()`, `batch()` — vocabulary pre-teach | +| Pacing | slow / ambient | No frantic motion; eye should rest | +| Loop seam | **Stage 8 ends at exactly the same camera/node positions as Stage 1 starts** | Imperceptible loop | +| Contrast | reduced — palette at ~70% saturation, bg `#0F172A` | Headline text on page must dominate | +| Render output | MP4 + a WebM/VP9 variant for `