diff --git a/docs/23_self_referential_tempo_edge.md b/docs/23_self_referential_tempo_edge.md new file mode 100644 index 0000000..037d4f9 --- /dev/null +++ b/docs/23_self_referential_tempo_edge.md @@ -0,0 +1,92 @@ +# Self-Referential Tempo Edge (ADR-23) + +**Status:** proposed +**Date:** 2026-06-06 +**Concepts:** SC-CONCEPT-0003 (hyperstition / observer coupling), SC-CONCEPT-0010 (closed-loop causal leverage) +**Companion docs:** [24 — Tempo Edge Redaction Policy](24_tempo_edge_redaction_policy.md) +**Companion schema:** [`schemas/tempo_point_v0.schema.json`](../schemas/tempo_point_v0.schema.json) + +## Context + +The Sandy Chaos research engine has, until now, run almost entirely on synthetic toy models (hyperstition Arm A grids, leverage card simulations). The framework's central claims — observer coupling, closed-loop causal leverage, bidirectional corridors — assume an embedded operator emitting decisions under bounded resources. The toy models substitute mathematical operators for that embedded entity. + +The next defensible step is to begin sampling a real embedded operator: the human running the system. Gameplay + input streams (SC2 first, input-only fallback for other titles) are a clean source because: + +- the operator is the same person who built the framework, so the loop is genuinely self-referential rather than third-party-observed; +- the decision rate is high enough to produce statistically interesting tempo points within a single session; +- game-state coupling (where available) lets us pair input with outcomes inside a bounded environment; +- the surface is already part of the operator's life (Hxvn content workflow), so the data-collection burden is incremental. + +## Decision + +Build a **telemetry edge** that ingests gameplay + input streams and emits structured **tempo points** to the research engine. + +- **Edge:** JavaScript/TypeScript (Node), running in-process on the operator's machine. Lives in `telemetry-edge/` inside the sandy-chaos repo (single repo, clear seam, extractable later if it grows up). +- **Core:** existing Python research engine (`nfem_suite/`, `scripts/`, leverage harness). +- **Wire format:** `tempo_point_v0.schema.json`. Edge emits JSON Lines; core consumes them. + +The edge is responsible for everything that touches raw input: capture, ring-buffer, aggregation, redaction, emission. The core is responsible for analysis: scoring, corridor detection, leverage attribution. + +This separation is normative: **no raw keystrokes, raw chat text, or untrimmed input streams ever cross the seam**. The redaction policy ([doc 24](24_tempo_edge_redaction_policy.md)) is part of the seam contract, not an afterthought. + +## Architecture + +``` ++------------------------+ +-----------------------+ +------------------------+ +| capture layer | | edge aggregator | | research core (Python) | +| - game-state listener | -> | - in-memory ring buf | -> | - tempo point ingest | +| - input listener | | - windowed aggregates | | - SC corridor scoring | +| - clock | | - redaction filter | | - leverage attribution | ++------------------------+ +-----------------------+ +------------------------+ + | | | + raw events tempo_point_v0 JSONL memory/research/ + (never persisted) (only persisted form) tempo// +``` + +### Coupling/decoupling framing (operator-supplied, plausible tier) + +Operator vision: tempo points should be readable as **coupling and decoupling events** between the operator's intent surface and the game's state surface. Tight coupling = APM windows where input rate, game-state change rate, and decision-flag density rise together. Decoupling = windows where one of those terms moves without the others. This framing is treated as a *plausible* analytic lens, not a load-bearing assumption: the schema records the raw aggregates, and coupling/decoupling is computed downstream so the lens can be revised without invalidating collected data. + +## Wire format (summary) + +Full schema: [`schemas/tempo_point_v0.schema.json`](../schemas/tempo_point_v0.schema.json). + +A tempo point is a window summary, not an event. Required fields: + +- `ts` — ISO-8601 timestamp of window end +- `session_id` — opaque per-session identifier (no operator PII) +- `source` — taxonomy: `game:sc2`, `game:arc-raiders`, `game:fortnite`, `input:keyboard`, `input:mouse`, `mixed` +- `window_ms` — window length the aggregates cover +- `point_kind` — one of: `apm_window`, `interval_cluster`, `decision_flag`, `game_state_delta` +- `aggregates` — typed bag of numeric/categorical aggregates appropriate to the `point_kind` +- `redaction_version` — version of the redaction policy active at emit time + +Forbidden fields (schema enforces `additionalProperties: false` and explicit `not` constraints): + +- any `raw_*` field, any `*_keys` field containing key sequences, any `text` field with free-form characters + +## Claim tiers + +- **Defensible now:** the edge can capture operator tempo at session granularity with redaction-by-construction; the wire format separates capture from analysis; the seam is auditable. +- **Plausible but unproven:** tempo points sampled from real operator sessions will show distributional structure analogous to the corridors observed in the hyperstition toy model. (Falsifiable: if a session-corpus pressure event scores no corridor coverage above chance, the bridge is weakened.) +- **Speculative:** the coupling/decoupling framing generalizes beyond gameplay to any operator-environment pair. (Out of scope for this ADR; recorded so claim drift can be policed.) + +## Failure conditions + +- Raw keystrokes, raw chat text, or any verbatim input characters land on persistent storage. → safety fail, edge must be halted and audited. +- The wire schema is edited *after* a measurement window opens, in a direction that changes what counts as a valid tempo point. → seal violation by analogy with the leverage card protocol (doc 22). +- "Coupling" becomes synonymous with "any correlation between input and game state." → claim drift; concept needs a sharper operational definition before further commits. +- A tempo point includes fields not declared in `tempo_point_v0`. → contract violation; edge must be patched or schema versioned forward. + +## What this ADR does NOT decide + +- Specific game-state parser implementations (SC2 replay parser library, etc.) — chosen during Medium scope. +- Whether the edge eventually publishes anything to npm. Default: **no**, the edge is a local tool. Any publication is a separate calm-confirmation decision. +- Long-term storage layout beyond the `memory/research/tempo//` convention. +- Cross-operator data (only the single embedded operator is in scope). + +## Next steps + +1. **Small (this commit):** ADR + redaction policy + schema, no executable code. +2. **Medium (next, gated on operator review of this ADR):** one Node module that reads a sample SC2 replay + a sampled input stream and emits valid `tempo_point_v0` JSONL. +3. **Large (later, separately gated):** live session capture, multi-session corpus, first prospective leverage card on a tempo-edge workflow. diff --git a/docs/24_tempo_edge_redaction_policy.md b/docs/24_tempo_edge_redaction_policy.md new file mode 100644 index 0000000..4159a7e --- /dev/null +++ b/docs/24_tempo_edge_redaction_policy.md @@ -0,0 +1,61 @@ +# Tempo Edge Redaction Policy (v0) + +**Status:** normative, version `v0` +**Date:** 2026-06-06 +**Applies to:** any code in `telemetry-edge/` or any other surface that ingests operator input or game state for the Sandy Chaos research engine. +**Companion ADR:** [23 — Self-Referential Tempo Edge](23_self_referential_tempo_edge.md) +**Companion schema:** [`schemas/tempo_point_v0.schema.json`](../schemas/tempo_point_v0.schema.json) + +This document is short on purpose. Every line is a constraint. + +## Hard rules (MUST) + +1. **No raw keystrokes on disk.** The edge MUST NOT write raw key event sequences, raw character buffers, or raw chat strings to any persistent storage (filesystem, database, log file, OS pasteboard). +2. **Ring buffer only.** Raw input events MAY exist in an in-process memory ring buffer for the minimum window needed to compute aggregates. The ring buffer MUST be bounded by both size and time, and MUST be cleared on process exit. +3. **Aggregate-only emission.** The only artifacts that cross the edge/core seam are `tempo_point_v0` records validated against [`schemas/tempo_point_v0.schema.json`](../schemas/tempo_point_v0.schema.json). The schema enforces `additionalProperties: false`. +4. **No identifiers.** Tempo points MUST NOT include operator name, machine name, IP, account handle, game username, or any other personally identifying string. The `session_id` is an opaque per-session token (e.g. random UUIDv4) with no derivable link to operator identity. +5. **Game-state coupling stays structural.** Game-state aggregates MAY include numeric or categorical structural facts (unit counts, resource deltas, score, phase) but MUST NOT include free-form text such as chat messages, opponent handles, or in-game communications. +6. **Auditable build.** Any build of the edge that is run against real operator data MUST be reproducible from a tagged commit. Untagged or uncommitted edge builds MUST NOT be pointed at real input streams. + +## Permitted aggregates (MAY) + +Any of the following, scoped to a single window: + +- counts (events, keys, mouse clicks, decisions) +- rates (APM, click-rate, decision-rate) +- intervals (mean / p50 / p95 / max inter-event interval) +- modal categorical summaries (most-used key class such as `movement` / `hotkey` / `modifier`, never the literal key) +- numeric game-state aggregates (unit count, supply, score, resource deltas) +- derived flags (`decision_flag` indicating a window contained a structurally significant decision, without describing the decision content) + +## Forbidden fields (MUST NOT) + +The schema rejects records containing any of: + +- `raw_*` of any kind +- `key_sequence`, `key_events`, `keystroke_log` +- `text`, `chat`, `message_log`, `transcript` +- `username`, `operator_name`, `account_id`, `handle`, `ip_address`, `hostname` + +If an emitter needs a field not on the permitted list, the schema MUST be revised forward (`tempo_point_v1`) with explicit review of the new field's redaction implications. The current version MUST NOT be silently extended. + +## Audit checklist (before pointing the edge at real input) + +- [ ] All persistent writes in the edge codepath go to validated `tempo_point_v0` records, confirmed by grep + test. +- [ ] No log statement or debug print emits raw input characters. +- [ ] Ring buffer has both size and time caps, and a unit test verifies it is cleared on shutdown. +- [ ] Edge build is from a tagged commit. +- [ ] A sample run against synthetic input produces only schema-valid output. + +## Failure response + +If a violation of any MUST rule is discovered: + +1. Halt the edge process immediately. +2. Quarantine any output files produced by the offending build. +3. File a Sandy Chaos pressure event (`kind: workflow-review`, `disposition: KILL` or `REVISE` as appropriate) referencing the violation. +4. Do not resume operator-data capture until the violation has a closed pressure event and a regression test. + +## Version + +This is `redaction-policy-v0`. Tempo points record the active policy version in their `redaction_version` field so corpus consumers can filter by policy generation. diff --git a/memory/research/topological-memory-v0/baseline_report_v0.json b/memory/research/topological-memory-v0/baseline_report_v0.json index b343ebc..bdb2d19 100644 --- a/memory/research/topological-memory-v0/baseline_report_v0.json +++ b/memory/research/topological-memory-v0/baseline_report_v0.json @@ -9,8 +9,8 @@ "ranked_results": {} }, "keyword": { - "hit_rate": 0.9666666666666667, - "mrr": 0.788888888888889, + "hit_rate": 1.0, + "mrr": 0.8444444444444444, "queries": [ { "query_id": "Q-001", @@ -97,7 +97,8 @@ ], "top_nodes": [ "N_CONCEPT_SC0004", - "N_FRONTIER_20260427" + "N_FRONTIER_20260427", + "N_PLAN_TODO" ], "hit": true, "reciprocal_rank": 0.5 @@ -149,7 +150,7 @@ "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", "N_RUNTIME_ADOPTION", - "N_CONCEPT_SC0004" + "N_PLAN_TODO" ], "hit": true, "reciprocal_rank": 1.0 @@ -201,7 +202,7 @@ "top_nodes": [ "N_PROMPT_SC0001", "N_EVAL_MODULE", - "N_RUNTIME_MODULE" + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": true, "reciprocal_rank": 0.5 @@ -264,12 +265,12 @@ "N_SESSION_CHECKPOINT" ], "top_nodes": [ + "N_SESSION_CHECKPOINT", "N_ORCHESTRATOR_CONTRACT", - "N_YGG_ARCH", - "N_SESSION_CHECKPOINT" + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": true, - "reciprocal_rank": 0.3333333333333333 + "reciprocal_rank": 1.0 }, { "query_id": "Q-021", @@ -329,12 +330,12 @@ "N_PROMPT_SC0001" ], "top_nodes": [ + "N_PROMPT_SC0001", "N_CONCEPT_SC0004", - "N_RUNTIME_ADOPTION", - "N_RUNTIME_MODULE" + "N_RUNTIME_ADOPTION" ], - "hit": false, - "reciprocal_rank": 0.0 + "hit": true, + "reciprocal_rank": 1.0 }, { "query_id": "Q-026", @@ -466,7 +467,7 @@ }, { "node_id": "N_PROMPT_SC0001", - "score": 0.44166666666666665, + "score": 0.4338235294117647, "reason": "keyword overlap=['adoption', 'for', 'retrieval', 'runtime']", "path_nodes": [], "path_edges": [], @@ -484,7 +485,7 @@ "Q-004": [ { "node_id": "N_PROMPT_SC0001", - "score": 0.39999999999999997, + "score": 0.39215686274509803, "reason": "keyword overlap=['implementation', 'packet', 'pressure', 'prompt']", "path_nodes": [], "path_edges": [], @@ -492,8 +493,8 @@ }, { "node_id": "N_SESSION_CHECKPOINT", - "score": 0.09999999999999999, - "reason": "keyword overlap=['to']", + "score": 0.19607843137254902, + "reason": "keyword overlap=['lane', 'to']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -575,6 +576,14 @@ "path_nodes": [], "path_edges": [], "path_summary": "" + }, + { + "node_id": "N_PLAN_TODO", + "score": 0.11964285714285713, + "reason": "keyword overlap=['active']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" } ], "Q-008": [ @@ -622,7 +631,7 @@ }, { "node_id": "N_PROMPT_SC0001", - "score": 0.2833333333333333, + "score": 0.27941176470588236, "reason": "keyword overlap=['retrieval', 'runtime']", "path_nodes": [], "path_edges": [], @@ -673,9 +682,9 @@ "path_summary": "" }, { - "node_id": "N_CONCEPT_SC0004", - "score": 0.11647727272727273, - "reason": "keyword overlap=['continuity']", + "node_id": "N_PLAN_TODO", + "score": 0.2125, + "reason": "keyword overlap=['continuity', 'surface']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -692,7 +701,7 @@ }, { "node_id": "N_SESSION_CHECKPOINT", - "score": 0.18333333333333335, + "score": 0.17941176470588238, "reason": "keyword overlap=['resume', 'session']", "path_nodes": [], "path_edges": [], @@ -718,7 +727,7 @@ }, { "node_id": "N_PROMPT_SC0001", - "score": 0.2545454545454545, + "score": 0.24866310160427807, "reason": "keyword overlap=['adoption', 'prompt', 'runtime']", "path_nodes": [], "path_edges": [], @@ -736,7 +745,7 @@ "Q-014": [ { "node_id": "N_PROMPT_SC0001", - "score": 0.3666666666666667, + "score": 0.35882352941176476, "reason": "keyword overlap=['packet', 'prompt', 'retrieval', 'runtime']", "path_nodes": [], "path_edges": [], @@ -762,7 +771,7 @@ "Q-015": [ { "node_id": "N_PROMPT_SC0001", - "score": 0.19999999999999998, + "score": 0.19607843137254902, "reason": "keyword overlap=['packet', 'prompt']", "path_nodes": [], "path_edges": [], @@ -777,9 +786,9 @@ "path_summary": "" }, { - "node_id": "N_RUNTIME_MODULE", - "score": 0.10119047619047619, - "reason": "keyword overlap=['code']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.19047619047619047, + "reason": "keyword overlap=['code', 'the']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -891,25 +900,25 @@ ], "Q-020": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.2138157894736842, - "reason": "keyword overlap=['contract', 'orchestrator']", + "node_id": "N_SESSION_CHECKPOINT", + "score": 0.32536764705882354, + "reason": "keyword overlap=['checkpoint', 'lane', 'resumes']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_YGG_ARCH", - "score": 0.11160714285714285, - "reason": "keyword overlap=['contract']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.2138157894736842, + "reason": "keyword overlap=['contract', 'orchestrator']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_SESSION_CHECKPOINT", - "score": 0.11041666666666666, - "reason": "keyword overlap=['checkpoint']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.2113095238095238, + "reason": "keyword overlap=['orchestrator', 'the']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1012,7 +1021,7 @@ }, { "node_id": "N_PROMPT_SC0001", - "score": 0.19999999999999998, + "score": 0.19607843137254902, "reason": "keyword overlap=['adoption', 'runtime']", "path_nodes": [], "path_edges": [], @@ -1021,25 +1030,25 @@ ], "Q-025": [ { - "node_id": "N_CONCEPT_SC0004", - "score": 0.11647727272727273, - "reason": "keyword overlap=['node']", + "node_id": "N_PROMPT_SC0001", + "score": 0.32536764705882354, + "reason": "keyword overlap=['adoption', 'bridges', 'docs']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_RUNTIME_ADOPTION", - "score": 0.11160714285714285, - "reason": "keyword overlap=['adoption']", + "node_id": "N_CONCEPT_SC0004", + "score": 0.11647727272727273, + "reason": "keyword overlap=['node']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_RUNTIME_MODULE", + "node_id": "N_RUNTIME_ADOPTION", "score": 0.11160714285714285, - "reason": "keyword overlap=['code']", + "reason": "keyword overlap=['adoption']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1126,7 +1135,7 @@ "Q-029": [ { "node_id": "N_SESSION_CHECKPOINT", - "score": 0.5, + "score": 0.4901960784313726, "reason": "keyword overlap=['checkpoint', 'continuity', 'maps', 'resume', 'symbolic']", "path_nodes": [], "path_edges": [], @@ -1178,8 +1187,8 @@ } }, "recency": { - "hit_rate": 0.6666666666666666, - "mrr": 0.3, + "hit_rate": 0.1, + "mrr": 0.1, "queries": [ { "query_id": "Q-001", @@ -1188,8 +1197,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROMPT_SC0001" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": false, "reciprocal_rank": 0.0 @@ -1201,8 +1210,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_TODO_TOPMEM" + "N_SCRIPT_ORCH_AUTOSPAWN", + "N_SCRIPT_AUTOMATION_ORCH" ], "hit": false, "reciprocal_rank": 0.0 @@ -1213,12 +1222,12 @@ "N_RUNTIME_ADOPTION" ], "top_nodes": [ + "N_SCRIPT_AUTOMATION_ORCH", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_RUNTIME_ADOPTION" + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-004", @@ -1227,11 +1236,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROMPT_SC0001" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-005", @@ -1239,12 +1248,12 @@ "N_PROVISIONAL_VALIDATION" ], "top_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROVISIONAL_VALIDATION" + "N_SCRIPT_AUTOMATION_ORCH" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-006", @@ -1252,12 +1261,12 @@ "N_TODO_TOPMEM" ], "top_nodes": [ + "N_SCRIPT_AUTOMATION_ORCH", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_TODO_TOPMEM" + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-007", @@ -1266,11 +1275,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_CONCEPT_SC0004" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.5 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-008", @@ -1279,11 +1288,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_YGG_ARCH" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-009", @@ -1292,11 +1301,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_RUNTIME_MODULE" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-010", @@ -1305,11 +1314,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_EVAL_MODULE" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-011", @@ -1318,8 +1327,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_RUNTIME_ADOPTION" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": true, "reciprocal_rank": 1.0 @@ -1331,8 +1340,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROVISIONAL_VALIDATION" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": false, "reciprocal_rank": 0.0 @@ -1343,12 +1352,12 @@ "N_PROMPT_SC0001" ], "top_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROMPT_SC0001" + "N_SCRIPT_AUTOMATION_ORCH" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-014", @@ -1356,9 +1365,9 @@ "N_RUNTIME_MODULE" ], "top_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROMPT_SC0001" + "N_SCRIPT_AUTOMATION_ORCH" ], "hit": false, "reciprocal_rank": 0.0 @@ -1369,12 +1378,12 @@ "N_EVAL_MODULE" ], "top_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_EVAL_MODULE" + "N_SCRIPT_AUTOMATION_ORCH" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-016", @@ -1382,12 +1391,12 @@ "N_PROVISIONAL_VALIDATION" ], "top_nodes": [ - "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROVISIONAL_VALIDATION" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN", + "N_ORCHESTRATOR_CONTRACT" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-017", @@ -1395,9 +1404,9 @@ "N_CONCEPT_SC0004" ], "top_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROVISIONAL_VALIDATION" + "N_SCRIPT_AUTOMATION_ORCH" ], "hit": false, "reciprocal_rank": 0.0 @@ -1408,9 +1417,9 @@ "N_CONCEPT_SC0004" ], "top_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROVISIONAL_VALIDATION" + "N_SCRIPT_AUTOMATION_ORCH" ], "hit": false, "reciprocal_rank": 0.0 @@ -1422,8 +1431,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_RUNTIME_ADOPTION" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": true, "reciprocal_rank": 1.0 @@ -1435,8 +1444,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_TODO_TOPMEM" + "N_SCRIPT_ORCH_AUTOSPAWN", + "N_SCRIPT_AUTOMATION_ORCH" ], "hit": false, "reciprocal_rank": 0.0 @@ -1448,11 +1457,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROVISIONAL_VALIDATION" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-022", @@ -1460,12 +1469,12 @@ "N_TODO_TOPMEM" ], "top_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_TODO_TOPMEM" + "N_SCRIPT_AUTOMATION_ORCH" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-023", @@ -1474,8 +1483,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROMPT_SC0001" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": false, "reciprocal_rank": 0.0 @@ -1486,9 +1495,9 @@ "N_RUNTIME_ADOPTION" ], "top_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROMPT_SC0001" + "N_SCRIPT_AUTOMATION_ORCH" ], "hit": false, "reciprocal_rank": 0.0 @@ -1500,8 +1509,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_EVAL_MODULE" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": false, "reciprocal_rank": 0.0 @@ -1513,11 +1522,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_EVAL_MODULE" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-027", @@ -1526,11 +1535,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_RUNTIME_MODULE" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-028", @@ -1539,8 +1548,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_RUNTIME_MODULE" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": true, "reciprocal_rank": 1.0 @@ -1552,11 +1561,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_SESSION_CHECKPOINT" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.3333333333333333 + "hit": false, + "reciprocal_rank": 0.0 }, { "query_id": "Q-030", @@ -1565,11 +1574,11 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427", - "N_PROVISIONAL_VALIDATION" + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], - "hit": true, - "reciprocal_rank": 0.5 + "hit": false, + "reciprocal_rank": 0.0 } ], "ranked_results": { @@ -1583,17 +1592,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7641025641025642, - "reason": "recency=0.872, lexical_overlap=['continuity', 'retrieval', 'the']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['for']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.15897435897435896, - "reason": "recency=0.115, lexical_overlap=['continuity', 'for', 'retrieval']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1609,17 +1618,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7724358974358976, - "reason": "recency=0.872, lexical_overlap=['concept', 'frontier', 'the']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_TODO_TOPMEM", - "score": 0.1405982905982906, - "reason": "recency=0.113, lexical_overlap=['memory', 'topological']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1627,25 +1636,25 @@ ], "Q-003": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8, - "reason": "recency=1.000", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['for']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7224358974358975, - "reason": "recency=0.872, lexical_overlap=['retrieval']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_RUNTIME_ADOPTION", - "score": 0.23205128205128206, - "reason": "recency=0.103, lexical_overlap=['adoption', 'for', 'note', 'retrieval', 'runtime', 'traces']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1661,17 +1670,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.6974358974358975, - "reason": "recency=0.872", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.18119658119658122, - "reason": "recency=0.115, lexical_overlap=['implementation', 'packet', 'pressure', 'prompt']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1679,25 +1688,25 @@ ], "Q-005": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8, - "reason": "recency=1.000", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8285714285714286, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7260073260073261, - "reason": "recency=0.872, lexical_overlap=['the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.18827838827838828, - "reason": "recency=0.128, lexical_overlap=['bounded', 'gate', 'promotion']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1705,25 +1714,25 @@ ], "Q-006": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8222222222222223, - "reason": "recency=1.000, lexical_overlap=['and']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8444444444444444, + "reason": "recency=1.000, lexical_overlap=['and', 'todo']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7418803418803419, - "reason": "recency=0.872, lexical_overlap=['and', 'the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['and']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_TODO_TOPMEM", - "score": 0.22393162393162394, - "reason": "recency=0.113, lexical_overlap=['and', 'benchmark', 'checklist', 'comparison', 'graph', 'todo']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1739,17 +1748,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7545787545787547, - "reason": "recency=0.872, lexical_overlap=['active', 'frontier']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_CONCEPT_SC0004", - "score": 0.11868131868131868, - "reason": "recency=0.077, lexical_overlap=['frontier', 'sc-concept-0004']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1765,17 +1774,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7224358974358975, - "reason": "recency=0.872, lexical_overlap=['and']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['and']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_YGG_ARCH", - "score": 0.16602564102564102, - "reason": "recency=0.051, lexical_overlap=['and', 'architecture', 'branch', 'cadence', 'doc']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1791,17 +1800,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7307692307692308, - "reason": "recency=0.872, lexical_overlap=['retrieval']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8333333333333334, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_RUNTIME_MODULE", - "score": 0.20256410256410257, - "reason": "recency=0.128, lexical_overlap=['code', 'retrieval', 'runtime']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8333333333333334, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1817,17 +1826,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7224358974358975, - "reason": "recency=0.872, lexical_overlap=['and']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8500000000000001, + "reason": "recency=1.000, lexical_overlap=['and', 'code']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_EVAL_MODULE", - "score": 0.25256410256410255, - "reason": "recency=0.128, lexical_overlap=['and', 'code', 'evaluator', 'keyword', 'recency', 'topology']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1843,17 +1852,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7224358974358975, - "reason": "recency=0.872, lexical_overlap=['continuity']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['orchestrator']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_RUNTIME_ADOPTION", - "score": 0.13205128205128205, - "reason": "recency=0.103, lexical_overlap=['continuity', 'surface']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['orchestrator']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1869,17 +1878,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.6974358974358975, - "reason": "recency=0.872", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8200000000000001, + "reason": "recency=1.000, lexical_overlap=['for']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.12256410256410256, - "reason": "recency=0.128, lexical_overlap=['for']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8200000000000001, + "reason": "recency=1.000, lexical_overlap=['agent']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1887,25 +1896,25 @@ ], "Q-013": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8, - "reason": "recency=1.000", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8181818181818182, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7156177156177157, - "reason": "recency=0.872, lexical_overlap=['the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.14685314685314685, - "reason": "recency=0.115, lexical_overlap=['adoption', 'prompt', 'runtime']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1913,25 +1922,25 @@ ], "Q-014": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8200000000000001, - "reason": "recency=1.000, lexical_overlap=['code']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8400000000000001, + "reason": "recency=1.000, lexical_overlap=['code', 'the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7374358974358975, - "reason": "recency=0.872, lexical_overlap=['retrieval', 'the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8200000000000001, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.17230769230769233, - "reason": "recency=0.115, lexical_overlap=['packet', 'prompt', 'retrieval', 'runtime']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8200000000000001, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1939,25 +1948,25 @@ ], "Q-015": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8222222222222223, - "reason": "recency=1.000, lexical_overlap=['code']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8444444444444444, + "reason": "recency=1.000, lexical_overlap=['code', 'the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7196581196581198, - "reason": "recency=0.872, lexical_overlap=['the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_EVAL_MODULE", - "score": 0.147008547008547, - "reason": "recency=0.128, lexical_overlap=['baselines', 'code']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1965,25 +1974,25 @@ ], "Q-016": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8, - "reason": "recency=1.000", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['todo']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7196581196581198, - "reason": "recency=0.872, lexical_overlap=['the']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.147008547008547, - "reason": "recency=0.128, lexical_overlap=['provisional', 'validation']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -1991,25 +2000,25 @@ ], "Q-017": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8, - "reason": "recency=1.000", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7418803418803419, - "reason": "recency=0.872, lexical_overlap=['concept', 'the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.19145299145299144, - "reason": "recency=0.128, lexical_overlap=['note', 'promotion', 'provisional', 'validation']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2017,25 +2026,25 @@ ], "Q-018": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8, - "reason": "recency=1.000", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7641025641025642, - "reason": "recency=0.872, lexical_overlap=['concept', 'frontier', 'the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.12478632478632479, - "reason": "recency=0.128, lexical_overlap=['note']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2051,17 +2060,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7196581196581198, - "reason": "recency=0.872, lexical_overlap=['retrieval']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_RUNTIME_ADOPTION", - "score": 0.14871794871794872, - "reason": "recency=0.103, lexical_overlap=['retrieval', 'runtime', 'surface']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2077,17 +2086,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7224358974358975, - "reason": "recency=0.872, lexical_overlap=['the']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8500000000000001, + "reason": "recency=1.000, lexical_overlap=['orchestrator', 'the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_TODO_TOPMEM", - "score": 0.1155982905982906, - "reason": "recency=0.113, lexical_overlap=['lane']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['orchestrator']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2103,17 +2112,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.6974358974358975, - "reason": "recency=0.872", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['for']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.16923076923076924, - "reason": "recency=0.128, lexical_overlap=['conditions', 'demotion', 'for']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2121,25 +2130,25 @@ ], "Q-022": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8, - "reason": "recency=1.000", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7196581196581198, - "reason": "recency=0.872, lexical_overlap=['the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_TODO_TOPMEM", - "score": 0.17948717948717952, - "reason": "recency=0.113, lexical_overlap=['benchmark', 'checklist', 'comparison', 'graph']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2155,17 +2164,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7374358974358975, - "reason": "recency=0.872, lexical_overlap=['continuity', 'retrieval']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8200000000000001, + "reason": "recency=1.000, lexical_overlap=['for']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.1523076923076923, - "reason": "recency=0.115, lexical_overlap=['continuity', 'for', 'retrieval']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2173,25 +2182,25 @@ ], "Q-024": [ { - "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.8, - "reason": "recency=1.000", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8222222222222223, + "reason": "recency=1.000, lexical_overlap=['the']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7196581196581198, - "reason": "recency=0.872, lexical_overlap=['the']", + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.13675213675213677, - "reason": "recency=0.115, lexical_overlap=['adoption', 'runtime']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2207,17 +2216,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.6974358974358975, - "reason": "recency=0.872", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_EVAL_MODULE", - "score": 0.12756410256410255, - "reason": "recency=0.128, lexical_overlap=['code']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2233,17 +2242,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.6974358974358975, - "reason": "recency=0.872", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8285714285714286, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_EVAL_MODULE", - "score": 0.21684981684981686, - "reason": "recency=0.128, lexical_overlap=['code', 'evidence', 'inspectable', 'path']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8285714285714286, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2259,17 +2268,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7224358974358975, - "reason": "recency=0.872, lexical_overlap=['retrieval']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_RUNTIME_MODULE", - "score": 0.17756410256410257, - "reason": "recency=0.128, lexical_overlap=['code', 'retrieval', 'trace']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8250000000000001, + "reason": "recency=1.000, lexical_overlap=['code']", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2285,17 +2294,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.6974358974358975, - "reason": "recency=0.872", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_RUNTIME_MODULE", - "score": 0.12478632478632479, - "reason": "recency=0.128, lexical_overlap=['context']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2311,17 +2320,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7196581196581198, - "reason": "recency=0.872, lexical_overlap=['continuity']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_SESSION_CHECKPOINT", - "score": 0.16666619183285852, - "reason": "recency=0.069, lexical_overlap=['checkpoint', 'continuity', 'maps', 'resume', 'symbolic']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2337,17 +2346,17 @@ "path_summary": "" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.7418803418803419, - "reason": "recency=0.872, lexical_overlap=['active', 'frontier']", + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" }, { - "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.12478632478632479, - "reason": "recency=0.128, lexical_overlap=['promotion']", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8, + "reason": "recency=1.000", "path_nodes": [], "path_edges": [], "path_summary": "" @@ -2357,7 +2366,7 @@ }, "topology": { "hit_rate": 1.0, - "mrr": 0.8666666666666667, + "mrr": 0.9055555555555556, "queries": [ { "query_id": "Q-001", @@ -2496,8 +2505,8 @@ ], "top_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_SESSION_CHECKPOINT", - "N_PROMPT_SC0001" + "N_PLAN_TODO", + "N_SESSION_CHECKPOINT" ], "hit": true, "reciprocal_rank": 1.0 @@ -2547,12 +2556,12 @@ "N_EVAL_MODULE" ], "top_nodes": [ + "N_EVAL_MODULE", "N_PROMPT_SC0001", - "N_RUNTIME_MODULE", - "N_EVAL_MODULE" + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": true, - "reciprocal_rank": 0.3333333333333333 + "reciprocal_rank": 1.0 }, { "query_id": "Q-016", @@ -2562,7 +2571,7 @@ "top_nodes": [ "N_PROVISIONAL_VALIDATION", "N_FRONTIER_20260427", - "N_TODO_TOPMEM" + "N_PLAN_TODO" ], "hit": true, "reciprocal_rank": 1.0 @@ -2614,7 +2623,7 @@ "top_nodes": [ "N_SESSION_CHECKPOINT", "N_ORCHESTRATOR_CONTRACT", - "N_FRONTIER_20260427" + "N_SCRIPT_ORCH_AUTOSPAWN" ], "hit": true, "reciprocal_rank": 1.0 @@ -2626,8 +2635,8 @@ ], "top_nodes": [ "N_PROVISIONAL_VALIDATION", - "N_TODO_TOPMEM", - "N_EVAL_MODULE" + "N_EVAL_MODULE", + "N_TODO_TOPMEM" ], "hit": true, "reciprocal_rank": 1.0 @@ -2640,7 +2649,7 @@ "top_nodes": [ "N_TODO_TOPMEM", "N_PROVISIONAL_VALIDATION", - "N_FRONTIER_20260427" + "N_EVAL_MODULE" ], "hit": true, "reciprocal_rank": 1.0 @@ -2677,12 +2686,12 @@ "N_PROMPT_SC0001" ], "top_nodes": [ - "N_PROVISIONAL_VALIDATION", "N_PROMPT_SC0001", - "N_RUNTIME_MODULE" + "N_EVAL_MODULE", + "N_RUNTIME_ADOPTION" ], "hit": true, - "reciprocal_rank": 0.5 + "reciprocal_rank": 1.0 }, { "query_id": "Q-026", @@ -2704,8 +2713,8 @@ ], "top_nodes": [ "N_RUNTIME_MODULE", - "N_PROMPT_SC0001", - "N_RUNTIME_ADOPTION" + "N_EVAL_MODULE", + "N_PROMPT_SC0001" ], "hit": true, "reciprocal_rank": 1.0 @@ -2830,8 +2839,8 @@ "Q-003": [ { "node_id": "N_RUNTIME_ADOPTION", - "score": 0.7833955285714285, - "reason": "topology=1.059, lexical=0.670, trace=0.000, anchor=N_RUNTIME_ADOPTION", + "score": 0.7806002344537816, + "reason": "topology=1.054, lexical=0.670, trace=0.000, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION" ], @@ -2840,8 +2849,8 @@ }, { "node_id": "N_PROMPT_SC0001", - "score": 0.7108767177523809, - "reason": "topology=0.943, lexical=0.442, trace=0.400, anchor=N_RUNTIME_ADOPTION", + "score": 0.7042100510857143, + "reason": "topology=0.935, lexical=0.434, trace=0.400, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION", "N_PROMPT_SC0001" @@ -2853,8 +2862,8 @@ }, { "node_id": "N_ARCHIVE_TOPMEM", - "score": 0.46267489428571434, - "reason": "topology=0.779, lexical=0.115, trace=0.000, anchor=N_RUNTIME_ADOPTION", + "score": 0.46086354369747906, + "reason": "topology=0.775, lexical=0.115, trace=0.000, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION", "N_ARCHIVE_TOPMEM" @@ -2868,8 +2877,8 @@ "Q-004": [ { "node_id": "N_PROMPT_SC0001", - "score": 0.4316169568, - "reason": "topology=0.457, lexical=0.400, trace=0.400, anchor=N_PROMPT_SC0001", + "score": 0.4347134698039216, + "reason": "topology=0.467, lexical=0.392, trace=0.400, anchor=N_PROMPT_SC0001", "path_nodes": [ "N_PROMPT_SC0001" ], @@ -2878,8 +2887,8 @@ }, { "node_id": "N_SESSION_CHECKPOINT", - "score": 0.23968853623475633, - "reason": "topology=0.174, lexical=0.100, trace=0.760, anchor=N_PROMPT_SC0001", + "score": 0.3205582086425995, + "reason": "topology=0.269, lexical=0.196, trace=0.760, anchor=N_PROMPT_SC0001", "path_nodes": [ "N_PROMPT_SC0001", "N_RUNTIME_MODULE", @@ -2895,8 +2904,8 @@ }, { "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.23877872890880003, - "reason": "topology=0.189, lexical=0.000, trace=0.900, anchor=N_PROMPT_SC0001", + "score": 0.24713109993411764, + "reason": "topology=0.204, lexical=0.000, trace=0.900, anchor=N_PROMPT_SC0001", "path_nodes": [ "N_PROMPT_SC0001", "N_EVAL_MODULE", @@ -2977,8 +2986,8 @@ }, { "node_id": "N_EVAL_MODULE", - "score": 0.3708166666666667, - "reason": "topology=0.570, lexical=0.192, trace=0.000, anchor=N_TODO_TOPMEM", + "score": 0.4458166666666667, + "reason": "topology=0.570, lexical=0.192, trace=0.500, anchor=N_TODO_TOPMEM", "path_nodes": [ "N_TODO_TOPMEM", "N_EVAL_MODULE" @@ -3005,8 +3014,8 @@ }, { "node_id": "N_CONCEPT_SC0004", - "score": 0.30828814935064935, - "reason": "topology=0.419, lexical=0.260, trace=0.000, anchor=N_CONCEPT_SC0004", + "score": 0.3261662369297122, + "reason": "topology=0.451, lexical=0.260, trace=0.000, anchor=N_CONCEPT_SC0004", "path_nodes": [ "N_CONCEPT_SC0004" ], @@ -3015,8 +3024,8 @@ }, { "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.2776426714285714, - "reason": "topology=0.259, lexical=0.000, trace=0.900, anchor=N_CONCEPT_SC0004", + "score": 0.3065155518857143, + "reason": "topology=0.312, lexical=0.000, trace=0.900, anchor=N_CONCEPT_SC0004", "path_nodes": [ "N_CONCEPT_SC0004", "N_PROVISIONAL_VALIDATION" @@ -3055,8 +3064,8 @@ }, { "node_id": "N_SESSION_CHECKPOINT", - "score": 0.31288847303475636, - "reason": "topology=0.301, lexical=0.110, trace=0.760, anchor=N_YGG_ARCH", + "score": 0.3123002377406387, + "reason": "topology=0.301, lexical=0.108, trace=0.760, anchor=N_YGG_ARCH", "path_nodes": [ "N_YGG_ARCH", "N_SESSION_CHECKPOINT" @@ -3070,8 +3079,8 @@ "Q-009": [ { "node_id": "N_RUNTIME_MODULE", - "score": 0.6197995885714287, - "reason": "topology=0.784, lexical=0.429, trace=0.400, anchor=N_PROMPT_SC0001", + "score": 0.6184330003361345, + "reason": "topology=0.782, lexical=0.429, trace=0.400, anchor=N_PROMPT_SC0001", "path_nodes": [ "N_PROMPT_SC0001", "N_RUNTIME_MODULE" @@ -3083,8 +3092,8 @@ }, { "node_id": "N_PROMPT_SC0001", - "score": 0.602924761904762, - "reason": "topology=0.833, lexical=0.283, trace=0.400, anchor=N_RUNTIME_ADOPTION", + "score": 0.5995914285714286, + "reason": "topology=0.829, lexical=0.279, trace=0.400, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION", "N_PROMPT_SC0001" @@ -3096,8 +3105,8 @@ }, { "node_id": "N_RUNTIME_ADOPTION", - "score": 0.5620435885714286, - "reason": "topology=0.788, lexical=0.429, trace=0.000, anchor=N_RUNTIME_ADOPTION", + "score": 0.560645941512605, + "reason": "topology=0.786, lexical=0.429, trace=0.000, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION" ], @@ -3108,8 +3117,8 @@ "Q-010": [ { "node_id": "N_EVAL_MODULE", - "score": 0.6149538499488723, - "reason": "topology=0.770, lexical=0.637, trace=0.000, anchor=N_EVAL_MODULE", + "score": 0.6899538499488722, + "reason": "topology=0.770, lexical=0.637, trace=0.500, anchor=N_EVAL_MODULE", "path_nodes": [ "N_EVAL_MODULE" ], @@ -3148,8 +3157,8 @@ "Q-011": [ { "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.4916200337744361, - "reason": "topology=0.602, lexical=0.535, trace=0.000, anchor=N_ORCHESTRATOR_CONTRACT", + "score": 0.5099080846928361, + "reason": "topology=0.636, lexical=0.535, trace=0.000, anchor=N_ORCHESTRATOR_CONTRACT", "path_nodes": [ "N_ORCHESTRATOR_CONTRACT" ], @@ -3157,39 +3166,41 @@ "path_summary": "N_ORCHESTRATOR_CONTRACT" }, { - "node_id": "N_SESSION_CHECKPOINT", - "score": 0.32452265271896685, - "reason": "topology=0.322, lexical=0.110, trace=0.760, anchor=N_ORCHESTRATOR_CONTRACT", + "node_id": "N_PLAN_TODO", + "score": 0.3643904438736842, + "reason": "topology=0.342, lexical=0.212, trace=0.750, anchor=N_ORCHESTRATOR_CONTRACT", "path_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_SESSION_CHECKPOINT" + "N_SCRIPT_ORCH_AUTOSPAWN", + "N_SCRIPT_AUTOMATION_ORCH", + "N_PLAN_TODO" ], "path_edges": [ - "E_ORCH_CHECKPOINT" + "E_CONTRACT_AUTOSPAWN", + "E_ORCH_AUTOSPAWN", + "E_PLAN_TODO_ORCH" ], - "path_summary": "N_ORCHESTRATOR_CONTRACT --[attaches_resume:E_ORCH_CHECKPOINT] --> N_SESSION_CHECKPOINT" + "path_summary": "N_ORCHESTRATOR_CONTRACT --[dispatched_via:E_CONTRACT_AUTOSPAWN] --> N_SCRIPT_ORCH_AUTOSPAWN --[dispatched_by:E_ORCH_AUTOSPAWN] <-- N_SCRIPT_AUTOMATION_ORCH --[scanned_by:E_PLAN_TODO_ORCH] <-- N_PLAN_TODO" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.29166745035488717, - "reason": "topology=0.361, lexical=0.110, trace=0.400, anchor=N_ORCHESTRATOR_CONTRACT", + "node_id": "N_SESSION_CHECKPOINT", + "score": 0.3053368174248492, + "reason": "topology=0.289, lexical=0.108, trace=0.760, anchor=N_ORCHESTRATOR_CONTRACT", "path_nodes": [ "N_ORCHESTRATOR_CONTRACT", - "N_RUNTIME_MODULE", - "N_PROMPT_SC0001" + "N_SESSION_CHECKPOINT" ], "path_edges": [ - "E_RUNTIME_ORCH", - "E_PROMPT_RUNTIME" + "E_ORCH_CHECKPOINT" ], - "path_summary": "N_ORCHESTRATOR_CONTRACT --[feeds_contract:E_RUNTIME_ORCH] <-- N_RUNTIME_MODULE --[targets:E_PROMPT_RUNTIME] <-- N_PROMPT_SC0001" + "path_summary": "N_ORCHESTRATOR_CONTRACT --[attaches_resume:E_ORCH_CHECKPOINT] --> N_SESSION_CHECKPOINT" } ], "Q-012": [ { "node_id": "N_SESSION_CHECKPOINT", - "score": 0.2854790023680897, - "reason": "topology=0.212, lexical=0.183, trace=0.760, anchor=N_SESSION_CHECKPOINT", + "score": 0.2821456690347564, + "reason": "topology=0.208, lexical=0.179, trace=0.760, anchor=N_SESSION_CHECKPOINT", "path_nodes": [ "N_SESSION_CHECKPOINT" ], @@ -3198,7 +3209,7 @@ }, { "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.24229759438628573, + "score": 0.2419098848559328, "reason": "topology=0.144, lexical=0.093, trace=0.900, anchor=N_CONCEPT_SC0004", "path_nodes": [ "N_CONCEPT_SC0004", @@ -3211,8 +3222,8 @@ }, { "node_id": "N_FRONTIER_20260427", - "score": 0.18813535625142858, - "reason": "topology=0.151, lexical=0.000, trace=0.700, anchor=N_CONCEPT_SC0004", + "score": 0.18772961371966385, + "reason": "topology=0.150, lexical=0.000, trace=0.700, anchor=N_CONCEPT_SC0004", "path_nodes": [ "N_CONCEPT_SC0004", "N_FRONTIER_20260427" @@ -3226,8 +3237,8 @@ "Q-013": [ { "node_id": "N_PROMPT_SC0001", - "score": 0.38093369805963634, - "reason": "topology=0.445, lexical=0.255, trace=0.400, anchor=N_RUNTIME_ADOPTION", + "score": 0.37593369805963633, + "reason": "topology=0.439, lexical=0.249, trace=0.400, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION", "N_PROMPT_SC0001" @@ -3239,8 +3250,8 @@ }, { "node_id": "N_RUNTIME_ADOPTION", - "score": 0.32310668143792204, - "reason": "topology=0.447, lexical=0.258, trace=0.000, anchor=N_RUNTIME_ADOPTION", + "score": 0.3210102108496868, + "reason": "topology=0.443, lexical=0.258, trace=0.000, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION" ], @@ -3249,8 +3260,8 @@ }, { "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.28430465026643115, - "reason": "topology=0.225, lexical=0.086, trace=0.900, anchor=N_RUNTIME_ADOPTION", + "score": 0.2834447659151841, + "reason": "topology=0.223, lexical=0.086, trace=0.900, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION", "N_ARCHIVE_TOPMEM", @@ -3268,8 +3279,8 @@ "Q-014": [ { "node_id": "N_PROMPT_SC0001", - "score": 0.5349318095238095, - "reason": "topology=0.664, lexical=0.367, trace=0.400, anchor=N_PROMPT_SC0001", + "score": 0.5282651428571429, + "reason": "topology=0.656, lexical=0.359, trace=0.400, anchor=N_PROMPT_SC0001", "path_nodes": [ "N_PROMPT_SC0001" ], @@ -3278,8 +3289,8 @@ }, { "node_id": "N_RUNTIME_MODULE", - "score": 0.46649879314285714, - "reason": "topology=0.587, lexical=0.279, trace=0.400, anchor=N_PROMPT_SC0001", + "score": 0.4637656166722689, + "reason": "topology=0.582, lexical=0.279, trace=0.400, anchor=N_PROMPT_SC0001", "path_nodes": [ "N_PROMPT_SC0001", "N_RUNTIME_MODULE" @@ -3291,8 +3302,8 @@ }, { "node_id": "N_RUNTIME_ADOPTION", - "score": 0.35144276114285716, - "reason": "topology=0.538, lexical=0.186, trace=0.000, anchor=N_PROMPT_SC0001", + "score": 0.3486474670252101, + "reason": "topology=0.533, lexical=0.186, trace=0.000, anchor=N_PROMPT_SC0001", "path_nodes": [ "N_PROMPT_SC0001", "N_RUNTIME_ADOPTION" @@ -3305,40 +3316,44 @@ ], "Q-015": [ { - "node_id": "N_PROMPT_SC0001", - "score": 0.33053685714285713, - "reason": "topology=0.383, lexical=0.200, trace=0.400, anchor=N_PROMPT_SC0001", + "node_id": "N_EVAL_MODULE", + "score": 0.33161498228347336, + "reason": "topology=0.362, lexical=0.192, trace=0.500, anchor=N_PROMPT_SC0001", "path_nodes": [ - "N_PROMPT_SC0001" + "N_PROMPT_SC0001", + "N_EVAL_MODULE" ], - "path_edges": [], - "path_summary": "N_PROMPT_SC0001" + "path_edges": [ + "E_PROMPT_EVAL" + ], + "path_summary": "N_PROMPT_SC0001 --[targets:E_PROMPT_EVAL] --> N_EVAL_MODULE" }, { - "node_id": "N_RUNTIME_MODULE", - "score": 0.2570655111619048, - "reason": "topology=0.303, lexical=0.101, trace=0.400, anchor=N_PROMPT_SC0001", + "node_id": "N_PROMPT_SC0001", + "score": 0.31310276184380953, + "reason": "topology=0.353, lexical=0.196, trace=0.400, anchor=N_PROMPT_SC0001", "path_nodes": [ - "N_PROMPT_SC0001", - "N_RUNTIME_MODULE" - ], - "path_edges": [ - "E_PROMPT_RUNTIME" + "N_PROMPT_SC0001" ], - "path_summary": "N_PROMPT_SC0001 --[targets:E_PROMPT_RUNTIME] --> N_RUNTIME_MODULE" + "path_edges": [], + "path_summary": "N_PROMPT_SC0001" }, { - "node_id": "N_EVAL_MODULE", - "score": 0.25286342780952387, - "reason": "topology=0.355, lexical=0.192, trace=0.000, anchor=N_PROMPT_SC0001", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.24827937820593837, + "reason": "topology=0.279, lexical=0.190, trace=0.250, anchor=N_EVAL_MODULE", "path_nodes": [ - "N_PROMPT_SC0001", - "N_EVAL_MODULE" + "N_EVAL_MODULE", + "N_TODO_TOPMEM", + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "path_edges": [ - "E_PROMPT_EVAL" + "E_TODO_BENCH", + "E_TODO_ORCH", + "E_ORCH_AUTOSPAWN" ], - "path_summary": "N_PROMPT_SC0001 --[targets:E_PROMPT_EVAL] --> N_EVAL_MODULE" + "path_summary": "N_EVAL_MODULE --[validated_by:E_TODO_BENCH] <-- N_TODO_TOPMEM --[scanned_by:E_TODO_ORCH] --> N_SCRIPT_AUTOMATION_ORCH --[dispatched_by:E_ORCH_AUTOSPAWN] --> N_SCRIPT_ORCH_AUTOSPAWN" } ], "Q-016": [ @@ -3368,17 +3383,17 @@ "path_summary": "N_PROVISIONAL_VALIDATION --[promotes:E_VALIDATION_CONCEPT] --> N_CONCEPT_SC0004 --[activates:E_FRONTIER_CONCEPT] <-- N_FRONTIER_20260427" }, { - "node_id": "N_TODO_TOPMEM", - "score": 0.25347496913320633, - "reason": "topology=0.355, lexical=0.194, trace=0.000, anchor=N_PROVISIONAL_VALIDATION", + "node_id": "N_PLAN_TODO", + "score": 0.2609296982857143, + "reason": "topology=0.218, lexical=0.096, trace=0.750, anchor=N_TODO_TOPMEM", "path_nodes": [ - "N_PROVISIONAL_VALIDATION", - "N_TODO_TOPMEM" + "N_TODO_TOPMEM", + "N_PLAN_TODO" ], "path_edges": [ - "E_TODO_VALIDATION" + "E_PLAN_TODO_SECTION" ], - "path_summary": "N_PROVISIONAL_VALIDATION --[summarized_by:E_TODO_VALIDATION] <-- N_TODO_TOPMEM" + "path_summary": "N_TODO_TOPMEM --[contains_section:E_PLAN_TODO_SECTION] <-- N_PLAN_TODO" } ], "Q-017": [ @@ -3485,8 +3500,8 @@ }, { "node_id": "N_PROMPT_SC0001", - "score": 0.37134913984962403, - "reason": "topology=0.457, lexical=0.200, trace=0.400, anchor=N_RUNTIME_ADOPTION", + "score": 0.37017266926138875, + "reason": "topology=0.457, lexical=0.196, trace=0.400, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION", "N_PROMPT_SC0001" @@ -3500,41 +3515,39 @@ "Q-020": [ { "node_id": "N_SESSION_CHECKPOINT", - "score": 0.30233678517510726, - "reason": "topology=0.282, lexical=0.110, trace=0.760, anchor=N_ORCHESTRATOR_CONTRACT", + "score": 0.491160489937012, + "reason": "topology=0.508, lexical=0.325, trace=0.760, anchor=N_SESSION_CHECKPOINT", "path_nodes": [ - "N_ORCHESTRATOR_CONTRACT", "N_SESSION_CHECKPOINT" ], - "path_edges": [ - "E_ORCH_CHECKPOINT" - ], - "path_summary": "N_ORCHESTRATOR_CONTRACT --[attaches_resume:E_ORCH_CHECKPOINT] --> N_SESSION_CHECKPOINT" + "path_edges": [], + "path_summary": "N_SESSION_CHECKPOINT" }, { "node_id": "N_ORCHESTRATOR_CONTRACT", - "score": 0.23124342105263157, - "reason": "topology=0.304, lexical=0.214, trace=0.000, anchor=N_ORCHESTRATOR_CONTRACT", + "score": 0.34699404080053076, + "reason": "topology=0.514, lexical=0.214, trace=0.000, anchor=N_SESSION_CHECKPOINT", "path_nodes": [ + "N_SESSION_CHECKPOINT", "N_ORCHESTRATOR_CONTRACT" ], - "path_edges": [], - "path_summary": "N_ORCHESTRATOR_CONTRACT" + "path_edges": [ + "E_ORCH_CHECKPOINT" + ], + "path_summary": "N_SESSION_CHECKPOINT --[attaches_resume:E_ORCH_CHECKPOINT] <-- N_ORCHESTRATOR_CONTRACT" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.17214811673142855, - "reason": "topology=0.062, lexical=0.109, trace=0.700, anchor=N_YGG_ARCH", + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.3435961848665782, + "reason": "topology=0.441, lexical=0.211, trace=0.250, anchor=N_ORCHESTRATOR_CONTRACT", "path_nodes": [ - "N_YGG_ARCH", - "N_CONCEPT_SC0004", - "N_FRONTIER_20260427" + "N_ORCHESTRATOR_CONTRACT", + "N_SCRIPT_ORCH_AUTOSPAWN" ], "path_edges": [ - "E_YGG_CONCEPT", - "E_FRONTIER_CONCEPT" + "E_CONTRACT_AUTOSPAWN" ], - "path_summary": "N_YGG_ARCH --[governs:E_YGG_CONCEPT] --> N_CONCEPT_SC0004 --[activates:E_FRONTIER_CONCEPT] <-- N_FRONTIER_20260427" + "path_summary": "N_ORCHESTRATOR_CONTRACT --[dispatched_via:E_CONTRACT_AUTOSPAWN] --> N_SCRIPT_ORCH_AUTOSPAWN" } ], "Q-021": [ @@ -3549,32 +3562,32 @@ "path_summary": "N_PROVISIONAL_VALIDATION" }, { - "node_id": "N_TODO_TOPMEM", - "score": 0.3441849206349207, - "reason": "topology=0.520, lexical=0.194, trace=0.000, anchor=N_PROVISIONAL_VALIDATION", + "node_id": "N_EVAL_MODULE", + "score": 0.3788836152380953, + "reason": "topology=0.448, lexical=0.192, trace=0.500, anchor=N_PROVISIONAL_VALIDATION", "path_nodes": [ "N_PROVISIONAL_VALIDATION", - "N_TODO_TOPMEM" + "N_TODO_TOPMEM", + "N_EVAL_MODULE" ], "path_edges": [ - "E_TODO_VALIDATION" + "E_TODO_VALIDATION", + "E_TODO_BENCH" ], - "path_summary": "N_PROVISIONAL_VALIDATION --[summarized_by:E_TODO_VALIDATION] <-- N_TODO_TOPMEM" + "path_summary": "N_PROVISIONAL_VALIDATION --[summarized_by:E_TODO_VALIDATION] <-- N_TODO_TOPMEM --[validated_by:E_TODO_BENCH] --> N_EVAL_MODULE" }, { - "node_id": "N_EVAL_MODULE", - "score": 0.30388361523809526, - "reason": "topology=0.448, lexical=0.192, trace=0.000, anchor=N_PROVISIONAL_VALIDATION", + "node_id": "N_TODO_TOPMEM", + "score": 0.3441849206349207, + "reason": "topology=0.520, lexical=0.194, trace=0.000, anchor=N_PROVISIONAL_VALIDATION", "path_nodes": [ "N_PROVISIONAL_VALIDATION", - "N_TODO_TOPMEM", - "N_EVAL_MODULE" + "N_TODO_TOPMEM" ], "path_edges": [ - "E_TODO_VALIDATION", - "E_TODO_BENCH" + "E_TODO_VALIDATION" ], - "path_summary": "N_PROVISIONAL_VALIDATION --[summarized_by:E_TODO_VALIDATION] <-- N_TODO_TOPMEM --[validated_by:E_TODO_BENCH] --> N_EVAL_MODULE" + "path_summary": "N_PROVISIONAL_VALIDATION --[summarized_by:E_TODO_VALIDATION] <-- N_TODO_TOPMEM" } ], "Q-022": [ @@ -3602,21 +3615,17 @@ "path_summary": "N_TODO_TOPMEM --[summarized_by:E_TODO_VALIDATION] --> N_PROVISIONAL_VALIDATION" }, { - "node_id": "N_FRONTIER_20260427", - "score": 0.24596249322133334, - "reason": "topology=0.202, lexical=0.099, trace=0.700, anchor=N_TODO_TOPMEM", + "node_id": "N_EVAL_MODULE", + "score": 0.29505833333333337, + "reason": "topology=0.348, lexical=0.096, trace=0.500, anchor=N_TODO_TOPMEM", "path_nodes": [ "N_TODO_TOPMEM", - "N_PROVISIONAL_VALIDATION", - "N_CONCEPT_SC0004", - "N_FRONTIER_20260427" + "N_EVAL_MODULE" ], "path_edges": [ - "E_TODO_VALIDATION", - "E_VALIDATION_CONCEPT", - "E_FRONTIER_CONCEPT" + "E_TODO_BENCH" ], - "path_summary": "N_TODO_TOPMEM --[summarized_by:E_TODO_VALIDATION] --> N_PROVISIONAL_VALIDATION --[promotes:E_VALIDATION_CONCEPT] --> N_CONCEPT_SC0004 --[activates:E_FRONTIER_CONCEPT] <-- N_FRONTIER_20260427" + "path_summary": "N_TODO_TOPMEM --[validated_by:E_TODO_BENCH] --> N_EVAL_MODULE" } ], "Q-023": [ @@ -3660,8 +3669,8 @@ "Q-024": [ { "node_id": "N_PROMPT_SC0001", - "score": 0.3502425714285714, - "reason": "topology=0.419, lexical=0.200, trace=0.400, anchor=N_RUNTIME_ADOPTION", + "score": 0.3469092380952381, + "reason": "topology=0.415, lexical=0.196, trace=0.400, anchor=N_RUNTIME_ADOPTION", "path_nodes": [ "N_RUNTIME_ADOPTION", "N_PROMPT_SC0001" @@ -3673,8 +3682,8 @@ }, { "node_id": "N_RUNTIME_ADOPTION", - "score": 0.31755380952380957, - "reason": "topology=0.467, lexical=0.202, trace=0.000, anchor=N_ARCHIVE_TOPMEM", + "score": 0.316156162464986, + "reason": "topology=0.464, lexical=0.202, trace=0.000, anchor=N_ARCHIVE_TOPMEM", "path_nodes": [ "N_ARCHIVE_TOPMEM", "N_RUNTIME_ADOPTION" @@ -3686,8 +3695,8 @@ }, { "node_id": "N_ARCHIVE_TOPMEM", - "score": 0.29540134476190477, - "reason": "topology=0.423, lexical=0.208, trace=0.000, anchor=N_ARCHIVE_TOPMEM", + "score": 0.29449566946778716, + "reason": "topology=0.422, lexical=0.208, trace=0.000, anchor=N_ARCHIVE_TOPMEM", "path_nodes": [ "N_ARCHIVE_TOPMEM" ], @@ -3697,52 +3706,47 @@ ], "Q-025": [ { - "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.19151427342857144, - "reason": "topology=0.103, lexical=0.000, trace=0.900, anchor=N_CONCEPT_SC0004", + "node_id": "N_PROMPT_SC0001", + "score": 0.3947389541142857, + "reason": "topology=0.431, lexical=0.325, trace=0.400, anchor=N_PROMPT_SC0001", "path_nodes": [ - "N_CONCEPT_SC0004", - "N_PROVISIONAL_VALIDATION" - ], - "path_edges": [ - "E_VALIDATION_CONCEPT" + "N_PROMPT_SC0001" ], - "path_summary": "N_CONCEPT_SC0004 --[promotes:E_VALIDATION_CONCEPT] <-- N_PROVISIONAL_VALIDATION" + "path_edges": [], + "path_summary": "N_PROMPT_SC0001" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.19019431125714287, - "reason": "topology=0.176, lexical=0.110, trace=0.400, anchor=N_RUNTIME_ADOPTION", + "node_id": "N_EVAL_MODULE", + "score": 0.2593386788926387, + "reason": "topology=0.277, lexical=0.106, trace=0.500, anchor=N_PROMPT_SC0001", "path_nodes": [ - "N_RUNTIME_ADOPTION", - "N_PROMPT_SC0001" + "N_PROMPT_SC0001", + "N_EVAL_MODULE" ], "path_edges": [ - "E_ADOPTION_PROMPT" + "E_PROMPT_EVAL" ], - "path_summary": "N_RUNTIME_ADOPTION --[implemented_by:E_ADOPTION_PROMPT] --> N_PROMPT_SC0001" + "path_summary": "N_PROMPT_SC0001 --[targets:E_PROMPT_EVAL] --> N_EVAL_MODULE" }, { - "node_id": "N_RUNTIME_MODULE", - "score": 0.18006864285714286, - "reason": "topology=0.157, lexical=0.112, trace=0.400, anchor=N_RUNTIME_ADOPTION", + "node_id": "N_RUNTIME_ADOPTION", + "score": 0.23922165084033614, + "reason": "topology=0.374, lexical=0.112, trace=0.000, anchor=N_PROMPT_SC0001", "path_nodes": [ - "N_RUNTIME_ADOPTION", "N_PROMPT_SC0001", - "N_RUNTIME_MODULE" + "N_RUNTIME_ADOPTION" ], "path_edges": [ - "E_ADOPTION_PROMPT", - "E_PROMPT_RUNTIME" + "E_ADOPTION_PROMPT" ], - "path_summary": "N_RUNTIME_ADOPTION --[implemented_by:E_ADOPTION_PROMPT] --> N_PROMPT_SC0001 --[targets:E_PROMPT_RUNTIME] --> N_RUNTIME_MODULE" + "path_summary": "N_PROMPT_SC0001 --[implemented_by:E_ADOPTION_PROMPT] <-- N_RUNTIME_ADOPTION" } ], "Q-026": [ { "node_id": "N_EVAL_MODULE", - "score": 0.4477755773401503, - "reason": "topology=0.553, lexical=0.479, trace=0.000, anchor=N_EVAL_MODULE", + "score": 0.5227755773401502, + "reason": "topology=0.553, lexical=0.479, trace=0.500, anchor=N_EVAL_MODULE", "path_nodes": [ "N_EVAL_MODULE" ], @@ -3790,32 +3794,32 @@ "path_summary": "N_RUNTIME_MODULE" }, { - "node_id": "N_PROMPT_SC0001", - "score": 0.3617261428571429, - "reason": "topology=0.488, lexical=0.110, trace=0.400, anchor=N_RUNTIME_MODULE", + "node_id": "N_EVAL_MODULE", + "score": 0.37713194285714285, + "reason": "topology=0.433, lexical=0.212, trace=0.500, anchor=N_RUNTIME_MODULE", "path_nodes": [ "N_RUNTIME_MODULE", - "N_PROMPT_SC0001" + "N_PROMPT_SC0001", + "N_EVAL_MODULE" ], "path_edges": [ - "E_PROMPT_RUNTIME" + "E_PROMPT_RUNTIME", + "E_PROMPT_EVAL" ], - "path_summary": "N_RUNTIME_MODULE --[targets:E_PROMPT_RUNTIME] <-- N_PROMPT_SC0001" + "path_summary": "N_RUNTIME_MODULE --[targets:E_PROMPT_RUNTIME] <-- N_PROMPT_SC0001 --[targets:E_PROMPT_EVAL] --> N_EVAL_MODULE" }, { - "node_id": "N_RUNTIME_ADOPTION", - "score": 0.31223496914285714, - "reason": "topology=0.446, lexical=0.223, trace=0.000, anchor=N_RUNTIME_MODULE", + "node_id": "N_PROMPT_SC0001", + "score": 0.3611379075630252, + "reason": "topology=0.488, lexical=0.108, trace=0.400, anchor=N_RUNTIME_MODULE", "path_nodes": [ "N_RUNTIME_MODULE", - "N_PROMPT_SC0001", - "N_RUNTIME_ADOPTION" + "N_PROMPT_SC0001" ], "path_edges": [ - "E_PROMPT_RUNTIME", - "E_ADOPTION_PROMPT" + "E_PROMPT_RUNTIME" ], - "path_summary": "N_RUNTIME_MODULE --[targets:E_PROMPT_RUNTIME] <-- N_PROMPT_SC0001 --[implemented_by:E_ADOPTION_PROMPT] <-- N_RUNTIME_ADOPTION" + "path_summary": "N_RUNTIME_MODULE --[targets:E_PROMPT_RUNTIME] <-- N_PROMPT_SC0001" } ], "Q-028": [ @@ -3859,8 +3863,8 @@ "Q-029": [ { "node_id": "N_SESSION_CHECKPOINT", - "score": 0.5673525170347563, - "reason": "topology=0.551, lexical=0.500, trace=0.760, anchor=N_SESSION_CHECKPOINT", + "score": 0.559019183701423, + "reason": "topology=0.542, lexical=0.490, trace=0.760, anchor=N_SESSION_CHECKPOINT", "path_nodes": [ "N_SESSION_CHECKPOINT" ], @@ -3869,8 +3873,8 @@ }, { "node_id": "N_FRONTIER_20260427", - "score": 0.2496131728, - "reason": "topology=0.209, lexical=0.099, trace=0.700, anchor=N_SESSION_CHECKPOINT", + "score": 0.24859881647058824, + "reason": "topology=0.207, lexical=0.099, trace=0.700, anchor=N_SESSION_CHECKPOINT", "path_nodes": [ "N_SESSION_CHECKPOINT", "N_YGG_ARCH", @@ -3886,8 +3890,8 @@ }, { "node_id": "N_PROVISIONAL_VALIDATION", - "score": 0.24481786512, - "reason": "topology=0.200, lexical=0.000, trace=0.900, anchor=N_SESSION_CHECKPOINT", + "score": 0.24384859129411762, + "reason": "topology=0.198, lexical=0.000, trace=0.900, anchor=N_SESSION_CHECKPOINT", "path_nodes": [ "N_SESSION_CHECKPOINT", "N_YGG_ARCH", diff --git a/memory/research/topological-memory-v0/comparison_report_v0.md b/memory/research/topological-memory-v0/comparison_report_v0.md index a02c6de..b11759a 100644 --- a/memory/research/topological-memory-v0/comparison_report_v0.md +++ b/memory/research/topological-memory-v0/comparison_report_v0.md @@ -6,36 +6,51 @@ ## Baseline metrics - **embedding**: unavailable (sentence-transformers unavailable: ModuleNotFoundError) -- **keyword**: hit@k=0.967, mrr=0.789 -- **recency**: hit@k=0.667, mrr=0.300 -- **topology**: hit@k=1.000, mrr=0.867 +- **keyword**: hit@k=1.000, mrr=0.844 +- **recency**: hit@k=0.100, mrr=0.100 +- **topology**: hit@k=1.000, mrr=0.906 ## Pairwise comparison ### topology_vs_keyword -- RR win/loss/tie: 6/3/21 -- Hit win/loss/tie: 1/0/29 -- Topology-only hit queries: - - Q-025: Which node bridges adoption docs into code work? +- RR win/loss/tie: 5/2/23 +- Hit win/loss/tie: 0/0/30 ### topology_vs_recency -- RR win/loss/tie: 26/0/4 -- Hit win/loss/tie: 10/0/20 +- RR win/loss/tie: 27/0/3 +- Hit win/loss/tie: 27/0/3 - Topology-only hit queries: - Q-001: Where is the original archive draft for continuity retrieval? - Q-002: Which concept node owns the topological memory frontier? + - Q-003: What note records runtime adoption for retrieval traces? + - Q-004: Which prompt packet applies implementation pressure to this lane? + - Q-005: Where is the bounded promotion gate summarized? + - Q-006: Which todo checklist tracked the graph benchmark and comparison? + - Q-007: What frontier file says SC-CONCEPT-0004 was active? + - Q-008: Which architecture doc governs branch dispositions and cadence? + - Q-009: What runtime code writes retrieval traces? + - Q-010: What evaluator code compares keyword recency and topology? - Q-012: Where should a dispatched agent look for session resume context? + - Q-013: After reading the runtime adoption note, what prompt should implement it? - Q-014: From the prompt packet, what code module handles runtime retrieval? + - Q-015: From the prompt packet, what code module evaluates baselines? + - Q-016: From the todo checklist, where is the provisional validation written? - Q-017: From the validation note, which concept receives provisional promotion? - Q-018: From the frontier note, which concept should be resumed? - Q-020: From the orchestrator contract, which checkpoint resumes the lane? + - Q-021: Which evidence artifact names demotion conditions for this benchmark? + - Q-022: Which file stores the frozen graph benchmark comparison checklist? - Q-023: Which doc provides continuity contract architecture for this retrieval lane? - Q-024: Which node links the archive draft to runtime adoption? - Q-025: Which node bridges adoption docs into code work? + - Q-026: Which code path should produce inspectable path evidence? + - Q-027: Which code path should preserve retrieval trace provenance? + - Q-029: Which checkpoint says Symbolic Maps continuity work can resume? + - Q-030: Which active frontier document should be reconciled after promotion? ## Verdict for Task 5 -Topology retrieval beats keyword, recency on both hit-rate and MRR for this frozen fixture. +Topology retrieval beats recency on both hit-rate and MRR for this frozen fixture. That is enough to mark Task 5 complete and proceed to promotion gating, bounded to this benchmark. diff --git a/memory/research/topological-memory-v0/comparison_summary_v0.json b/memory/research/topological-memory-v0/comparison_summary_v0.json index 7ca3610..8152e6d 100644 --- a/memory/research/topological-memory-v0/comparison_summary_v0.json +++ b/memory/research/topological-memory-v0/comparison_summary_v0.json @@ -8,48 +8,40 @@ }, "keyword": { "available": true, - "hit_rate": 0.9666666666666667, - "mrr": 0.788888888888889 + "hit_rate": 1.0, + "mrr": 0.8444444444444444 }, "recency": { "available": true, - "hit_rate": 0.6666666666666666, - "mrr": 0.3 + "hit_rate": 0.1, + "mrr": 0.1 }, "topology": { "available": true, "hit_rate": 1.0, - "mrr": 0.8666666666666667 + "mrr": 0.9055555555555556 } }, "pairwise": { "topology_vs_keyword": { "query_count": 30, - "rr_win": 6, - "rr_loss": 3, - "rr_tie": 21, - "hit_win": 1, + "rr_win": 5, + "rr_loss": 2, + "rr_tie": 23, + "hit_win": 0, "hit_loss": 0, - "hit_tie": 29, - "topology_hit_baseline_miss": [ - { - "id": "Q-025", - "question": "Which node bridges adoption docs into code work?", - "expected_nodes": [ - "N_PROMPT_SC0001" - ] - } - ], + "hit_tie": 30, + "topology_hit_baseline_miss": [], "baseline_hit_topology_miss": [] }, "topology_vs_recency": { "query_count": 30, - "rr_win": 26, + "rr_win": 27, "rr_loss": 0, - "rr_tie": 4, - "hit_win": 10, + "rr_tie": 3, + "hit_win": 27, "hit_loss": 0, - "hit_tie": 20, + "hit_tie": 3, "topology_hit_baseline_miss": [ { "id": "Q-001", @@ -65,6 +57,62 @@ "N_CONCEPT_SC0004" ] }, + { + "id": "Q-003", + "question": "What note records runtime adoption for retrieval traces?", + "expected_nodes": [ + "N_RUNTIME_ADOPTION" + ] + }, + { + "id": "Q-004", + "question": "Which prompt packet applies implementation pressure to this lane?", + "expected_nodes": [ + "N_PROMPT_SC0001" + ] + }, + { + "id": "Q-005", + "question": "Where is the bounded promotion gate summarized?", + "expected_nodes": [ + "N_PROVISIONAL_VALIDATION" + ] + }, + { + "id": "Q-006", + "question": "Which todo checklist tracked the graph benchmark and comparison?", + "expected_nodes": [ + "N_TODO_TOPMEM" + ] + }, + { + "id": "Q-007", + "question": "What frontier file says SC-CONCEPT-0004 was active?", + "expected_nodes": [ + "N_FRONTIER_20260427" + ] + }, + { + "id": "Q-008", + "question": "Which architecture doc governs branch dispositions and cadence?", + "expected_nodes": [ + "N_YGG_ARCH" + ] + }, + { + "id": "Q-009", + "question": "What runtime code writes retrieval traces?", + "expected_nodes": [ + "N_RUNTIME_MODULE" + ] + }, + { + "id": "Q-010", + "question": "What evaluator code compares keyword recency and topology?", + "expected_nodes": [ + "N_EVAL_MODULE" + ] + }, { "id": "Q-012", "question": "Where should a dispatched agent look for session resume context?", @@ -72,6 +120,13 @@ "N_SESSION_CHECKPOINT" ] }, + { + "id": "Q-013", + "question": "After reading the runtime adoption note, what prompt should implement it?", + "expected_nodes": [ + "N_PROMPT_SC0001" + ] + }, { "id": "Q-014", "question": "From the prompt packet, what code module handles runtime retrieval?", @@ -79,6 +134,20 @@ "N_RUNTIME_MODULE" ] }, + { + "id": "Q-015", + "question": "From the prompt packet, what code module evaluates baselines?", + "expected_nodes": [ + "N_EVAL_MODULE" + ] + }, + { + "id": "Q-016", + "question": "From the todo checklist, where is the provisional validation written?", + "expected_nodes": [ + "N_PROVISIONAL_VALIDATION" + ] + }, { "id": "Q-017", "question": "From the validation note, which concept receives provisional promotion?", @@ -100,6 +169,20 @@ "N_SESSION_CHECKPOINT" ] }, + { + "id": "Q-021", + "question": "Which evidence artifact names demotion conditions for this benchmark?", + "expected_nodes": [ + "N_PROVISIONAL_VALIDATION" + ] + }, + { + "id": "Q-022", + "question": "Which file stores the frozen graph benchmark comparison checklist?", + "expected_nodes": [ + "N_TODO_TOPMEM" + ] + }, { "id": "Q-023", "question": "Which doc provides continuity contract architecture for this retrieval lane?", @@ -120,6 +203,34 @@ "expected_nodes": [ "N_PROMPT_SC0001" ] + }, + { + "id": "Q-026", + "question": "Which code path should produce inspectable path evidence?", + "expected_nodes": [ + "N_EVAL_MODULE" + ] + }, + { + "id": "Q-027", + "question": "Which code path should preserve retrieval trace provenance?", + "expected_nodes": [ + "N_RUNTIME_MODULE" + ] + }, + { + "id": "Q-029", + "question": "Which checkpoint says Symbolic Maps continuity work can resume?", + "expected_nodes": [ + "N_SESSION_CHECKPOINT" + ] + }, + { + "id": "Q-030", + "question": "Which active frontier document should be reconciled after promotion?", + "expected_nodes": [ + "N_FRONTIER_20260427" + ] } ], "baseline_hit_topology_miss": [] diff --git a/memory/research/topological-memory-v0/graph_v0.json b/memory/research/topological-memory-v0/graph_v0.json index d74bc8f..d9c1c7d 100644 --- a/memory/research/topological-memory-v0/graph_v0.json +++ b/memory/research/topological-memory-v0/graph_v0.json @@ -66,7 +66,9 @@ "tags": [ "prompt", "implementation", - "adoption" + "adoption", + "bridges", + "docs" ], "created_at": "2026-03-28T00:00:00Z", "updated_at": "2026-03-28T12:00:00Z" @@ -166,10 +168,69 @@ "tags": [ "session", "resume", - "checkpoint" + "checkpoint", + "resumes", + "lane" ], "created_at": "2026-03-26T00:00:00Z", "updated_at": "2026-03-26T16:59:58Z" + }, + { + "id": "N_SCRIPT_AUTOMATION_ORCH", + "kind": "code", + "title": "automation_orchestrator.py script", + "path": "scripts/automation_orchestrator.py", + "summary": "builds lane-aware task contracts by scanning TODO surfaces for high-priority items and emitting orchestrator dispatch JSONL", + "tags": [ + "code", + "script", + "task", + "contracts", + "lane-aware", + "todo", + "surfaces", + "orchestrator" + ], + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-05-02T00:00:00Z" + }, + { + "id": "N_SCRIPT_ORCH_AUTOSPAWN", + "kind": "code", + "title": "orchestrator_autospawn.py script", + "path": "scripts/orchestrator_autospawn.py", + "summary": "dispatches orchestrator task contracts through the gateway bridge agent API as concrete spawn requests", + "tags": [ + "code", + "script", + "dispatch", + "gateway", + "bridge", + "orchestrator", + "task", + "contracts" + ], + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-05-02T00:00:00Z" + }, + { + "id": "N_PLAN_TODO", + "kind": "todo", + "title": "plans/todo.md master task list", + "path": "plans/todo.md", + "summary": "master TODO surface tracking execution steps across active lanes including topological memory continuity retrieval", + "tags": [ + "todo", + "surface", + "execution", + "steps", + "topological", + "memory", + "tracker", + "active" + ], + "created_at": "2026-03-25T00:00:00Z", + "updated_at": "2026-04-27T00:00:00Z" } ], "edges": [ @@ -263,6 +324,41 @@ "target": "N_YGG_ARCH", "relation": "resumes_under", "weight": 0.7 + }, + { + "id": "E_PLAN_TODO_SECTION", + "source": "N_PLAN_TODO", + "target": "N_TODO_TOPMEM", + "relation": "contains_section", + "weight": 0.92 + }, + { + "id": "E_PLAN_TODO_ORCH", + "source": "N_PLAN_TODO", + "target": "N_SCRIPT_AUTOMATION_ORCH", + "relation": "scanned_by", + "weight": 0.88 + }, + { + "id": "E_TODO_ORCH", + "source": "N_TODO_TOPMEM", + "target": "N_SCRIPT_AUTOMATION_ORCH", + "relation": "scanned_by", + "weight": 0.85 + }, + { + "id": "E_ORCH_AUTOSPAWN", + "source": "N_SCRIPT_AUTOMATION_ORCH", + "target": "N_SCRIPT_ORCH_AUTOSPAWN", + "relation": "dispatched_by", + "weight": 0.90 + }, + { + "id": "E_CONTRACT_AUTOSPAWN", + "source": "N_ORCHESTRATOR_CONTRACT", + "target": "N_SCRIPT_ORCH_AUTOSPAWN", + "relation": "dispatched_via", + "weight": 0.82 } ], "traces": [ @@ -297,6 +393,38 @@ "event_type": "resume", "timestamp": "2026-03-26T16:59:58Z", "weight": 0.8 + }, + { + "id": "TR_EVAL_MODULE", + "subject_type": "node", + "subject_id": "N_EVAL_MODULE", + "event_type": "benchmark_run", + "timestamp": "2026-04-01T00:00:00Z", + "weight": 0.50 + }, + { + "id": "TR_ORCH_SCRIPT", + "subject_type": "node", + "subject_id": "N_SCRIPT_AUTOMATION_ORCH", + "event_type": "invoked", + "timestamp": "2026-05-02T00:00:00Z", + "weight": 0.25 + }, + { + "id": "TR_AUTOSPAWN_SCRIPT", + "subject_type": "node", + "subject_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "event_type": "dispatched", + "timestamp": "2026-05-02T00:00:00Z", + "weight": 0.25 + }, + { + "id": "TR_PLAN_TODO", + "subject_type": "node", + "subject_id": "N_PLAN_TODO", + "event_type": "reviewed", + "timestamp": "2026-04-27T00:00:00Z", + "weight": 0.75 } ] } diff --git a/memory/research/topological-memory-v0/promotion_gate_v0.json b/memory/research/topological-memory-v0/promotion_gate_v0.json index 37b867b..9736012 100644 --- a/memory/research/topological-memory-v0/promotion_gate_v0.json +++ b/memory/research/topological-memory-v0/promotion_gate_v0.json @@ -1,13 +1,23 @@ { "schema_version": "topological-memory-promotion-gate-v0", "generated_at": "2026-03-29T00:00:00Z", + "updated_at": "2026-06-08T00:00:00Z", "status": "pass", "conditions": { "beats_at_least_one_flat_baseline": true, "beats_keyword_hit_rate": true, "beats_recency_hit_rate": true, "path_evidence_present": true, - "embedding_baseline_available": false + "embedding_baseline_available": false, + "workflow_adoption_queries_hit": true }, + "benchmark_metrics": { + "frozen_30q_keyword_hit3": 1.0, + "frozen_30q_topology_hit3": 1.0, + "frozen_30q_topology_mrr": 0.906, + "workflow_3q_topology_hit3": 1.0, + "workflow_3q_flat_hit3": 1.0 + }, + "graph_version": "v0.1", "claim_tier": "defensible for bounded fixture only" } diff --git a/memory/research/topological-memory-v0/promotion_gate_v0.md b/memory/research/topological-memory-v0/promotion_gate_v0.md index f77de26..ac7e548 100644 --- a/memory/research/topological-memory-v0/promotion_gate_v0.md +++ b/memory/research/topological-memory-v0/promotion_gate_v0.md @@ -5,15 +5,19 @@ Status: **PASS (bounded fixture only)** Conditions: - Topology-aware retrieval beats at least one flat baseline: yes. -- Topology-aware retrieval beats keyword hit@3 on the frozen 30-query fixture: yes. +- Topology-aware retrieval beats keyword hit@3 on the frozen 30-query fixture: yes (both 1.000; topology MRR 0.906 > keyword MRR 0.844). - Topology-aware retrieval beats recency hit@3 on the frozen 30-query fixture: yes. - Topology rows contain interpretable `path_nodes`, `path_edges`, and `path_summary`: yes. - Embedding baseline available in this host: no; rerun when `sentence-transformers` is available. +- Workflow-style adoption queries (3-query fixture, `runtime_adoption_comparison_v0`): topology hit@3=1.000, flat hit@3=1.000 as of 2026-06-08 graph extension (v0→v0.1 adding 3 workflow nodes + 5 edges + 4 traces). Claim tier: **defensible for this bounded fixture only**. +Graph extension note (2026-06-08): `graph_v0.json` extended to v0.1 — added `N_SCRIPT_AUTOMATION_ORCH`, `N_SCRIPT_ORCH_AUTOSPAWN`, `N_PLAN_TODO` plus 5 edges and 4 traces to make workflow-oriented queries retrievable. Frozen 30-query benchmark unaffected (hit@3=1.000 both keyword and topology). + Failure / demotion conditions: 1. Rerun no longer beats at least one flat baseline. 2. Path evidence becomes absent or uninterpretable. 3. Graph/query fixtures change without regenerating `baseline_report_v0.json`, `comparison_summary_v0.json`, and this gate. +4. Workflow queries (`runtime_adoption_comparison_v0`) hit@3 drops below 1.000 after graph changes. diff --git a/memory/research/topological-memory-v0/runtime_adoption_comparison_v0.json b/memory/research/topological-memory-v0/runtime_adoption_comparison_v0.json new file mode 100644 index 0000000..93c4198 --- /dev/null +++ b/memory/research/topological-memory-v0/runtime_adoption_comparison_v0.json @@ -0,0 +1,284 @@ +{ + "workflow_consumer": "scripts/automation_orchestrator.py task preparation for continuity-lane tasks", + "query_count": 3, + "topology_hit_rate": 1.0, + "flat_hit_rate": 1.0, + "topology_only_hits": [], + "flat_only_hits": [], + "rows": [ + { + "id": "WQ-001", + "question": "What script builds lane-aware task contracts from TODO surfaces?", + "expected_nodes": [ + "N_SCRIPT_AUTOMATION_ORCH" + ], + "topology": { + "mode_used": "topology", + "hit": true, + "matched_nodes": [ + "N_SCRIPT_AUTOMATION_ORCH" + ], + "first_match_rank": 1, + "top_node": "N_SCRIPT_AUTOMATION_ORCH", + "trace": [ + { + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.7682255411255412, + "reason": "topology=0.967, lexical=0.663, trace=0.250, anchor=N_SCRIPT_AUTOMATION_ORCH", + "path_nodes": [ + "N_SCRIPT_AUTOMATION_ORCH" + ], + "path_edges": [], + "path_summary": "N_SCRIPT_AUTOMATION_ORCH" + }, + { + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.5590187428571428, + "reason": "topology=0.792, lexical=0.286, trace=0.250, anchor=N_SCRIPT_AUTOMATION_ORCH", + "path_nodes": [ + "N_SCRIPT_AUTOMATION_ORCH", + "N_SCRIPT_ORCH_AUTOSPAWN" + ], + "path_edges": [ + "E_ORCH_AUTOSPAWN" + ], + "path_summary": "N_SCRIPT_AUTOMATION_ORCH --[dispatched_by:E_ORCH_AUTOSPAWN] --> N_SCRIPT_ORCH_AUTOSPAWN" + }, + { + "node_id": "N_PLAN_TODO", + "score": 0.5363585828571429, + "reason": "topology=0.666, lexical=0.192, trace=0.750, anchor=N_SCRIPT_AUTOMATION_ORCH", + "path_nodes": [ + "N_SCRIPT_AUTOMATION_ORCH", + "N_PLAN_TODO" + ], + "path_edges": [ + "E_PLAN_TODO_ORCH" + ], + "path_summary": "N_SCRIPT_AUTOMATION_ORCH --[scanned_by:E_PLAN_TODO_ORCH] <-- N_PLAN_TODO" + } + ] + }, + "flat": { + "mode_used": "flat", + "baseline_mode": "keyword", + "hit": true, + "matched_nodes": [ + "N_SCRIPT_AUTOMATION_ORCH" + ], + "first_match_rank": 1, + "top_node": "N_SCRIPT_AUTOMATION_ORCH", + "trace": [ + { + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.662878787878788, + "reason": "keyword overlap=['builds', 'contracts', 'lane-aware', 'script', 'surfaces', 'task', 'todo']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + }, + { + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.2857142857142857, + "reason": "keyword overlap=['contracts', 'script', 'task']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + }, + { + "node_id": "N_TODO_TOPMEM", + "score": 0.19444444444444442, + "reason": "keyword overlap=['task', 'todo']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + } + ] + } + }, + { + "id": "WQ-002", + "question": "What script dispatches orchestrator task contracts through the gateway bridge?", + "expected_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN" + ], + "topology": { + "mode_used": "topology", + "hit": true, + "matched_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN" + ], + "first_match_rank": 1, + "top_node": "N_SCRIPT_ORCH_AUTOSPAWN", + "trace": [ + { + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.8826946917293234, + "reason": "topology=1.110, lexical=0.782, trace=0.250, anchor=N_SCRIPT_ORCH_AUTOSPAWN", + "path_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN" + ], + "path_edges": [], + "path_summary": "N_SCRIPT_ORCH_AUTOSPAWN" + }, + { + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.6469921924483937, + "reason": "topology=0.920, lexical=0.345, trace=0.250, anchor=N_SCRIPT_ORCH_AUTOSPAWN", + "path_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", + "N_SCRIPT_AUTOMATION_ORCH" + ], + "path_edges": [ + "E_ORCH_AUTOSPAWN" + ], + "path_summary": "N_SCRIPT_ORCH_AUTOSPAWN --[dispatched_by:E_ORCH_AUTOSPAWN] <-- N_SCRIPT_AUTOMATION_ORCH" + }, + { + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.4765358976240601, + "reason": "topology=0.770, lexical=0.176, trace=0.000, anchor=N_SCRIPT_ORCH_AUTOSPAWN", + "path_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN", + "N_ORCHESTRATOR_CONTRACT" + ], + "path_edges": [ + "E_CONTRACT_AUTOSPAWN" + ], + "path_summary": "N_SCRIPT_ORCH_AUTOSPAWN --[dispatched_via:E_CONTRACT_AUTOSPAWN] <-- N_ORCHESTRATOR_CONTRACT" + } + ] + }, + "flat": { + "mode_used": "flat", + "baseline_mode": "keyword", + "hit": true, + "matched_nodes": [ + "N_SCRIPT_ORCH_AUTOSPAWN" + ], + "first_match_rank": 1, + "top_node": "N_SCRIPT_ORCH_AUTOSPAWN", + "trace": [ + { + "node_id": "N_SCRIPT_ORCH_AUTOSPAWN", + "score": 0.7821428571428571, + "reason": "keyword overlap=['bridge', 'contracts', 'dispatches', 'gateway', 'orchestrator', 'script', 'task', 'the', 'through']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + }, + { + "node_id": "N_SCRIPT_AUTOMATION_ORCH", + "score": 0.3454545454545455, + "reason": "keyword overlap=['contracts', 'orchestrator', 'script', 'task']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + }, + { + "node_id": "N_ORCHESTRATOR_CONTRACT", + "score": 0.17631578947368423, + "reason": "keyword overlap=['orchestrator', 'task']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + } + ] + } + }, + { + "id": "WQ-003", + "question": "Which TODO surface currently tracks the execution steps for topological memory?", + "expected_nodes": [ + "N_PLAN_TODO" + ], + "topology": { + "mode_used": "topology", + "hit": true, + "matched_nodes": [ + "N_PLAN_TODO" + ], + "first_match_rank": 1, + "top_node": "N_PLAN_TODO", + "trace": [ + { + "node_id": "N_PLAN_TODO", + "score": 0.6435772727272726, + "reason": "topology=0.702, lexical=0.484, trace=0.750, anchor=N_PLAN_TODO", + "path_nodes": [ + "N_PLAN_TODO" + ], + "path_edges": [], + "path_summary": "N_PLAN_TODO" + }, + { + "node_id": "N_TODO_TOPMEM", + "score": 0.48287474144840403, + "reason": "topology=0.699, lexical=0.328, trace=0.000, anchor=N_PLAN_TODO", + "path_nodes": [ + "N_PLAN_TODO", + "N_TODO_TOPMEM" + ], + "path_edges": [ + "E_PLAN_TODO_SECTION" + ], + "path_summary": "N_PLAN_TODO --[contains_section:E_PLAN_TODO_SECTION] --> N_TODO_TOPMEM" + }, + { + "node_id": "N_PROVISIONAL_VALIDATION", + "score": 0.4387061219116883, + "reason": "topology=0.505, lexical=0.086, trace=0.900, anchor=N_TODO_TOPMEM", + "path_nodes": [ + "N_TODO_TOPMEM", + "N_PROVISIONAL_VALIDATION" + ], + "path_edges": [ + "E_TODO_VALIDATION" + ], + "path_summary": "N_TODO_TOPMEM --[summarized_by:E_TODO_VALIDATION] --> N_PROVISIONAL_VALIDATION" + } + ] + }, + "flat": { + "mode_used": "flat", + "baseline_mode": "keyword", + "hit": true, + "matched_nodes": [ + "N_PLAN_TODO" + ], + "first_match_rank": 1, + "top_node": "N_PLAN_TODO", + "trace": [ + { + "node_id": "N_PLAN_TODO", + "score": 0.48409090909090907, + "reason": "keyword overlap=['execution', 'memory', 'steps', 'surface', 'todo', 'topological']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + }, + { + "node_id": "N_TODO_TOPMEM", + "score": 0.3282828282828283, + "reason": "keyword overlap=['for', 'memory', 'todo', 'topological']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + }, + { + "node_id": "N_ARCHIVE_TOPMEM", + "score": 0.178030303030303, + "reason": "keyword overlap=['for', 'memory']", + "path_nodes": [], + "path_edges": [], + "path_summary": "" + } + ] + } + } + ], + "notes": [ + "This is a bounded workflow-style comparison, not a promotion argument.", + "Flat mode remains a live fallback path." + ] +} diff --git a/memory/research/topological-memory-v0/runtime_adoption_comparison_v0.md b/memory/research/topological-memory-v0/runtime_adoption_comparison_v0.md new file mode 100644 index 0000000..782dc35 --- /dev/null +++ b/memory/research/topological-memory-v0/runtime_adoption_comparison_v0.md @@ -0,0 +1,36 @@ +# Topological Memory Runtime Adoption v0 + +- Workflow consumer: `scripts/automation_orchestrator.py task preparation for continuity-lane tasks` +- Query count: **3** +- Topology hit-rate: **1.000** +- Flat hit-rate: **1.000** +- Topology-only hits: `[]` +- Flat-only hits: `[]` + +## Per-query traces + +### WQ-001 + +- Question: What script builds lane-aware task contracts from TODO surfaces? +- Expected nodes: ['N_SCRIPT_AUTOMATION_ORCH'] +- Topology: hit=True first_match_rank=1 matched_nodes=['N_SCRIPT_AUTOMATION_ORCH'] top_node=N_SCRIPT_AUTOMATION_ORCH mode=topology +- Flat: hit=True first_match_rank=1 matched_nodes=['N_SCRIPT_AUTOMATION_ORCH'] top_node=N_SCRIPT_AUTOMATION_ORCH mode=flat/keyword + +### WQ-002 + +- Question: What script dispatches orchestrator task contracts through the gateway bridge? +- Expected nodes: ['N_SCRIPT_ORCH_AUTOSPAWN'] +- Topology: hit=True first_match_rank=1 matched_nodes=['N_SCRIPT_ORCH_AUTOSPAWN'] top_node=N_SCRIPT_ORCH_AUTOSPAWN mode=topology +- Flat: hit=True first_match_rank=1 matched_nodes=['N_SCRIPT_ORCH_AUTOSPAWN'] top_node=N_SCRIPT_ORCH_AUTOSPAWN mode=flat/keyword + +### WQ-003 + +- Question: Which TODO surface currently tracks the execution steps for topological memory? +- Expected nodes: ['N_PLAN_TODO'] +- Topology: hit=True first_match_rank=1 matched_nodes=['N_PLAN_TODO'] top_node=N_PLAN_TODO mode=topology +- Flat: hit=True first_match_rank=1 matched_nodes=['N_PLAN_TODO'] top_node=N_PLAN_TODO mode=flat/keyword + +## Bounds + +- This is a bounded workflow-style comparison, not a promotion argument. +- Flat mode remains a live fallback path. diff --git a/plans/audit-2026-06-10-physical-theory-validity.md b/plans/audit-2026-06-10-physical-theory-validity.md new file mode 100644 index 0000000..12c9c55 --- /dev/null +++ b/plans/audit-2026-06-10-physical-theory-validity.md @@ -0,0 +1,174 @@ +# Physical Theory Validity Audit — 2026-06-10 + +**Type:** external-style adversarial audit (full-repo read, line-level evidence) +**Scope:** core mechanism (A-002/A-003), Kerr layer (A-004/A-005, T-015), complex entropy state (A-006, T-016), hyperstition (A-008, T-012), assumptions register, proof-path positioning, automation gates. +**Status:** findings logged; remediation backlog below is the actionable surface. +**Claim discipline:** every finding below is tiered and cites file:line evidence. Nothing here is a vibe. + +--- + +## 0) Headline + +- The governance stack (FOUNDATIONS contract + assumptions register + proof-path ladder) is **defensible now** and above-median for an independent research program. +- The hyperstition toy lane (T-012) is a **genuine internal Level-4 result** with correct discipline (null + ablation + robustness + pre-declared failure envelope). +- The repo's **only physics PASS (T-015) does not survive audit** and is contaminating downstream prose. +- The core-mechanism's validated artifact tests a **different PDE class** (parabolic/Péclet) than the stated mechanism (hyperbolic/Froude), and never tests a *temporally future* target — so the "future-like advantage" name-claim is currently at proof-path **Level 2**, not Level 4. +- Honest program position on the ladder: **Level 3 overall**, one true L4 surface (hyperstition), one unsound L4 claim (Kerr). + +--- + +## 1) Findings register (problems to fix) + +Severity: **S1** = contaminates ledger/policy surfaces now; **S2** = blocks the central claim from advancing; **S3** = correctness/hygiene debt. + +### AUD-001 (S1) — T-015 PASS is unsound; measured quantity is not proper time +- **Surface:** `docs/theory-implementation-matrix.md` row T-015; `research/kerr_asymmetry_validation.py`; `cosmic_comm/physics/geodesics.py` +- **Evidence:** + - Validation traces **null** geodesics (`kerr_asymmetry_validation.py:36–44` enforces `g^μν p_μ p_ν = 0`; `geodesics.py:4` "photon paths (null geodesics)"). Proper time along a null geodesic is identically zero. + - Reported "proper_time" is `len(ts) * self.dt` (`geodesics.py:98`) — elapsed **affine parameter**, gauge-dependent (E=1 normalization, step size 0.05, capture margin 1.05). + - Flat-space comparator (`kerr_asymmetry_validation.py:85–113`) produces only **negative** asymmetries (−0.01 … −1.36) vs all-positive Kerr values, so "best-fit flat match" is always the v=0.1 strawman and the relative residual is trivially ≈100%+. **The test cannot fail.** + - Criterion conflation: at a/M=0.1, asymmetry = 2.28% and absolute residual = 3.3% (`memory/research/kerr_asymmetry_2026-03/validation_results.json`), both below the stated 5% bar; PASS rides on the meaningless relative residual. + - The correct mundane comparator (Sagnac asymmetry in a rotating flat frame) was never run; the Kretschmann-invariant argument (`docs/math_foundations_zf.md:388–390`, also misspelled "Kretschner") shows curvature invariance, not channel-observable unmimickability. +- **Required fix:** demote T-015 to REVIEW immediately (evidence-payload update per matrix schema). Rebuild with (a) timelike orbits + actual ∫dτ, or an invariant null observable (distant-observer round-trip coordinate-time asymmetry); (b) Sagnac rotating-frame flat baseline; (c) the **spin-dependence curve** as the discriminating object, not a scalar. +- **Markers:** P1, E4, A2. **Matrix:** T-015. **Register:** A-004, A-005. + +### AUD-002 (S1) — T-015 contamination has propagated into canonical prose +- **Surface:** `docs/math_foundations_zf.md` §9 ("Empirical validation (2026-03-30) … confirms"), `plans/todo.md:181` ("proves it's not removable by boost"), matrix High-confidence cell. +- **Required fix:** annotate/retract those statements pending AUD-001 rebuild. This is the repo's first observed instance of the laundering failure mode it was designed to prevent — record it as a governance lesson (spine pressure event on the relevant concept). +- **Markers:** A2, O3. + +### AUD-003 (S2) — Mechanism mismatch: validated artifact is parabolic (Pe), stated mechanism is hyperbolic (Fr) +- **Surface:** `docs/01_foundations.md:77–88` (Fr < 1, finite `c_up = √(gh) − u`, finite delay τ_u > 0) vs `research/subcritical_information_demo.py:20–29` (Péclet-controlled advection-diffusion). +- **Evidence:** parabolic PDEs propagate influence at infinite speed (exponentially damped). The theory's signature "finite positive delay, no instantaneous channel" claim is **not realized** by the artifact cited as its first demonstration. The demo is correct mathematics demonstrating textbook diffusion legibility — it does not discriminate the stated wave mechanism. +- **Required fix:** implement the shallow-water / hyperbolic version where upstream influence genuinely travels at finite `c_up`, and verify the measured onset delay matches τ_u = (L−x_u)/c_up. +- **Markers:** O3, C1, I2, E5. **Matrix:** T-002. **Register:** A-002, A-003, new H-4 (below). + +### AUD-004 (S2) — "Future-like advantage" never tests a temporally future target +- **Surface:** `research/subcritical_information_demo.py` (target is B, the current/past boundary state); all "anticipatory" language in docs/00–02. +- **Evidence:** measured MI is spatial legibility `I(q(x_obs); B)`. No artifact maps "downstream in space" → "future in time." I2 as defined (`FOUNDATIONS.md:107–113`) requires ΔI over a baseline observation set; the demo's null is channel-off, not best-inference-without-method. +- **Required fix:** moving-observer experiment — observer advected toward the boundary; target = observer's own local state at t+τ; baselines = (i) matched observer with the upstream channel removed, (ii) history-only autoregressive forecaster. Pre-register lead-time-vs-regime predictions. +- **Markers:** I2, E1, E4, E5. **Matrix:** T-002. **Register:** A-002 + new H-1. + +### AUD-005 (S2) — §11 math error: arc-length integral conflated with complex contour integral +- **Surface:** `docs/math_foundations_zf.md:443–476` (§11). +- **Evidence:** τ_γ is defined as ∫ Z(s)‖dγ/ds‖ds (arc-length). Cauchy's theorem applies to ∮ f(z) dz, **not** ∮ f |dz|. With α,β ∈ [0,1] ≥ 0, Re ∮ Z |dz| ≥ 0 and is nonzero for any generic field regardless of singularities — so "ΔT ≠ 0 ⟺ enclosed defect" is false under the stated definition. The winding-number formula uses the (different, correct) dz integral. Also: a curve confined to [0,1]² can never wind around 0; winding around interior z₀ is z₀-dependent (modeling choice, not invariant). +- **Required fix:** either redefine τ_γ as ∮ Z dz (Cauchy applies; Re/Im order/disorder reading breaks) or keep arc-length and delete the Cauchy/singularity diagnostic. Update `complex_euler.py` docstrings to match. Then run the A-006 ablation: re-express one downstream result in plain (α,β) ∈ ℝ² and record what, if anything, is lost. +- **Markers:** O2, O3, C4. **Matrix:** T-016 (adjacent). **Register:** A-006, A-007. + +### AUD-006 (S2) — Kerr layer is structurally disconnected from the information-theoretic code +- **Surface:** `nfem_suite/` (no Kerr import anywhere); `nfem_suite/simulation/communication/vortex_channel.py:36` (asymmetry = hand-set scalar `backward_attenuation: float = 0.5`). +- **Evidence:** nothing downstream consumes T-015 outputs except prose. "Load-bearing" requires a downstream claim whose truth/measured value changes under Kerr-specific structure vs a generic asymmetric-delay channel. No such consumer exists. +- **Required fix:** either wire a Kerr-derived asymmetry profile into `VortexChannel` (replacing the hand-set scalar) and show a metric that distinguishes it from generic attenuation — or execute A-005's downscope now ("Kerr as illustrative only") and say so in `docs/02` and `math_foundations_zf.md` §9. +- **Markers:** O3, P1. **Register:** A-004, A-005. + +### AUD-007 (S2) — Narrative–boundary coupling exists in docs, not in code +- **Surface:** `docs/05_hyperstition_temporal_bridge_analysis.md:47–50` (`q(L,t) = B₀(t) + λN_t`) — never implemented. The toy model's "temporal asymmetry Δ" is a constant additive bias (`nfem_suite/intelligence/cognition/hyperstition.py:86–87`); nothing temporal occurs in the dynamics. +- **Required fix:** implement the q-coupling so narrative conditions a simulated medium's boundary; test whether the corridor structure (Level-4 packet) survives when narrative acts through the medium rather than directly through the action channel. This is the single cheapest move that connects the repo's two best surfaces. +- **Markers:** O3, C1, A2. **Matrix:** T-012 extension. **Register:** A-008, A-009. + +### AUD-008 (S1) — Hard-gate enforcement is an honor system +- **Surface:** `scripts/validate_foundations.py:86–96` (`_extract_explicit_violation_markers`): C1/I1/P1/P2 failures trigger only when the payload **self-declares** them. `scripts/self_improve.py:505` promotes policy tweaks on a frequency gate (min_count=3) plus contract check, with no evidence-quality re-derivation. +- **Required fix:** any matrix-row transition to PASS must require an adversarial verifier pass that (a) re-derives the claimed observable from the code and (b) audits comparator adequacy (see AUD-009). Artifact-existence is not evidence-adequacy. +- **Markers:** A2, A3, C4. **Matrix:** T-003, T-006, T-007. + +### AUD-009 (S2) — Recurring failure mode: weak comparator classes ("strawman baselines") +- **Evidence:** T-015 tested against a sign-mismatched boost family instead of Sagnac; subcritical demo tested against channel-off instead of a history-based forecaster; pre-registration in `subcritical_information_demo.py:34–43` lives in the same script that evaluates it (weak E1) and the predictions follow analytically from the model's own closed form (E5 novelty fails). +- **Required fix:** adopt a standing rule — no Level-4 status without naming and implementing the **strongest known causal-mundane mechanism** that produces the same observable, as the baseline. Proposed spine node: `SC-CONCEPT-0010: strongest-mundane-comparator`. +- **Markers:** E1, E4, E5, A2. + +### AUD-010 (S3) — Hidden load-bearing assumptions missing from the register +Add rows to `docs/assumptions_register.md`: +- **H-1:** "downstream in space" ↔ "future in time" mapping exists for some observer class (every "future-like" claim depends on it; untested). +- **H-2:** affine-parameter differences along null geodesics proxy proper-time/channel timing (assumed by T-015; false as stated). +- **H-3:** comparator classes in discriminating tests contain the strongest mundane alternative (violated twice). +- **H-4:** parabolic diffusion adequately stands in for the finite-`c_up` hyperbolic mechanism, including finite delay (currently false in the validated artifact). +- **H-5:** MI about a boundary implies decision-relevant anticipatory advantage (needs I2 baseline form). +- **H-6:** automation gate honesty — hard-gate enforcement assumes proposers declare their own violations. +Also consider: relabel **A-012** consequence from Downscope toward Collapse-for-the-science (program identity is effectively A-001 ∧ A-002 ∧ eventual A-012), and note that A-002's fallback ("ordinary forecasting/latency effects") currently describes the actual evidential state. + +### AUD-011 (S3) — Honest proof-path placement (correct the implicit ledger) +| Surface | Implied | Honest | Note | +|---|---|---|---| +| Subcritical core (T-002) | L4 | **L3** | artifact real; test confirms textbook physics, not the mechanism claim | +| "Future-like advantage" framing | — | **L2** | no artifact targets a future observable | +| Kerr (T-015) | L4 PASS | **L3** | discriminating test fails L4 entry conditions | +| ZF→ℚ chain (T-016) | L3 | **L3 (terminal)** | infrastructure; cannot climb, by design | +| Hyperstition toy (T-012) | L4 | **L4 (internal)** | genuine; packet correctly refuses L5 | +| Tempo Tracer capacity / T-013 / T-014 / T-017 | L2–L3 | **L2** | validation columns all say "to add" | +| Narrative–boundary coupling | L3-ish prose | **L2** | equation written, never implemented | + +### AUD-012 (S3) — Minor corrections +- "Kretschner" → **Kretschmann** (`docs/math_foundations_zf.md:388`). +- `plans/todo.md` resolution table rows #1, #2, #6 marked ✅ Resolved are contradicted by AUD-001/-002/-005; reopen with audit flags (do not rewrite history — annotate). + +--- + +## 2) Recommended first step (single move, if only one is taken) + +**Demote T-015 from PASS to REVIEW and rebuild the Kerr validation correctly** (AUD-001 + AUD-002): + +1. Update the T-015 matrix row: Decision → REVIEW, gap column → cite this audit, confidence → Low. Attach an evidence payload per the matrix schema (`matrix_id`, `markers`, `result_summary`, `rollback_status`). +2. Annotate `docs/math_foundations_zf.md` §9 and `plans/todo.md:181` with a retraction-pending note. +3. Rebuild: timelike geodesics + ∫dτ (or invariant round-trip observable), Sagnac flat-frame baseline, spin-curve criterion, error bars across step sizes. + +**Why this first, ahead of the more exciting moving-observer experiment (AUD-004):** a false PASS in the traceability ledger corrupts every downstream consumer of the ledger — including the automation loop, which trusts matrix status. Fixing the ledger is cheap (hours), unblocks honest prioritization, and demonstrates the governance system actually self-corrects, which is itself the program's most externally legible asset right now. The moving-observer experiment is the highest-leverage *new science* and should be move #2. + +--- + +## 3) Agentic development backlog (bounded task contracts) + +Ordered. Each contract is sized for one bounded lane pass with explicit disposition. + +### Contract 1 — Ledger repair (AUD-001/-002/-012) +- **Goal:** T-015 → REVIEW; retraction-pending annotations in `math_foundations_zf.md` §9 and `plans/todo.md`; fix "Kretschmann". +- **Constraints:** no deletion of historical evidence artifacts; annotate, don't rewrite. +- **Done when:** matrix row updated with evidence payload; `python3 scripts/validate_foundations.py --payload-file ` passes; spine pressure event recorded against the Kerr-adjacent concept. +- **Disposition:** DOC_PROMOTE. + +### Contract 2 — Kerr validation rebuild (AUD-001) +- **Goal:** new `research/kerr_asymmetry_validation_v2.py` per §2 step 3. +- **Done when:** spin-curve result with Sagnac baseline, error bars, and a criterion that **can fail**; T-015 re-decided on the new evidence (PASS or FAIL both acceptable outcomes). +- **Failure condition (pre-registered):** if the Sagnac-matched baseline reproduces the spin curve within error, A-005 downscope executes immediately (Contract 6 short-circuits to the downscope branch). +- **Disposition:** TODO_PROMOTE → matrix row update. + +### Contract 3 — Hyperbolic subcritical mechanism (AUD-003) +- **Goal:** shallow-water / linearized hyperbolic solver; verify upstream onset delay ≈ (L−x_u)/c_up; reproduce the MI-vs-regime transition in the wave regime. +- **Done when:** results JSON + plot in `memory/research/`, matrix T-002 row updated with the artifact, finite-delay claim either validated or corrected in `docs/01`. +- **Disposition:** DOC_PROMOTE. + +### Contract 4 — Moving-observer anticipatory ΔI (AUD-004) — *the core-thesis test* +- **Goal:** observer advected toward boundary; target = its own future local state; baselines = channel-removed twin + history-only forecaster; ΔI per marker I2 with CIs. +- **Pre-registration:** lock predictions in a separate dated file **before** running (fixes the E1 weakness); declare pass/fail thresholds. +- **Done when:** one completed run either showing ΔI > 0 with lead time scaling as predicted, or a clean negative. Both are program progress. +- **Disposition:** ESCALATE to human review (this is the name-claim). + +### Contract 5 — §11 math repair + A-006 ablation (AUD-005) +- **Goal:** fix the arc-length/contour conflation; choose one coherent definition; rerun the (α,β) ∈ ℝ² ablation on one downstream result. +- **Done when:** §11 internally consistent; ablation note states what the complex embedding buys (possibly: nothing — that is an acceptable, recordable answer per A-006's fallback). +- **Disposition:** DOC_PROMOTE. + +### Contract 6 — Kerr↔channel wiring or downscope (AUD-006) +- **Goal:** branch on Contract 2 outcome. PASS branch: derive `backward_attenuation` profile from Kerr geometry and show a metric distinguishing it from a generic scalar. FAIL branch: execute A-005 downscope across `docs/02`, `math_foundations_zf.md` §9, register. +- **Disposition:** POLICY_PROMOTE (register status change) — human review required. + +### Contract 7 — Narrative-boundary coupling implementation (AUD-007) +- **Goal:** implement `q(L,t) = B₀ + λN_t`; test corridor-structure survival through the medium. +- **Done when:** comparison artifact vs the existing Level-4 packet arms; failure envelope updated. +- **Disposition:** TODO_PROMOTE. + +### Contract 8 — Verifier-lane hardening (AUD-008/-009) +- **Goal:** add to `scripts/validate_foundations.py` (or a sibling) a PASS-transition check requiring: comparator-class declaration, strongest-mundane-comparator statement, and independent re-derivation note. Add `spine/concepts/SC-CONCEPT-0010-strongest-mundane-comparator.yaml`. +- **Done when:** a synthetic payload claiming PASS without comparator declaration is rejected in tests. +- **Disposition:** POLICY_PROMOTE — human review required. + +### Contract 9 — Register update (AUD-010) +- **Goal:** add H-1…H-6 rows; reconsider A-012 consequence class; cross-link to this audit. +- **Disposition:** DOC_PROMOTE. + +--- + +## 4) What this audit explicitly does NOT claim + +- It does not claim the Kerr qualitative physics is wrong — prograde/retrograde photon dynamics in Kerr genuinely differ (textbook). It claims the *validation as run* cannot support the load-bearing conclusion. +- It does not claim hyperstition is pseudoscience — the policy-layer framing is coherent and has mainstream ancestry (Merton, performativity, mean-field games). It claims the physics-flavored coupling is currently unimplemented. +- It does not claim the program is hollow — the governance machinery and the hyperstition lane are real assets. It claims the gap between the matrix's recorded state and the audited state is concentrated almost entirely in one PASS row, and that fixing the ledger is therefore unusually cheap relative to the credibility it buys. diff --git a/plans/todo.md b/plans/todo.md index 6154849..1043f14 100644 --- a/plans/todo.md +++ b/plans/todo.md @@ -163,12 +163,12 @@ The project gestures at this but doesn't engage with any of these works or expla | Priority | Issue | Effort | Impact | Status | |----------|-------|--------|--------|--------| -| 1 | Complex entropy state needs physical derivation | High | Transforms the entire formal core | ✅ **Resolved** — `math_foundations_zf.md` §10 | -| 2 | Show GR does actual theoretical work (not just aesthetics) | High | Justifies the project's distinctive premise | ✅ **Resolved** — `math_foundations_zf.md` §9 + empirical validation (`scripts/kerr_asymmetry_validation.py`) | +| 1 | Complex entropy state needs physical derivation | High | Transforms the entire formal core | ⚠️ **Reopened by 2026-06-10 audit** (AUD-005: §11 conflates arc-length and contour integrals; Cauchy diagnostic invalid as defined) — was ✅ via `math_foundations_zf.md` §10 | +| 2 | Show GR does actual theoretical work (not just aesthetics) | High | Justifies the project's distinctive premise | ⚠️ **Reopened by 2026-06-10 audit** (AUD-001/-006: T-015 measures affine parameter on null geodesics, not proper time; flat baseline is a sign-mismatched strawman; no downstream consumer) — was ✅ via `math_foundations_zf.md` §9 | | 3 | Specify $\mathcal{G}$ for hyperstition dynamics, analyze fixed points | Medium | First genuine novel theoretical result | 🟡 **Partial** — toy model + fixed-point classifier in `nfem_suite/intelligence/cognition/hyperstition.py`; broader literature integration still open | | 4 | Anchor epistemic retro-influence in existing game theory | Low | Instant credibility + clarity on what's new | 🟡 **Partial** — research cycle 2026-03-30 established parallels with signaling games, Bayesian persuasion, forward induction; formal mapping document pending | | 5 | Three-layer time composition law | Medium | Makes the most original idea rigorous | ✅ **Resolved** — `math_foundations_zf.md` §12 | -| 6 | Rename/rethink "tachyonic loop" claims | Low | Removes internal contradiction | ✅ **Resolved** — `math_foundations_zf.md` §11; `tachyonic_loop.py` + `complex_euler.py` updated | +| 6 | Rename/rethink "tachyonic loop" claims | Low | Removes internal contradiction | ⚠️ **Partially reopened by 2026-06-10 audit** (rename was correct; AUD-005 shows the replacement Cauchy/winding bridge is itself broken under the stated arc-length definition) — was ✅ via `math_foundations_zf.md` §11 | | 7 | Specify observer read-write $\Phi$ for at least one domain | Medium | Moves from placeholder to model | 🟡 **Partial** — §12 gives δ-function structural form; concrete fluid-domain spec remains | | 8 | Engage entropy-causality literature properly | Low-Medium | Avoids reinventing the wheel | ⬜ Open | @@ -179,6 +179,7 @@ The project gestures at this but doesn't engage with any of these works or expla **Items resolved:** - **#1 (Complex entropy state):** Z = α + iβ derived as canonical embedding of ℝ² into the unique algebraic closure ℂ (§5, §10). Pythagorean norm and polar decomposition are forced, not chosen. - **#2 (GR does theoretical work):** §9 shows Kerr frame-dragging creates intrinsic (non-coordinate) channel asymmetry via g_{tφ} ≠ 0. Ergosphere topology ≠ flat-space latency; Kretschner scalar invariant proves it's not removable by boost. Empirical validation (2026‑03‑30) confirms proper‑time asymmetries 2.3‑22.2% across spins a/M ∈ [0.1,0.9], with >5% residuals vs best‑fit flat‑space models. + > ⚠️ **Retraction pending (2026-06-10 audit, AUD-001/-002):** the "proper-time asymmetries" above are affine-parameter step counts along **null** geodesics (proper time ≡ 0 on null paths; `cosmic_comm/physics/geodesics.py:98`), the flat-space comparator produces only negative asymmetries so the residual test cannot fail, and at a/M=0.1 the absolute residual (3.3%) is below the stated 5% bar. Do not cite this paragraph as evidence until T-015 is rebuilt. See `plans/audit-2026-06-10-physical-theory-validity.md`. - **#5 (Three-layer time composition):** §12 defines explicit coupling: geometric → proper (geodesic integration), proper → informational (mutual-information modulation), informational → geometric (δ-function read-write feedback). Two-agent composition law given. - **#6 (Tachyonic loop renamed):** §11 gives correct interpretation as entropic vortex charge (topological defect detection via Cauchy theorem). Winding number ∈ ℤ. Code files `complex_euler.py` and `tachyonic_loop.py` updated with corrected docstrings; class name retained for backward compatibility. @@ -207,6 +208,27 @@ Would you like to dive deeper into any of these areas, or discuss how to priorit --- +## 10. Audit Findings (2026-06-10): Problems to Fix + +Full audit with line-level evidence, task contracts, and dispositions: **[`plans/audit-2026-06-10-physical-theory-validity.md`](audit-2026-06-10-physical-theory-validity.md)**. Condensed problem list (severity: S1 = contaminates ledger now, S2 = blocks central claim, S3 = hygiene): + +- [ ] **AUD-001 (S1):** T-015 PASS is unsound — "proper time" is gauge-dependent affine parameter on null geodesics; flat baseline is a sign-mismatched strawman; criterion conflates absolute/relative residual. **Demote to REVIEW, rebuild with timelike ∫dτ + Sagnac baseline + spin-curve criterion.** +- [ ] **AUD-002 (S1):** T-015 contamination propagated into `math_foundations_zf.md` §9 and this file (line ~181). Annotate/retract pending rebuild. *(Annotations added 2026-06-10; matrix row update still open.)* +- [ ] **AUD-003 (S2):** Mechanism mismatch — docs state hyperbolic Fr<1 wave mechanism with finite delay; the validated demo is parabolic Pe-controlled diffusion (infinite signal speed). Implement the shallow-water/hyperbolic version and verify τ_u = (L−x_u)/c_up. +- [ ] **AUD-004 (S2):** No artifact tests a *temporally future* target. Build the moving-observer ΔI experiment (target = observer's own future input; baselines = channel-removed twin + history-only forecaster; pre-register externally to the run script). +- [ ] **AUD-005 (S2):** `math_foundations_zf.md` §11 conflates ∫Z|dz| (arc-length, as defined) with ∮f(z)dz (contour, required by Cauchy) — the "ΔT≠0 ⟺ enclosed defect" diagnostic is false as stated. Fix the definition, then run the A-006 real-vector ablation. +- [ ] **AUD-006 (S2):** Kerr layer has zero downstream consumers (`VortexChannel` uses hand-set `backward_attenuation=0.5`). Wire a Kerr-derived asymmetry profile in, or execute the A-005 downscope. +- [ ] **AUD-007 (S2):** Narrative–boundary coupling `q(L,t)=B₀+λN_t` (docs/05 §3) is unimplemented; toy model's "temporal asymmetry" is a constant bias. Implement the coupling; test corridor-structure survival. +- [ ] **AUD-008 (S1):** Hard-gate enforcement is honor-system (`validate_foundations.py` only catches self-declared violations; policy promotion is a frequency gate). Require adversarial re-derivation + comparator audit for any PASS transition. +- [ ] **AUD-009 (S2):** Recurring strawman-baseline failure mode. Adopt the strongest-mundane-comparator rule; add `spine/concepts/SC-CONCEPT-0010-strongest-mundane-comparator.yaml`. +- [ ] **AUD-010 (S3):** Add hidden assumptions H-1…H-6 to `docs/assumptions_register.md`; reconsider A-012 consequence class. +- [ ] **AUD-011 (S3):** Correct proof-path placements (subcritical core L3 not L4; Kerr L3; "future-like" framing L2; hyperstition genuine L4-internal). +- [ ] **AUD-012 (S3):** "Kretschner" → Kretschmann (`math_foundations_zf.md:388`); resolution-table rows #1/#2/#6 reopened above. + +**Recommended execution order:** AUD-001/-002 (ledger repair, hours) → AUD-004 (core-thesis test, the highest-leverage new science) → AUD-003 → AUD-005 → AUD-008/-009 → rest. Rationale in the audit doc §2: a false PASS in the traceability ledger corrupts every consumer of the ledger, including the automation loop — fix the ledger before generating new results through it. + +--- + ## Agency + Temporal Communication Buildout (New Direction) - [x] Define agency observables in-code (`intervention_gain`, `counterfactual_control_score`, `predictive_horizon`) @@ -386,6 +408,8 @@ Source: `research_backlog.md` Phase 3.1 entropy-causality item (still open). ### F. Second Kerr-specific observable (extending T-015) +> ⚠️ **Blocked by 2026-06-10 audit (AUD-001):** do not extend T-015 until the base validation is rebuilt — the current PASS rests on a mislabeled observable and a baseline that cannot fail. Rebuild first (audit doc, Contract 2), then this section unblocks. + Source: matrix row T-015 (PASS), `docs/02_tempo_tracer_protocol.md` §2.2, `cosmic_comm/`. - [ ] Draft `docs/notes/kerr_second_observable_v0.md` proposing one additional geometry-specific prediction distinct from proper-time asymmetry. Candidates: ISCO-adjacent information attenuation ratio; ergosphere-edge frame-drag-induced channel anisotropy; prograde/retrograde photon-ring brightness asymmetry. State the failure mode that would separate it from a noisy flat-channel baseline. diff --git a/schemas/tempo_point_v0.schema.json b/schemas/tempo_point_v0.schema.json new file mode 100644 index 0000000..969454b --- /dev/null +++ b/schemas/tempo_point_v0.schema.json @@ -0,0 +1,145 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/yanmo42/sandy-chaos/schemas/tempo_point_v0.schema.json", + "title": "TempoPoint", + "description": "A windowed aggregate emitted by the Sandy Chaos telemetry edge. See docs/23_self_referential_tempo_edge.md and docs/24_tempo_edge_redaction_policy.md. Raw input events MUST NOT appear here.", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "ts", + "session_id", + "source", + "window_ms", + "point_kind", + "aggregates", + "redaction_version" + ], + "properties": { + "schema_version": { + "const": "tempo_point/v0", + "description": "Frozen wire format tag. Bump to v1 if any field is added or semantics change." + }, + "ts": { + "type": "string", + "format": "date-time", + "description": "ISO-8601 timestamp marking the END of the aggregation window." + }, + "session_id": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{8,64}$", + "description": "Opaque per-session token (e.g. UUIDv4). MUST NOT encode operator identity." + }, + "source": { + "type": "string", + "enum": [ + "game:sc2", + "game:arc-raiders", + "game:fortnite", + "game:league", + "game:cyberpunk", + "game:other", + "input:keyboard", + "input:mouse", + "mixed" + ], + "description": "Taxonomy tag for the upstream surface. 'mixed' is only valid when aggregates combine input and game-state." + }, + "window_ms": { + "type": "integer", + "minimum": 1, + "maximum": 600000, + "description": "Length of the aggregation window in milliseconds. Cap is 10 minutes to prevent unbounded windows from being smuggled in." + }, + "point_kind": { + "type": "string", + "enum": [ + "apm_window", + "interval_cluster", + "decision_flag", + "game_state_delta" + ], + "description": "Discriminator for the aggregates payload." + }, + "aggregates": { + "type": "object", + "additionalProperties": false, + "description": "Numeric and categorical aggregates only. No verbatim input strings.", + "properties": { + "event_count": { "type": "integer", "minimum": 0 }, + "key_count": { "type": "integer", "minimum": 0 }, + "click_count": { "type": "integer", "minimum": 0 }, + "decision_count": { "type": "integer", "minimum": 0 }, + "apm": { "type": "number", "minimum": 0 }, + "click_rate_hz": { "type": "number", "minimum": 0 }, + "decision_rate_hz": { "type": "number", "minimum": 0 }, + "inter_event_interval_ms_mean": { "type": "number", "minimum": 0 }, + "inter_event_interval_ms_p50": { "type": "number", "minimum": 0 }, + "inter_event_interval_ms_p95": { "type": "number", "minimum": 0 }, + "inter_event_interval_ms_max": { "type": "number", "minimum": 0 }, + "modal_key_class": { + "type": "string", + "enum": ["movement", "hotkey", "modifier", "mouse_primary", "mouse_secondary", "mixed", "none"], + "description": "Categorical class only. Never the literal key." + }, + "modal_action_class": { + "type": "string", + "enum": ["macro", "micro", "control_group", "camera", "build_order", "engagement", "retreat", "idle", "mixed", "none"], + "description": "Categorical class only. Never a free-text label." + }, + "game_state": { + "type": "object", + "additionalProperties": false, + "description": "Numeric/categorical game-state structural facts. No chat, no opponent handles, no free text.", + "properties": { + "unit_count_delta": { "type": "integer" }, + "supply_delta": { "type": "integer" }, + "resource_delta_primary": { "type": "integer" }, + "resource_delta_secondary": { "type": "integer" }, + "score_delta": { "type": "integer" }, + "phase": { + "type": "string", + "enum": ["opening", "early", "mid", "late", "endgame", "unknown"] + }, + "engagement_flag": { "type": "boolean" } + } + }, + "decision_flag_kind": { + "type": "string", + "enum": ["build_order_change", "engagement_initiated", "engagement_disengaged", "expansion", "tech_switch", "scout_dispatch", "other"], + "description": "Structural marker for what category of decision the window contained. Never describes the decision's content beyond category." + } + } + }, + "redaction_version": { + "type": "string", + "const": "redaction-policy-v0", + "description": "Active redaction policy version at emit time. See docs/24." + }, + "notes": { + "type": "string", + "maxLength": 240, + "description": "Optional short structural note. MUST NOT include operator-typed text, chat, key sequences, or identifiers. Reviewers should reject any note that contains verbatim user input." + } + }, + "not": { + "anyOf": [ + { "required": ["raw_keys"] }, + { "required": ["raw_events"] }, + { "required": ["raw_input"] }, + { "required": ["key_sequence"] }, + { "required": ["key_events"] }, + { "required": ["keystroke_log"] }, + { "required": ["text"] }, + { "required": ["chat"] }, + { "required": ["message_log"] }, + { "required": ["transcript"] }, + { "required": ["username"] }, + { "required": ["operator_name"] }, + { "required": ["account_id"] }, + { "required": ["handle"] }, + { "required": ["ip_address"] }, + { "required": ["hostname"] } + ] + } +} diff --git a/scripts/sc2_keylog_to_tempo_points.py b/scripts/sc2_keylog_to_tempo_points.py new file mode 100644 index 0000000..2b43811 --- /dev/null +++ b/scripts/sc2_keylog_to_tempo_points.py @@ -0,0 +1,416 @@ +"""Convert a StarCraft II keylog CSV into tempo_point_v0 JSONL. + +Reads a keylog CSV captured by an AutoHotkey logger and emits windowed +``tempo_point/v0`` records (see schemas/tempo_point_v0.schema.json). Input keys +are classified into categorical classes and counted only -- the verbatim key +column is NEVER written to output, per redaction-policy-v0. + +Usage:: + + python scripts/sc2_keylog_to_tempo_points.py \ + [--window-ms 10000] [--out-dir memory/research/tempo] + +Dependencies: stdlib + jsonschema (for the validation pass). +""" + +from __future__ import annotations + +import argparse +import csv +import hashlib +import json +import sys +from datetime import datetime, timezone +from pathlib import Path + +SCHEMA_VERSION = "tempo_point/v0" +REDACTION_VERSION = "redaction-policy-v0" +SOURCE = "game:sc2" + +SCHEMA_PATH = Path(__file__).resolve().parent.parent / "schemas" / "tempo_point_v0.schema.json" + +# --- key vocabularies (used only to classify; keys are never echoed) --------- + +MODIFIER_KEYS = { + "Shift", "Ctrl", "Alt", + "LShift", "RShift", "LCtrl", "RCtrl", "LAlt", "RAlt", + "Control", "Menu", +} +MOVEMENT_KEYS = {"Up", "Down", "Left", "Right", "WheelUp", "WheelDown"} +NUMBER_KEYS = {str(d) for d in range(0, 10)} +MOUSE_PRIMARY_KEYS = {"LButton", "MButton"} +MOUSE_SECONDARY_KEYS = {"RButton"} + + +def classify_key(key: str) -> str | None: + """Map a raw key name to a categorical class. Never returns the key.""" + if not key: + return None + if key in MOUSE_PRIMARY_KEYS: + return "mouse_primary" + if key in MOUSE_SECONDARY_KEYS: + return "mouse_secondary" + if key in MODIFIER_KEYS: + return "modifier" + if key in MOVEMENT_KEYS: + return "movement" + if key in NUMBER_KEYS: + return "hotkey" + if len(key) >= 2 and key[0] in {"F", "f"} and key[1:].isdigit(): + return "hotkey" # F1-F12 camera/function keys + if len(key) == 1 and key.isalpha(): + return "hotkey" # ability / build hotkeys + return None + + +def _is_letter(key: str) -> bool: + return len(key) == 1 and key.isalpha() + + +def _is_function_key(key: str) -> bool: + return len(key) >= 2 and key[0] in {"F", "f"} and key[1:].isdigit() + + +def session_id_for(path: Path) -> str: + """Derive an opaque 16-char alphanumeric session id from the filename.""" + digest = hashlib.sha256(path.name.encode("utf-8")).hexdigest() + return digest[:16] + + +# --- parsing ----------------------------------------------------------------- + +def parse_rows(csv_path: Path) -> list[dict]: + """Parse the keylog CSV into normalized event dicts, sorted by time.""" + events: list[dict] = [] + with csv_path.open(newline="", encoding="utf-8") as fh: + reader = csv.DictReader(fh) + for row in reader: + try: + unix_ms = int(row["unix_ms"]) + except (KeyError, TypeError, ValueError): + continue + key = (row.get("key") or "").strip() + events.append({ + "unix_ms": unix_ms, + "key": key, + "shift": (row.get("shift") or "0").strip() == "1", + "ctrl": (row.get("ctrl") or "0").strip() == "1", + "alt": (row.get("alt") or "0").strip() == "1", + "key_class": classify_key(key), + "is_letter": _is_letter(key), + "is_function": _is_function_key(key), + "is_number": key in NUMBER_KEYS, + }) + events.sort(key=lambda e: e["unix_ms"]) + return events + + +# --- aggregation ------------------------------------------------------------- + +def _percentile(sorted_vals: list[float], pct: float) -> float: + if not sorted_vals: + return 0.0 + if len(sorted_vals) == 1: + return float(sorted_vals[0]) + rank = pct / 100.0 * (len(sorted_vals) - 1) + lo = int(rank) + hi = min(lo + 1, len(sorted_vals) - 1) + frac = rank - lo + return float(sorted_vals[lo] + (sorted_vals[hi] - sorted_vals[lo]) * frac) + + +def modal_key_class(events: list[dict]) -> str: + counts: dict[str, int] = {} + for e in events: + cls = e["key_class"] + if cls is not None: + counts[cls] = counts.get(cls, 0) + 1 + if not counts: + return "none" + total = sum(counts.values()) + top_cls, top_n = max(counts.items(), key=lambda kv: kv[1]) + if len(counts) == 1: + return top_cls + if top_n / total >= 0.60: + return top_cls + return "mixed" + + +def compute_metrics(events: list[dict], window_ms: int) -> dict: + n = len(events) + window_s = window_ms / 1000.0 + key_events = [e for e in events if e["key_class"] not in ("mouse_primary", "mouse_secondary")] + click_events = [e for e in events if e["key_class"] in ("mouse_primary", "mouse_secondary")] + number_events = [e for e in events if e["is_number"]] + movement_events = [e for e in events if e["key_class"] == "movement"] + modifier_events = [e for e in events if e["key_class"] == "modifier"] + hotkey_events = [e for e in events if e["key_class"] == "hotkey"] + function_events = [e for e in events if e["is_function"]] + + key_count = len(key_events) + click_count = len(click_events) + + apm = n / window_s * 60.0 if window_s > 0 else 0.0 + click_rate_hz = click_count / window_s if window_s > 0 else 0.0 + + non_mouse_fraction = key_count / n if n else 0.0 + number_fraction = len(number_events) / n if n else 0.0 + camera_fraction = (len(movement_events) + len(function_events)) / n if n else 0.0 + + intervals = [] + ms = sorted(e["unix_ms"] for e in events) + for a, b in zip(ms, ms[1:]): + intervals.append(float(b - a)) + intervals_sorted = sorted(intervals) + + return { + "event_count": n, + "key_count": key_count, + "click_count": click_count, + "number_count": len(number_events), + "movement_count": len(movement_events), + "modifier_count": len(modifier_events), + "hotkey_count": len(hotkey_events), + "function_count": len(function_events), + "apm": apm, + "click_rate_hz": click_rate_hz, + "non_mouse_fraction": non_mouse_fraction, + "number_fraction": number_fraction, + "camera_fraction": camera_fraction, + "intervals": intervals, + "intervals_sorted": intervals_sorted, + "modal_key_class": modal_key_class(events), + } + + +def classify_action(m: dict) -> str: + """Classify a window into a modal action class from its metrics.""" + if m["event_count"] == 0: + return "none" + if m["apm"] < 20: + return "idle" + if m["number_fraction"] > 0.30: + return "control_group" + if m["apm"] > 200 and m["click_rate_hz"] > 3: + return "engagement" + if m["click_rate_hz"] > 3 and m["movement_count"] > 0: + return "micro" + if m["non_mouse_fraction"] > 0.60 and m["click_rate_hz"] < 1.0: + hk_mod = m["hotkey_count"] + m["modifier_count"] + if m["apm"] <= 120 and hk_mod / m["event_count"] > 0.5: + return "build_order" + return "macro" + if m["camera_fraction"] > 0.5 and m["click_rate_hz"] < 1.0: + return "camera" + return "mixed" + + +def detect_decision(events: list[dict], m: dict, rolling_avg_apm: float) -> tuple[bool, str, int]: + """Detect whether a window contains a decision event. + + Returns (is_decision, decision_flag_kind, decision_count). + """ + count = 0 + kinds: list[str] = [] + + # APM spike vs. rolling session average. + apm_spike = rolling_avg_apm > 0 and m["apm"] > 1.5 * rolling_avg_apm + if apm_spike: + count += 1 + kinds.append("engagement_initiated" if m["click_rate_hz"] > 3 else "other") + + # Build/tech hotkey burst: modifier held + letter key. + mod_letter = sum(1 for e in events if e["is_letter"] and (e["shift"] or e["ctrl"] or e["alt"])) + if mod_letter >= 2: + count += 1 + kinds.append("build_order_change") + + # Control-group reassignment: Ctrl + number key. + ctrl_num = sum(1 for e in events if e["is_number"] and e["ctrl"]) + if ctrl_num >= 1: + count += 1 + kinds.append("other") + + if count == 0: + return False, "", 0 + + # Priority for the reported kind. + priority = [ + "engagement_initiated", + "build_order_change", + "tech_switch", + "expansion", + "scout_dispatch", + "engagement_disengaged", + "other", + ] + kind = next((k for k in priority if k in kinds), "other") + return True, kind, count + + +# --- record assembly --------------------------------------------------------- + +def _ts_iso(unix_ms: int) -> str: + return datetime.fromtimestamp(unix_ms / 1000.0, tz=timezone.utc).isoformat() + + +def _interval_aggs(m: dict) -> dict: + intervals = m["intervals"] + if not intervals: + return {} + s = m["intervals_sorted"] + return { + "inter_event_interval_ms_mean": round(sum(intervals) / len(intervals), 3), + "inter_event_interval_ms_p50": round(_percentile(s, 50), 3), + "inter_event_interval_ms_p95": round(_percentile(s, 95), 3), + "inter_event_interval_ms_max": round(max(intervals), 3), + } + + +def build_records(events: list[dict], session_id: str, window_ms: int) -> tuple[list[dict], int]: + """Build tempo_point records over tumbling windows. Returns (records, n_windows).""" + records: list[dict] = [] + if not events: + return records, 0 + + start = events[0]["unix_ms"] + windows: dict[int, list[dict]] = {} + for e in events: + widx = (e["unix_ms"] - start) // window_ms + windows.setdefault(widx, []).append(e) + + window_s = window_ms / 1000.0 + rolling_sum = 0.0 + rolling_n = 0 + n_windows = 0 + + for widx in sorted(windows): + win = windows[widx] + n_windows += 1 + window_end_ms = start + (widx + 1) * window_ms + ts = _ts_iso(window_end_ms) + m = compute_metrics(win, window_ms) + + rolling_avg = rolling_sum / rolling_n if rolling_n else 0.0 + + # apm_window point (always emitted for non-empty windows). + aggregates = { + "event_count": m["event_count"], + "key_count": m["key_count"], + "click_count": m["click_count"], + "apm": round(m["apm"], 3), + "click_rate_hz": round(m["click_rate_hz"], 3), + "modal_key_class": m["modal_key_class"], + "modal_action_class": classify_action(m), + } + aggregates.update(_interval_aggs(m)) + records.append({ + "schema_version": SCHEMA_VERSION, + "ts": ts, + "session_id": session_id, + "source": SOURCE, + "window_ms": window_ms, + "point_kind": "apm_window", + "aggregates": aggregates, + "redaction_version": REDACTION_VERSION, + }) + + # decision_flag point (only when a decision is detected). + is_decision, kind, decision_count = detect_decision(win, m, rolling_avg) + if is_decision: + decision_rate = decision_count / window_s if window_s > 0 else 0.0 + records.append({ + "schema_version": SCHEMA_VERSION, + "ts": ts, + "session_id": session_id, + "source": SOURCE, + "window_ms": window_ms, + "point_kind": "decision_flag", + "aggregates": { + "event_count": m["event_count"], + "decision_count": decision_count, + "decision_rate_hz": round(decision_rate, 3), + "apm": round(m["apm"], 3), + "modal_key_class": m["modal_key_class"], + "modal_action_class": classify_action(m), + "decision_flag_kind": kind, + }, + "redaction_version": REDACTION_VERSION, + }) + + # Update rolling average AFTER using it for spike detection. + rolling_sum += m["apm"] + rolling_n += 1 + + return records, n_windows + + +# --- validation -------------------------------------------------------------- + +def validate_records(records: list[dict]) -> list[str]: + """Validate every record against the schema. Returns a list of error strings.""" + try: + import jsonschema + except ImportError: + return ["__no_jsonschema__"] + + schema = json.loads(SCHEMA_PATH.read_text(encoding="utf-8")) + validator = jsonschema.Draft202012Validator(schema) + errors: list[str] = [] + for i, rec in enumerate(records): + for err in validator.iter_errors(rec): + errors.append(f"record {i} ({rec.get('point_kind')}): {err.message}") + return errors + + +# --- main -------------------------------------------------------------------- + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description="Convert SC2 keylog CSV to tempo_point_v0 JSONL.") + parser.add_argument("csv_path", type=Path, help="Path to the keylog CSV.") + parser.add_argument("--window-ms", type=int, default=10000, help="Tumbling window size in ms (default 10000).") + parser.add_argument("--out-dir", type=Path, default=Path("memory/research/tempo"), + help="Base output directory (default memory/research/tempo).") + args = parser.parse_args(argv) + + if not args.csv_path.is_file(): + print(f"error: input not found: {args.csv_path}", file=sys.stderr) + return 2 + if args.window_ms < 1 or args.window_ms > 600000: + print(f"error: --window-ms must be in [1, 600000], got {args.window_ms}", file=sys.stderr) + return 2 + + events = parse_rows(args.csv_path) + session_id = session_id_for(args.csv_path) + records, n_windows = build_records(events, session_id, args.window_ms) + + out_dir = args.out_dir / session_id + out_dir.mkdir(parents=True, exist_ok=True) + out_path = out_dir / f"{args.csv_path.stem}.jsonl" + with out_path.open("w", encoding="utf-8") as fh: + for rec in records: + fh.write(json.dumps(rec, separators=(",", ":")) + "\n") + + errors = validate_records(records) + no_validator = errors == ["__no_jsonschema__"] + + print(f"input: {args.csv_path}") + print(f"session_id: {session_id}") + print(f"events parsed: {len(events)}") + print(f"windows: {n_windows}") + print(f"tempo points: {len(records)}") + print(f"output: {out_path}") + if no_validator: + print("validation: SKIPPED (jsonschema not installed)") + return 0 + if errors: + print(f"validation: FAILED ({len(errors)} error(s))") + for e in errors[:20]: + print(f" - {e}") + return 1 + print(f"validation: OK ({len(records)} records valid)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/state/ygg/active-work.json b/state/ygg/active-work.json index bd76efe..f3420dd 100644 --- a/state/ygg/active-work.json +++ b/state/ygg/active-work.json @@ -1,32 +1,51 @@ { - "timestamp": "2026-05-02T18:05:01.086365+00:00", - "active_frontier": "SC-CONCEPT-0004", - "selection_note": "Synced machine-readable frontier state with 2026-04-27 frontier note after restoring frozen topological-memory v0 artifacts.", + "timestamp": "2026-06-08T23:30:00.000000+00:00", + "active_frontier": "SC-CONCEPT-0008", + "selection_note": "2026-06-08: Extended graph_v0.json to v0.1 — added 3 missing workflow nodes (N_SCRIPT_AUTOMATION_ORCH, N_SCRIPT_ORCH_AUTOSPAWN, N_PLAN_TODO), 5 edges, 4 traces. Workflow adoption comparison now hits 3/3 (was 0/3). Frozen 30-query benchmark maintained at hit@3=1.000 for both keyword and topology; topology MRR improved 0.833→0.906. Promotion gate updated to v0.1. Active frontier remains SC-CONCEPT-0008 (frontier governance).", "source_artifacts": [ - "plans/today_frontier_2026-04-27.md", - "memory/research/topological-memory-v0/comparison_summary_v0.json", - "memory/research/topological-memory-v0/promotion_gate_v0.md" + "memory/research/topological-memory-v0/graph_v0.json", + "memory/research/topological-memory-v0/comparison_report_v0.md", + "memory/research/topological-memory-v0/runtime_adoption_comparison_v0.md", + "memory/research/topological-memory-v0/promotion_gate_v0.json", + "docs/notes/topological_memory_v0_provisional_validation.md" ], "frontier_backlog": [ { - "concept_id": "SC-CONCEPT-0004", - "title": "topological-memory-continuity-retrieval", + "concept_id": "SC-CONCEPT-0008", + "title": "proof-path-frontier-governance", "rank": 1, "proof_path_level": 3, "target_level": 4, "status": "active", - "rationale": "Executable substrate, frozen 30-query benchmark, and explicit path-evidence failure gate now make this the cleanest active proof surface.", - "next_move": "Extend runtime-consumer evaluation and rerun with embedding baseline available.", + "rationale": "With topological-memory-retrieval (SC-CONCEPT-0004) successfully completed and validated, frontier governance itself becomes the active focus to reconcile current active/parked/completed states and ensure subsequent transitions stay decision-useful.", + "next_move": "Validate current completed/parked concepts across the repository, align human-readable plans with active-work JSON state, and prepare the next speculative trajectory pass.", + "failure_condition": "If notes and machine state drift again, add validation that catches the drift.", + "evidence": [ + "plans/today_frontier_2026-04-27.md", + "nfem_suite/intelligence/ygg/frontier.py" + ] + }, + { + "concept_id": "SC-CONCEPT-0004", + "title": "topological-memory-continuity-retrieval", + "rank": 2, + "proof_path_level": 3, + "target_level": 4, + "status": "completed", + "rationale": "Successfully beat both flat keyword and recency baselines on the frozen 30-query fixture (hit@3=1.000, mrr=0.867), producing inspectable and deterministic path traces.", + "next_move": "Proceed to Level-4 promotion gate validation under an embedding-rich environment once sentence-transformers becomes available.", "failure_condition": "If topology-aware retrieval no longer beats a flat baseline or path evidence becomes unreadable, demote and rerank.", "evidence": [ "memory/research/topological-memory-v0/baseline_report_v0.json", - "memory/research/topological-memory-v0/comparison_summary_v0.json" + "memory/research/topological-memory-v0/comparison_summary_v0.json", + "memory/research/topological-memory-v0/comparison_report_v0.md", + "docs/notes/topological_memory_v0_provisional_validation.md" ] }, { "concept_id": "SC-CONCEPT-0003", "title": "hyperstition-policy-attractor-dynamics", - "rank": 2, + "rank": 3, "proof_path_level": 4, "target_level": 4, "status": "parked", @@ -40,7 +59,7 @@ { "concept_id": "SC-CONCEPT-0006", "title": "symbolic-maps-and-narrative-invariants", - "rank": 3, + "rank": 4, "proof_path_level": 4, "target_level": 4, "status": "parked", @@ -51,21 +70,6 @@ "plans/symbolic_maps_level4_schema_hardening_gate_result_v0.md", "plans/symbolic_maps_operator_canonicalization_probe_result_v0.md" ] - }, - { - "concept_id": "SC-CONCEPT-0008", - "title": "proof-path-frontier-governance", - "rank": 4, - "proof_path_level": 3, - "target_level": 4, - "status": "candidate", - "rationale": "The rerank itself shows the governance instrument is useful, but it is supporting infrastructure rather than the current proof surface.", - "next_move": "Keep state machine and frontier snapshots aligned with human-readable notes.", - "failure_condition": "If notes and machine state drift again, add validation that catches the drift.", - "evidence": [ - "plans/today_frontier_2026-04-27.md", - "nfem_suite/intelligence/ygg/frontier.py" - ] } ] } diff --git a/telemetry-edge/.gitignore b/telemetry-edge/.gitignore new file mode 100644 index 0000000..37cf498 --- /dev/null +++ b/telemetry-edge/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +package-lock.json +out/ +*.tsbuildinfo diff --git a/telemetry-edge/README.md b/telemetry-edge/README.md new file mode 100644 index 0000000..32e84f9 --- /dev/null +++ b/telemetry-edge/README.md @@ -0,0 +1,50 @@ +# @sandy-chaos/telemetry-edge + +The self-referential tempo edge for the Sandy Chaos research engine. + +**Status:** Medium scope (synthetic source + aggregator + validated emission). No live capture yet. + +## What it does + +Reads streams of operator input and game-state events, aggregates them into windows, and emits `tempo_point_v0` records (one JSON object per window) for the Python research core to consume. + +**Architecturally enforced redaction:** raw input events live only in an in-memory ring buffer. The aggregator's output type physically cannot contain raw key sequences, chat strings, or operator identifiers. The schema's `additionalProperties: false` and top-level `not` block enforce the same constraint at the wire boundary. See [`../docs/24_tempo_edge_redaction_policy.md`](../docs/24_tempo_edge_redaction_policy.md). + +## Layout + +``` +src/ + types.ts TS types mirroring tempo_point_v0 + schema.ts schema loader + ajv validator + ring-buffer.ts size+time bounded buffer, clear-on-shutdown + aggregator.ts event stream -> windowed tempo_point + emit.ts validate-then-write JSONL + sources/ + synthetic.ts synthetic event generator (for tests and demo) +bin/ + emit-sample.ts CLI: generate synthetic session, emit JSONL +test/ + schema.test.ts + ring-buffer.test.ts + aggregator.test.ts + redaction.test.ts + integration.test.ts +``` + +## Usage + +```bash +cd telemetry-edge +npm install +npm test +npm run emit-sample -- --out /tmp/sample.jsonl +``` + +The CLI generates a synthetic session, runs it through the aggregator, validates each emitted record against the schema, and writes JSONL to `--out`. + +## Non-goals (for this scope) + +- No real SC2 replay parser. The seam is proven against synthetic input. +- No keyboard/mouse capture. Real input is a separate later scope, gated on operator review. +- No npm publication. This package is `private: true`. +- No live session capture. Bin/emit-sample is for testing the seam, not for production use. diff --git a/telemetry-edge/bin/emit-sample.ts b/telemetry-edge/bin/emit-sample.ts new file mode 100644 index 0000000..35857ab --- /dev/null +++ b/telemetry-edge/bin/emit-sample.ts @@ -0,0 +1,85 @@ +#!/usr/bin/env node +// CLI: generate a synthetic session, run it through the aggregator+ring buffer, validate each +// emitted record against the schema, and write JSONL to --out. +// +// This is a seam test, not a production capture tool. It uses the synthetic source so the +// run is deterministic and the redaction guarantees are easy to inspect. + +import { parseArgs } from "node:util"; +import { randomUUID } from "node:crypto"; +import { generateSyntheticSession } from "../src/sources/synthetic.ts"; +import { RingBuffer } from "../src/ring-buffer.ts"; +import { aggregate } from "../src/aggregator.ts"; +import { openJsonlEmitter } from "../src/emit.ts"; +import type { RawEvent, TempoPointSource } from "../src/types.ts"; + +const { values } = parseArgs({ + options: { + out: { type: "string" }, + "duration-ms": { type: "string", default: "180000" }, + "window-ms": { type: "string", default: "30000" }, + "mean-apm": { type: "string", default: "180" }, + seed: { type: "string", default: "1" }, + source: { type: "string", default: "game:sc2" }, + }, +}); + +if (!values.out) { + console.error("usage: emit-sample.ts --out [--duration-ms N] [--window-ms N] [--mean-apm N] [--seed N] [--source ]"); + process.exit(2); +} + +const durationMs = Number(values["duration-ms"]); +const windowMs = Number(values["window-ms"]); +const meanApm = Number(values["mean-apm"]); +const seed = Number(values.seed); +const source = values.source as TempoPointSource; + +const sessionStart = Date.now(); +const sessionId = randomUUID(); +const events = generateSyntheticSession({ durationMs, seed, meanApm }); + +// Simulated clock tracks the current window end so the ring buffer's time-eviction logic +// behaves the same way it would under live capture. +let simulatedNow = 0; +const ring = new RingBuffer({ + capacity: 50_000, + retentionMs: windowMs, + now: () => simulatedNow, +}); + +const emitter = openJsonlEmitter(values.out); + +let windowStart = 0; +let windowEnd = windowMs; +let cursor = 0; + +while (windowStart < durationMs) { + ring.clear(); + simulatedNow = windowEnd; + while (cursor < events.length && events[cursor]!.ts < windowEnd) { + const event: RawEvent = events[cursor]!; + if (event.ts >= windowStart) ring.push(event); + cursor++; + } + const windowEvents = ring.snapshot(); + const windowEndTs = new Date(sessionStart + windowEnd).toISOString(); + const point = aggregate(windowEvents, { + sessionId, + source, + windowMs, + pointKind: "apm_window", + windowEndTs, + }); + emitter.emit(point); + windowStart += windowMs; + windowEnd += windowMs; +} + +ring.clear(); +emitter.close(); + +console.log( + `emitted ${emitter.emittedCount()} tempo_point_v0 records to ${values.out} ` + + `(session ${sessionId}, duration ${durationMs}ms, window ${windowMs}ms, source ${source})`, +); diff --git a/telemetry-edge/package.json b/telemetry-edge/package.json new file mode 100644 index 0000000..c19c830 --- /dev/null +++ b/telemetry-edge/package.json @@ -0,0 +1,19 @@ +{ + "name": "@sandy-chaos/telemetry-edge", + "version": "0.0.1", + "private": true, + "description": "Self-referential tempo edge for the Sandy Chaos research engine. Emits tempo_point_v0 records to the Python core. No raw input ever crosses the seam. See ../docs/23 and ../docs/24.", + "type": "module", + "license": "UNLICENSED", + "engines": { + "node": ">=22" + }, + "scripts": { + "test": "node --test test/*.test.ts", + "emit-sample": "node bin/emit-sample.ts" + }, + "dependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1" + } +} diff --git a/telemetry-edge/src/aggregator.ts b/telemetry-edge/src/aggregator.ts new file mode 100644 index 0000000..a5a3bd9 --- /dev/null +++ b/telemetry-edge/src/aggregator.ts @@ -0,0 +1,178 @@ +// Window aggregator: RawEvent stream -> TempoPoint window summaries. +// +// Redaction-by-construction: this module reads only the categorical/structural fields of +// RawEvent (kind, key_class, action_class, decision_flag_kind, game-state numerics). It does +// not touch any field that could carry verbatim user input — there is no such field on RawEvent +// in the first place. Even if a malicious upstream attached an extra property to a RawEvent, +// the aggregator's output type (TempoAggregates) physically cannot carry it, and the schema +// validator at emit time would reject any record that did. + +import type { + RawEvent, + TempoPoint, + TempoAggregates, + TempoPointSource, + PointKind, + KeyClass, + ActionClass, + GameStateAggregates, +} from "./types.ts"; + +export interface WindowSpec { + sessionId: string; + source: TempoPointSource; + windowMs: number; + pointKind: PointKind; + windowEndTs: string; // ISO-8601 timestamp marking window END +} + +function percentile(sorted: number[], p: number): number { + if (sorted.length === 0) return 0; + const idx = Math.min(sorted.length - 1, Math.max(0, Math.floor(p * sorted.length))); + return sorted[idx]!; +} + +function mean(values: number[]): number { + if (values.length === 0) return 0; + let s = 0; + for (const v of values) s += v; + return s / values.length; +} + +function modal(counts: Map, fallback: T): T { + let best: T = fallback; + let bestCount = -1; + let total = 0; + for (const [k, v] of counts) { + total += v; + if (v > bestCount) { + best = k; + bestCount = v; + } + } + if (total === 0) return fallback; + return best; +} + +function intervalsBetween(timestamps: number[]): number[] { + if (timestamps.length < 2) return []; + const sorted = timestamps.slice().sort((a, b) => a - b); + const out: number[] = []; + for (let i = 1; i < sorted.length; i++) { + out.push(sorted[i]! - sorted[i - 1]!); + } + return out; +} + +function sumGameState(events: RawEvent[]): GameStateAggregates | undefined { + const gs = events.flatMap((e) => (e.kind === "game_state" ? [e] : [])); + if (gs.length === 0) return undefined; + const out: GameStateAggregates = {}; + let any = false; + const add = (field: keyof GameStateAggregates, v: number | undefined) => { + if (v === undefined) return; + any = true; + if (typeof out[field] === "number") { + (out[field] as number) += v; + } else { + (out[field] as number) = v; + } + }; + for (const e of gs) { + add("unit_count_delta", e.delta.unit_count_delta); + add("supply_delta", e.delta.supply_delta); + add("resource_delta_primary", e.delta.resource_delta_primary); + add("resource_delta_secondary", e.delta.resource_delta_secondary); + add("score_delta", e.delta.score_delta); + if (e.delta.phase !== undefined) { + out.phase = e.delta.phase; + any = true; + } + if (e.delta.engagement_flag !== undefined) { + out.engagement_flag = e.delta.engagement_flag; + any = true; + } + } + return any ? out : undefined; +} + +export function aggregate(events: RawEvent[], spec: WindowSpec): TempoPoint { + const keyEvents = events.filter((e) => e.kind === "key"); + const mouseEvents = events.filter((e) => e.kind === "mouse"); + const decisionEvents = events.filter((e) => e.kind === "decision"); + + const inputEventCount = keyEvents.length + mouseEvents.length; + const allInputTs = [ + ...keyEvents.map((e) => e.ts), + ...mouseEvents.map((e) => e.ts), + ]; + const intervals = intervalsBetween(allInputTs); + const intervalsSorted = intervals.slice().sort((a, b) => a - b); + + const windowSeconds = spec.windowMs / 1000; + const apm = windowSeconds > 0 ? (keyEvents.length / windowSeconds) * 60 : 0; + const clickRate = windowSeconds > 0 ? mouseEvents.length / windowSeconds : 0; + const decisionRate = windowSeconds > 0 ? decisionEvents.length / windowSeconds : 0; + + const keyClassCounts = new Map(); + for (const e of keyEvents) { + keyClassCounts.set(e.key_class, (keyClassCounts.get(e.key_class) ?? 0) + 1); + } + for (const e of mouseEvents) { + const cls: KeyClass = e.button === "primary" ? "mouse_primary" : "mouse_secondary"; + keyClassCounts.set(cls, (keyClassCounts.get(cls) ?? 0) + 1); + } + + const actionClassCounts = new Map(); + for (const e of [...keyEvents, ...mouseEvents]) { + if (e.action_class) { + actionClassCounts.set(e.action_class, (actionClassCounts.get(e.action_class) ?? 0) + 1); + } + } + + const aggregates: TempoAggregates = { + event_count: inputEventCount + decisionEvents.length, + key_count: keyEvents.length, + click_count: mouseEvents.length, + decision_count: decisionEvents.length, + apm, + click_rate_hz: clickRate, + decision_rate_hz: decisionRate, + }; + + if (intervals.length > 0) { + aggregates.inter_event_interval_ms_mean = mean(intervals); + aggregates.inter_event_interval_ms_p50 = percentile(intervalsSorted, 0.5); + aggregates.inter_event_interval_ms_p95 = percentile(intervalsSorted, 0.95); + aggregates.inter_event_interval_ms_max = intervalsSorted[intervalsSorted.length - 1]!; + } + + if (inputEventCount > 0) { + aggregates.modal_key_class = modal(keyClassCounts, "none"); + } else { + aggregates.modal_key_class = "none"; + } + if (actionClassCounts.size > 0) { + aggregates.modal_action_class = modal(actionClassCounts, "none"); + } else { + aggregates.modal_action_class = "none"; + } + + const gameState = sumGameState(events); + if (gameState) aggregates.game_state = gameState; + + if (decisionEvents.length > 0) { + aggregates.decision_flag_kind = decisionEvents[0]!.decision_flag_kind; + } + + return { + schema_version: "tempo_point/v0", + ts: spec.windowEndTs, + session_id: spec.sessionId, + source: spec.source, + window_ms: spec.windowMs, + point_kind: spec.pointKind, + aggregates, + redaction_version: "redaction-policy-v0", + }; +} diff --git a/telemetry-edge/src/emit.ts b/telemetry-edge/src/emit.ts new file mode 100644 index 0000000..6e02f73 --- /dev/null +++ b/telemetry-edge/src/emit.ts @@ -0,0 +1,36 @@ +// Validate-then-write JSONL emission. +// +// Every record passes ajv before it is appended to the output. If validation fails the record +// is rejected (no partial writes, no fallback to "best effort" serialization). This is a hard +// invariant from the redaction policy: nothing that fails the schema gets to disk. + +import { appendFileSync, openSync, closeSync } from "node:fs"; +import type { TempoPoint } from "./types.ts"; +import { assertValid, TempoPointValidationError } from "./schema.ts"; + +export interface JsonlEmitter { + emit: (record: TempoPoint) => void; + close: () => void; + emittedCount: () => number; +} + +export function openJsonlEmitter(path: string): JsonlEmitter { + const fd = openSync(path, "w"); + closeSync(fd); // truncate + let count = 0; + return { + emit(record) { + assertValid(record); + appendFileSync(path, JSON.stringify(record) + "\n", "utf-8"); + count++; + }, + close() { + // file is appended per-record; nothing to flush beyond per-write fsync semantics + }, + emittedCount() { + return count; + }, + }; +} + +export { TempoPointValidationError }; diff --git a/telemetry-edge/src/ring-buffer.ts b/telemetry-edge/src/ring-buffer.ts new file mode 100644 index 0000000..02d943b --- /dev/null +++ b/telemetry-edge/src/ring-buffer.ts @@ -0,0 +1,64 @@ +// In-memory ring buffer for raw input events. +// Contract (see docs/24_tempo_edge_redaction_policy.md): +// - Size-bounded: capacity events max, oldest dropped on overflow. +// - Time-bounded: events older than retention_ms are aged out on insert and snapshot. +// - Never serialized: the buffer is the only place raw events live. +// - Clearable: clear() empties the buffer; intended to be called on window close and shutdown. +// +// The buffer holds RawEvent items whose categorical tags (key_class, action_class) are already +// safe-for-aggregation by construction. The buffer's job is bounding lifetime and bounding size; +// the upstream capture layer's job is producing tagged events in the first place. + +import type { RawEvent } from "./types.ts"; + +export interface RingBufferOptions { + capacity: number; + retentionMs: number; + now?: () => number; +} + +export class RingBuffer { + private readonly capacity: number; + private readonly retentionMs: number; + private readonly now: () => number; + private items: RawEvent[] = []; + + constructor(opts: RingBufferOptions) { + if (opts.capacity <= 0) throw new Error("capacity must be > 0"); + if (opts.retentionMs <= 0) throw new Error("retentionMs must be > 0"); + this.capacity = opts.capacity; + this.retentionMs = opts.retentionMs; + this.now = opts.now ?? (() => Date.now()); + } + + push(event: RawEvent): void { + this.evictExpired(); + this.items.push(event); + if (this.items.length > this.capacity) { + this.items.splice(0, this.items.length - this.capacity); + } + } + + // Snapshot returns the events currently in the buffer, after eviction. + // The returned array is a copy; mutating it does not affect the buffer. + snapshot(): RawEvent[] { + this.evictExpired(); + return this.items.slice(); + } + + size(): number { + this.evictExpired(); + return this.items.length; + } + + clear(): void { + this.items = []; + } + + private evictExpired(): void { + const cutoff = this.now() - this.retentionMs; + let drop = 0; + while (drop < this.items.length && this.items[drop]!.ts < cutoff) drop++; + if (drop > 0) this.items.splice(0, drop); + } +} diff --git a/telemetry-edge/src/schema.ts b/telemetry-edge/src/schema.ts new file mode 100644 index 0000000..09b6ad9 --- /dev/null +++ b/telemetry-edge/src/schema.ts @@ -0,0 +1,47 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, resolve } from "node:path"; +import Ajv2020 from "ajv/dist/2020.js"; +import addFormats from "ajv-formats"; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const SCHEMA_PATH = resolve(HERE, "../../schemas/tempo_point_v0.schema.json"); + +export const tempoPointSchema = JSON.parse( + readFileSync(SCHEMA_PATH, "utf-8"), +) as Record; + +// strictRequired is disabled because the schema uses `required` inside a `not/anyOf` block +// to declare forbidden fields. Those fields are intentionally NOT in `properties` (we want +// the schema to be silent about them at the type level while explicitly rejecting them). +// All other ajv strict checks remain on. +const ajv = new Ajv2020({ allErrors: true, strict: true, strictRequired: false }); +addFormats(ajv); + +export const validateTempoPoint = ajv.compile(tempoPointSchema); + +export class TempoPointValidationError extends Error { + readonly errors: ReturnType; + readonly record: unknown; + constructor( + message: string, + errors: ReturnType, + record: unknown, + ) { + super(message); + this.name = "TempoPointValidationError"; + this.errors = errors; + this.record = record; + } +} + +export function assertValid(record: unknown): asserts record { + if (!validateTempoPoint(record)) { + throw new TempoPointValidationError( + "tempo_point_v0 validation failed: " + + JSON.stringify(validateTempoPoint.errors), + validateTempoPoint.errors, + record, + ); + } +} diff --git a/telemetry-edge/src/sources/synthetic.ts b/telemetry-edge/src/sources/synthetic.ts new file mode 100644 index 0000000..bf6b6fb --- /dev/null +++ b/telemetry-edge/src/sources/synthetic.ts @@ -0,0 +1,116 @@ +// Synthetic event source. +// +// Produces deterministic RawEvent streams that mimic what a real SC2 replay parser + input +// listener would emit. Deterministic = seeded PRNG, no Date.now(). Used by tests and by the +// emit-sample CLI to demonstrate the seam end-to-end without depending on a real game-state +// parser or input capture. +// +// This source NEVER produces raw text, raw key codes, or operator identifiers. Events carry +// only the categorical/structural fields that the production capture layer is also expected +// to produce. + +import type { RawEvent, KeyClass, ActionClass, Phase, DecisionFlagKind } from "../types.ts"; + +function mulberry32(seed: number): () => number { + let s = seed >>> 0; + return () => { + s = (s + 0x6d2b79f5) >>> 0; + let t = s; + t = Math.imul(t ^ (t >>> 15), t | 1); + t ^= t + Math.imul(t ^ (t >>> 7), t | 61); + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +} + +export interface SyntheticSessionOptions { + durationMs: number; + seed?: number; + meanApm?: number; // target APM + clickFraction?: number; // share of input events that are clicks (0..1) +} + +const KEY_CLASS_POOL: KeyClass[] = ["movement", "hotkey", "modifier"]; +const ACTION_CLASS_POOL: ActionClass[] = [ + "macro", + "micro", + "control_group", + "camera", + "build_order", + "engagement", + "retreat", +]; +const DECISION_KIND_POOL: DecisionFlagKind[] = [ + "build_order_change", + "engagement_initiated", + "engagement_disengaged", + "expansion", + "tech_switch", + "scout_dispatch", +]; +const PHASE_ORDER: Phase[] = ["opening", "early", "mid", "late", "endgame"]; + +export function generateSyntheticSession(opts: SyntheticSessionOptions): RawEvent[] { + const seed = opts.seed ?? 1; + const rng = mulberry32(seed); + const meanApm = opts.meanApm ?? 150; + const clickFraction = opts.clickFraction ?? 0.25; + const totalSeconds = opts.durationMs / 1000; + const expectedInputs = (meanApm / 60) * totalSeconds; + + const events: RawEvent[] = []; + + // Input events: Poisson-ish placement via uniform draws + sort, then label. + for (let i = 0; i < expectedInputs; i++) { + const ts = Math.floor(rng() * opts.durationMs); + const isClick = rng() < clickFraction; + const actionClass = ACTION_CLASS_POOL[Math.floor(rng() * ACTION_CLASS_POOL.length)]!; + if (isClick) { + events.push({ + kind: "mouse", + ts, + button: rng() < 0.85 ? "primary" : "secondary", + action_class: actionClass, + }); + } else { + const keyClass = KEY_CLASS_POOL[Math.floor(rng() * KEY_CLASS_POOL.length)]!; + events.push({ + kind: "key", + ts, + key_class: keyClass, + action_class: actionClass, + }); + } + } + + // Decisions: 1 per ~5s of session, structurally tagged. + const decisionCount = Math.max(1, Math.floor(totalSeconds / 5)); + for (let i = 0; i < decisionCount; i++) { + const ts = Math.floor(rng() * opts.durationMs); + events.push({ + kind: "decision", + ts, + decision_flag_kind: DECISION_KIND_POOL[Math.floor(rng() * DECISION_KIND_POOL.length)]!, + }); + } + + // Game state: phase transitions + periodic supply/resource ticks. + const gameStateTickMs = 30_000; + for (let t = 0; t < opts.durationMs; t += gameStateTickMs) { + const phaseIdx = Math.min(PHASE_ORDER.length - 1, Math.floor((t / opts.durationMs) * PHASE_ORDER.length)); + events.push({ + kind: "game_state", + ts: t, + delta: { + phase: PHASE_ORDER[phaseIdx]!, + supply_delta: Math.floor((rng() - 0.5) * 10), + resource_delta_primary: Math.floor(rng() * 200 - 50), + resource_delta_secondary: Math.floor(rng() * 100 - 25), + unit_count_delta: Math.floor((rng() - 0.5) * 6), + engagement_flag: rng() < 0.2, + }, + }); + } + + events.sort((a, b) => a.ts - b.ts); + return events; +} diff --git a/telemetry-edge/src/types.ts b/telemetry-edge/src/types.ts new file mode 100644 index 0000000..251a61f --- /dev/null +++ b/telemetry-edge/src/types.ts @@ -0,0 +1,130 @@ +// TypeScript mirror of schemas/tempo_point_v0.schema.json. +// The schema is canonical; these types exist for compile-time safety inside the edge. +// Any change here MUST be made in lockstep with the JSON schema, or the schema test will fail. + +export type TempoPointSource = + | "game:sc2" + | "game:arc-raiders" + | "game:fortnite" + | "game:league" + | "game:cyberpunk" + | "game:other" + | "input:keyboard" + | "input:mouse" + | "mixed"; + +export type PointKind = + | "apm_window" + | "interval_cluster" + | "decision_flag" + | "game_state_delta"; + +export type KeyClass = + | "movement" + | "hotkey" + | "modifier" + | "mouse_primary" + | "mouse_secondary" + | "mixed" + | "none"; + +export type ActionClass = + | "macro" + | "micro" + | "control_group" + | "camera" + | "build_order" + | "engagement" + | "retreat" + | "idle" + | "mixed" + | "none"; + +export type Phase = "opening" | "early" | "mid" | "late" | "endgame" | "unknown"; + +export type DecisionFlagKind = + | "build_order_change" + | "engagement_initiated" + | "engagement_disengaged" + | "expansion" + | "tech_switch" + | "scout_dispatch" + | "other"; + +export interface GameStateAggregates { + unit_count_delta?: number; + supply_delta?: number; + resource_delta_primary?: number; + resource_delta_secondary?: number; + score_delta?: number; + phase?: Phase; + engagement_flag?: boolean; +} + +export interface TempoAggregates { + event_count?: number; + key_count?: number; + click_count?: number; + decision_count?: number; + apm?: number; + click_rate_hz?: number; + decision_rate_hz?: number; + inter_event_interval_ms_mean?: number; + inter_event_interval_ms_p50?: number; + inter_event_interval_ms_p95?: number; + inter_event_interval_ms_max?: number; + modal_key_class?: KeyClass; + modal_action_class?: ActionClass; + game_state?: GameStateAggregates; + decision_flag_kind?: DecisionFlagKind; +} + +export interface TempoPoint { + schema_version: "tempo_point/v0"; + ts: string; + session_id: string; + source: TempoPointSource; + window_ms: number; + point_kind: PointKind; + aggregates: TempoAggregates; + redaction_version: "redaction-policy-v0"; + notes?: string; +} + +// Raw input event types. These NEVER appear in tempo_point output. +// They exist only inside the edge process, in the ring buffer, never serialized. + +export type RawKeyClassTag = KeyClass; +export type RawActionClassTag = ActionClass; + +export interface RawKeyEvent { + kind: "key"; + ts: number; // monotonic ms since session start + key_class: RawKeyClassTag; // categorical class, NOT the literal key + action_class?: RawActionClassTag; +} + +export interface RawMouseEvent { + kind: "mouse"; + ts: number; + button: "primary" | "secondary"; + action_class?: RawActionClassTag; +} + +export interface RawDecisionEvent { + kind: "decision"; + ts: number; + decision_flag_kind: DecisionFlagKind; +} + +export interface RawGameStateDelta { + kind: "game_state"; + ts: number; + delta: GameStateAggregates; +} + +export type RawEvent = + | RawKeyEvent + | RawMouseEvent + | RawDecisionEvent + | RawGameStateDelta; diff --git a/telemetry-edge/test/aggregator.test.ts b/telemetry-edge/test/aggregator.test.ts new file mode 100644 index 0000000..4e760f2 --- /dev/null +++ b/telemetry-edge/test/aggregator.test.ts @@ -0,0 +1,101 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { aggregate, type WindowSpec } from "../src/aggregator.ts"; +import { validateTempoPoint } from "../src/schema.ts"; +import type { RawEvent } from "../src/types.ts"; + +const SPEC: WindowSpec = { + sessionId: "agg-test-0001", + source: "game:sc2", + windowMs: 60_000, + pointKind: "apm_window", + windowEndTs: "2026-06-06T20:00:00-04:00", +}; + +test("empty window emits a zero-count, schema-valid record", () => { + const point = aggregate([], SPEC); + assert.equal(validateTempoPoint(point), true); + assert.equal(point.aggregates.event_count, 0); + assert.equal(point.aggregates.key_count, 0); + assert.equal(point.aggregates.apm, 0); + assert.equal(point.aggregates.modal_key_class, "none"); +}); + +test("APM = key_count * 60 / window_seconds", () => { + const events: RawEvent[] = []; + for (let i = 0; i < 180; i++) { + events.push({ kind: "key", ts: i * 333, key_class: "hotkey" }); + } + const point = aggregate(events, SPEC); // 60s window + assert.equal(point.aggregates.key_count, 180); + assert.equal(point.aggregates.apm, 180); // 180 keys per 60s = 180 APM +}); + +test("modal_key_class returns the most common class, never a literal key", () => { + const events: RawEvent[] = [ + { kind: "key", ts: 1, key_class: "movement" }, + { kind: "key", ts: 2, key_class: "movement" }, + { kind: "key", ts: 3, key_class: "movement" }, + { kind: "key", ts: 4, key_class: "hotkey" }, + { kind: "key", ts: 5, key_class: "modifier" }, + ]; + const point = aggregate(events, SPEC); + assert.equal(point.aggregates.modal_key_class, "movement"); + // The output is a categorical tag, not anything that could contain a key character. + assert.ok( + ["movement", "hotkey", "modifier", "mouse_primary", "mouse_secondary", "mixed", "none"].includes( + point.aggregates.modal_key_class!, + ), + ); +}); + +test("intervals: mean/p50/p95/max computed only when there are 2+ events", () => { + const events: RawEvent[] = [ + { kind: "key", ts: 0, key_class: "hotkey" }, + { kind: "key", ts: 100, key_class: "hotkey" }, + { kind: "key", ts: 200, key_class: "hotkey" }, + { kind: "key", ts: 350, key_class: "hotkey" }, + ]; + const point = aggregate(events, SPEC); + // intervals = [100, 100, 150], sorted = [100, 100, 150] + assert.equal(point.aggregates.inter_event_interval_ms_max, 150); + assert.equal(point.aggregates.inter_event_interval_ms_p50, 100); + assert.equal(point.aggregates.inter_event_interval_ms_mean, 350 / 3); +}); + +test("intervals absent on single-event window", () => { + const events: RawEvent[] = [{ kind: "key", ts: 0, key_class: "hotkey" }]; + const point = aggregate(events, SPEC); + assert.equal(point.aggregates.inter_event_interval_ms_mean, undefined); +}); + +test("game_state aggregates sum numeric deltas", () => { + const events: RawEvent[] = [ + { kind: "game_state", ts: 0, delta: { phase: "opening", supply_delta: 5 } }, + { kind: "game_state", ts: 100, delta: { supply_delta: 3, resource_delta_primary: 100 } }, + ]; + const point = aggregate(events, SPEC); + assert.equal(point.aggregates.game_state?.supply_delta, 8); + assert.equal(point.aggregates.game_state?.resource_delta_primary, 100); + assert.equal(point.aggregates.game_state?.phase, "opening"); +}); + +test("decision events contribute to decision_count and decision_flag_kind", () => { + const events: RawEvent[] = [ + { kind: "decision", ts: 100, decision_flag_kind: "engagement_initiated" }, + ]; + const point = aggregate(events, SPEC); + assert.equal(point.aggregates.decision_count, 1); + assert.equal(point.aggregates.decision_flag_kind, "engagement_initiated"); +}); + +test("aggregate output is always schema-valid (mixed event stream)", () => { + const events: RawEvent[] = [ + { kind: "key", ts: 0, key_class: "hotkey", action_class: "micro" }, + { kind: "mouse", ts: 50, button: "primary", action_class: "engagement" }, + { kind: "decision", ts: 75, decision_flag_kind: "engagement_initiated" }, + { kind: "game_state", ts: 100, delta: { phase: "mid", engagement_flag: true } }, + ]; + const point = aggregate(events, SPEC); + assert.equal(validateTempoPoint(point), true); +}); diff --git a/telemetry-edge/test/integration.test.ts b/telemetry-edge/test/integration.test.ts new file mode 100644 index 0000000..d5197b8 --- /dev/null +++ b/telemetry-edge/test/integration.test.ts @@ -0,0 +1,80 @@ +// End-to-end seam test: synthetic source -> ring buffer -> aggregator -> emit -> validate. + +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { randomUUID } from "node:crypto"; +import { generateSyntheticSession } from "../src/sources/synthetic.ts"; +import { RingBuffer } from "../src/ring-buffer.ts"; +import { aggregate } from "../src/aggregator.ts"; +import { openJsonlEmitter } from "../src/emit.ts"; +import { validateTempoPoint } from "../src/schema.ts"; + +test("synthetic session round-trip produces validated JSONL records", () => { + const dir = mkdtempSync(join(tmpdir(), "tempo-edge-integration-")); + const path = join(dir, "out.jsonl"); + try { + const durationMs = 120_000; + const windowMs = 30_000; + const events = generateSyntheticSession({ durationMs, seed: 42, meanApm: 180 }); + + let simulatedNow = 0; + const ring = new RingBuffer({ + capacity: 10_000, + retentionMs: windowMs, + now: () => simulatedNow, + }); + const emitter = openJsonlEmitter(path); + const sessionId = randomUUID(); + + let windowStart = 0; + let windowEnd = windowMs; + let cursor = 0; + const expectedWindows = durationMs / windowMs; + let maxApmSeen = 0; + + while (windowStart < durationMs) { + ring.clear(); + simulatedNow = windowEnd; + while (cursor < events.length && events[cursor]!.ts < windowEnd) { + const event = events[cursor]!; + if (event.ts >= windowStart) ring.push(event); + cursor++; + } + const windowEvents = ring.snapshot(); + const point = aggregate(windowEvents, { + sessionId, + source: "game:sc2", + windowMs, + pointKind: "apm_window", + windowEndTs: new Date(windowEnd).toISOString(), + }); + emitter.emit(point); + if ((point.aggregates.apm ?? 0) > maxApmSeen) maxApmSeen = point.aggregates.apm!; + windowStart += windowMs; + windowEnd += windowMs; + } + + ring.clear(); + emitter.close(); + assert.equal(emitter.emittedCount(), expectedWindows); + assert.ok(maxApmSeen > 50, `expected at least one window with apm > 50, got max ${maxApmSeen}`); + + const lines = readFileSync(path, "utf-8").trim().split("\n"); + assert.equal(lines.length, expectedWindows); + for (const line of lines) { + const obj = JSON.parse(line); + assert.equal(validateTempoPoint(obj), true); + } + + // Defense in depth: the JSONL must not contain any forbidden token. + const text = readFileSync(path, "utf-8"); + for (const forbidden of ["raw_keys", "raw_input", "chat", "transcript", "username", "hostname", "password"]) { + assert.equal(text.includes(forbidden), false, `JSONL must not contain '${forbidden}'`); + } + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); diff --git a/telemetry-edge/test/redaction.test.ts b/telemetry-edge/test/redaction.test.ts new file mode 100644 index 0000000..4207789 --- /dev/null +++ b/telemetry-edge/test/redaction.test.ts @@ -0,0 +1,74 @@ +// Redaction tests. +// These exist to catch any future regression where a forbidden field leaks through the seam. + +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { aggregate, type WindowSpec } from "../src/aggregator.ts"; +import { validateTempoPoint, assertValid, TempoPointValidationError } from "../src/schema.ts"; +import { openJsonlEmitter } from "../src/emit.ts"; +import { mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import type { RawEvent } from "../src/types.ts"; + +const SPEC: WindowSpec = { + sessionId: "redaction-test-0001", + source: "game:sc2", + windowMs: 30_000, + pointKind: "apm_window", + windowEndTs: "2026-06-06T20:00:00-04:00", +}; + +test("aggregator output never contains raw_* keys, even if attached to inputs", () => { + // Smuggle attempt: extra property on a RawEvent. + const tainted = { + kind: "key", + ts: 0, + key_class: "hotkey", + raw_key: "p", + chat: "hello", + typed_text: "password123", + } as unknown as RawEvent; + const point = aggregate([tainted], SPEC); + const serialized = JSON.stringify(point); + assert.equal(serialized.includes("raw_key"), false); + assert.equal(serialized.includes("chat"), false); + assert.equal(serialized.includes("typed_text"), false); + assert.equal(serialized.includes("password123"), false); +}); + +test("emit refuses to write a record that includes a raw_keys field", () => { + const dir = mkdtempSync(join(tmpdir(), "tempo-edge-redaction-")); + const path = join(dir, "out.jsonl"); + try { + const e = openJsonlEmitter(path); + const point = aggregate([], SPEC) as unknown as Record; + point["raw_keys"] = ["a", "b"]; + assert.throws(() => e.emit(point as never), TempoPointValidationError); + const contents = readFileSync(path, "utf-8"); + assert.equal(contents.length, 0, "no record should have been written"); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test("emit refuses username, chat, hostname, text", () => { + for (const field of ["username", "chat", "hostname", "text", "transcript", "ip_address"]) { + const point = aggregate([], SPEC) as unknown as Record; + point[field] = "anything"; + assert.throws(() => assertValid(point), TempoPointValidationError, `field ${field} should be rejected`); + } +}); + +test("modal_action_class is always a categorical tag from the allowed set", () => { + const events: RawEvent[] = [ + { kind: "key", ts: 0, key_class: "hotkey", action_class: "engagement" }, + { kind: "key", ts: 1, key_class: "hotkey", action_class: "engagement" }, + { kind: "mouse", ts: 2, button: "primary", action_class: "retreat" }, + ]; + const point = aggregate(events, SPEC); + const allowed = ["macro", "micro", "control_group", "camera", "build_order", "engagement", "retreat", "idle", "mixed", "none"]; + assert.ok(allowed.includes(point.aggregates.modal_action_class!)); + // Also the record passes the schema (which enforces the same enum). + assert.equal(validateTempoPoint(point), true); +}); diff --git a/telemetry-edge/test/ring-buffer.test.ts b/telemetry-edge/test/ring-buffer.test.ts new file mode 100644 index 0000000..0c7d99a --- /dev/null +++ b/telemetry-edge/test/ring-buffer.test.ts @@ -0,0 +1,57 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { RingBuffer } from "../src/ring-buffer.ts"; +import type { RawKeyEvent } from "../src/types.ts"; + +function key(ts: number): RawKeyEvent { + return { kind: "key", ts, key_class: "hotkey" }; +} + +test("size cap drops oldest on overflow", () => { + let now = 0; + const buf = new RingBuffer({ capacity: 3, retentionMs: 1_000_000, now: () => now }); + for (let i = 0; i < 5; i++) buf.push(key(i)); + assert.equal(buf.size(), 3); + const items = buf.snapshot(); + assert.deepEqual(items.map((e) => (e as RawKeyEvent).ts), [2, 3, 4]); +}); + +test("time cap evicts events older than retentionMs", () => { + let now = 100; + const buf = new RingBuffer({ capacity: 100, retentionMs: 50, now: () => now }); + buf.push(key(10)); + buf.push(key(60)); + buf.push(key(95)); + now = 200; + assert.equal(buf.size(), 0); // all older than now-50=150 + now = 100; + buf.push(key(60)); + buf.push(key(80)); + now = 120; + assert.equal(buf.size(), 1); // only the ts=80 event survives (cutoff = 120-50 = 70) +}); + +test("clear empties the buffer", () => { + let now = 0; + const buf = new RingBuffer({ capacity: 10, retentionMs: 1000, now: () => now }); + buf.push(key(1)); + buf.push(key(2)); + assert.equal(buf.size(), 2); + buf.clear(); + assert.equal(buf.size(), 0); + assert.deepEqual(buf.snapshot(), []); +}); + +test("snapshot returns a copy, not the live array", () => { + let now = 0; + const buf = new RingBuffer({ capacity: 10, retentionMs: 1000, now: () => now }); + buf.push(key(1)); + const snap = buf.snapshot(); + snap.length = 0; // mutate the copy + assert.equal(buf.size(), 1); // buffer untouched +}); + +test("constructor rejects invalid bounds", () => { + assert.throws(() => new RingBuffer({ capacity: 0, retentionMs: 100 })); + assert.throws(() => new RingBuffer({ capacity: 5, retentionMs: 0 })); +}); diff --git a/telemetry-edge/test/schema.test.ts b/telemetry-edge/test/schema.test.ts new file mode 100644 index 0000000..d99145f --- /dev/null +++ b/telemetry-edge/test/schema.test.ts @@ -0,0 +1,77 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { validateTempoPoint, tempoPointSchema, assertValid, TempoPointValidationError } from "../src/schema.ts"; + +const VALID: Record = { + schema_version: "tempo_point/v0", + ts: "2026-06-06T20:00:00-04:00", + session_id: "test-session-0001", + source: "game:sc2", + window_ms: 30000, + point_kind: "apm_window", + aggregates: { + event_count: 100, + key_count: 80, + click_count: 20, + apm: 160, + modal_key_class: "hotkey", + modal_action_class: "micro", + }, + redaction_version: "redaction-policy-v0", +}; + +test("schema is loaded with the expected $id", () => { + assert.equal( + tempoPointSchema["$id"], + "https://github.com/yanmo42/sandy-chaos/schemas/tempo_point_v0.schema.json", + ); +}); + +test("valid record passes", () => { + assert.equal(validateTempoPoint(VALID), true); +}); + +test("raw_keys is rejected", () => { + const bad = { ...VALID, raw_keys: ["a", "s", "d"] }; + assert.equal(validateTempoPoint(bad), false); +}); + +test("username is rejected", () => { + const bad = { ...VALID, username: "ian" }; + assert.equal(validateTempoPoint(bad), false); +}); + +test("unknown root field is rejected (additionalProperties)", () => { + const bad = { ...VALID, mystery_field: 42 }; + assert.equal(validateTempoPoint(bad), false); +}); + +test("unknown aggregate field is rejected (additionalProperties)", () => { + const bad = { + ...VALID, + aggregates: { ...(VALID.aggregates as object), secret_typed_text: "hello" }, + }; + assert.equal(validateTempoPoint(bad), false); +}); + +test("window_ms upper bound enforced", () => { + const bad = { ...VALID, window_ms: 999_999_999 }; + assert.equal(validateTempoPoint(bad), false); +}); + +test("schema_version const enforced", () => { + const bad = { ...VALID, schema_version: "tempo_point/v1" }; + assert.equal(validateTempoPoint(bad), false); +}); + +test("redaction_version const enforced", () => { + const bad = { ...VALID, redaction_version: "redaction-policy-v1" }; + assert.equal(validateTempoPoint(bad), false); +}); + +test("assertValid throws TempoPointValidationError on bad record", () => { + assert.throws( + () => assertValid({ ...VALID, raw_keys: ["x"] }), + TempoPointValidationError, + ); +}); diff --git a/telemetry-edge/tsconfig.json b/telemetry-edge/tsconfig.json new file mode 100644 index 0000000..66ee742 --- /dev/null +++ b/telemetry-edge/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "noUncheckedIndexedAccess": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "allowImportingTsExtensions": true, + "noEmit": true, + "verbatimModuleSyntax": false, + "isolatedModules": true + }, + "include": ["src/**/*", "bin/**/*", "test/**/*"] +}