sneakoscope 0.6.3 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -74
- package/docs/PERFORMANCE.md +17 -2
- package/package.json +4 -1
- package/src/cli/main.mjs +255 -5
- package/src/core/evaluation.mjs +11 -0
- package/src/core/fsx.mjs +1 -1
- package/src/core/gx-renderer.mjs +40 -1
- package/src/core/hooks-runtime.mjs +5 -1
- package/src/core/init.mjs +27 -8
- package/src/core/triwiki-attention.mjs +44 -11
- package/src/core/wiki-coordinate.mjs +228 -0
package/README.md
CHANGED
|
@@ -16,6 +16,16 @@ Sneakoscope Codex is an update-aware, zero-runtime-dependency Node.js harness fo
|
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npm i -g sneakoscope
|
|
19
|
+
sks
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`npm i -g sneakoscope` prints the next command without opening an interactive prompt, so CI and agent installs do not hang. Run `sks` in a real terminal to open the setup UI. The UI asks whether this project should use the global install or a project-only install, then offers to run setup, doctor, and selftest.
|
|
23
|
+
|
|
24
|
+
Default non-interactive setup:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
sks setup
|
|
28
|
+
sks doctor --fix
|
|
19
29
|
```
|
|
20
30
|
|
|
21
31
|
The npm package name is `sneakoscope`; the command is branded as SKS and exposed as lowercase `sks` for shell portability. The package also exposes a `sneakoscope` command alias, so `sks setup` and `sneakoscope setup` are equivalent.
|
|
@@ -25,22 +35,20 @@ Global installation is the default and recommended setup. During `sks setup` or
|
|
|
25
35
|
|
|
26
36
|
## One-Prompt LLM Install
|
|
27
37
|
|
|
28
|
-
If you are using Codex App, ChatGPT, Claude Code, Cursor, or another coding agent, copy
|
|
38
|
+
If you are using Codex App, ChatGPT, Claude Code, Cursor, or another coding agent, copy this short prompt from your target project directory. It intentionally avoids recovery branches and broad instructions so the agent does only the install and verification work.
|
|
29
39
|
|
|
30
40
|
````text
|
|
31
|
-
Install Sneakoscope Codex in
|
|
32
|
-
|
|
33
|
-
Repository:
|
|
34
|
-
https://github.com/mandarange/Sneakoscope-Codex.git
|
|
41
|
+
Install Sneakoscope Codex in this project.
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
43
|
+
Rules:
|
|
44
|
+
- Do not modify application source files.
|
|
45
|
+
- Ask only when a command requires user approval.
|
|
46
|
+
- If Node.js is below 20.11, stop and report it.
|
|
47
|
+
- If Codex CLI is missing, report: install @openai/codex or set SKS_CODEX_BIN.
|
|
41
48
|
|
|
42
|
-
Run:
|
|
49
|
+
Run exactly:
|
|
43
50
|
```bash
|
|
51
|
+
node -v
|
|
44
52
|
npm i -g sneakoscope
|
|
45
53
|
sks setup
|
|
46
54
|
sks update-check
|
|
@@ -50,68 +58,16 @@ sks commands
|
|
|
50
58
|
sks dollar-commands
|
|
51
59
|
```
|
|
52
60
|
|
|
53
|
-
If
|
|
54
|
-
```bash
|
|
55
|
-
npm uninstall -g sneakoscope
|
|
56
|
-
npm i -g sneakoscope
|
|
57
|
-
sks setup
|
|
58
|
-
sks doctor --fix
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
If `sks` is not on PATH:
|
|
62
|
-
```bash
|
|
63
|
-
npx -y -p sneakoscope sks setup
|
|
64
|
-
npx -y -p sneakoscope sks doctor --fix
|
|
65
|
-
npx -y -p sneakoscope sks selftest --mock
|
|
66
|
-
npx -y -p sneakoscope sks commands
|
|
67
|
-
npx -y -p sneakoscope sks dollar-commands
|
|
68
|
-
```
|
|
61
|
+
If `sks` is unavailable after install, replace `sks` with `npx -y -p sneakoscope sks` and continue.
|
|
69
62
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
npx sks update-check
|
|
75
|
-
npx sks doctor --fix --install-scope project
|
|
76
|
-
npx sks selftest --mock
|
|
77
|
-
npx sks commands
|
|
78
|
-
npx sks dollar-commands
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Finish by reporting pass/fail and explaining only these generated outputs:
|
|
82
|
-
- `.sneakoscope/` mission state and policy
|
|
83
|
-
- `.codex/config.toml` Codex App profiles
|
|
84
|
-
- `.codex/hooks.json` SKS hook integration
|
|
85
|
-
- `.codex/skills/` local Codex App skills
|
|
86
|
-
- `.codex/agents/` local Codex App multi-agent roles
|
|
87
|
-
- `.codex/SNEAKOSCOPE.md` Codex App quick reference
|
|
88
|
-
- `AGENTS.md` repository rules
|
|
89
|
-
|
|
90
|
-
Show command discovery:
|
|
91
|
-
```bash
|
|
92
|
-
sks help
|
|
93
|
-
sks update-check
|
|
94
|
-
sks commands
|
|
95
|
-
sks usage team
|
|
96
|
-
sks usage ralph
|
|
97
|
-
sks quickstart
|
|
98
|
-
sks codex-app
|
|
99
|
-
sks dollar-commands
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Tell the user they can use these prompt commands inside Codex App:
|
|
103
|
-
```text
|
|
104
|
-
$DF 글자 색 바꿔줘
|
|
105
|
-
$DF 내용을 영어로 바꿔줘
|
|
106
|
-
$SKS show me available workflows
|
|
107
|
-
$Team agree on the best plan and implement with specialists
|
|
108
|
-
$Ralph implement this with mandatory clarification
|
|
109
|
-
$Research investigate this idea
|
|
110
|
-
$AutoResearch improve this workflow with experiments
|
|
111
|
-
$DB check this migration safely
|
|
112
|
-
```
|
|
63
|
+
Finish with only:
|
|
64
|
+
- setup passed/failed
|
|
65
|
+
- Codex CLI present/missing
|
|
66
|
+
- generated files: `.sneakoscope/`, `.codex/config.toml`, `.codex/hooks.json`, `.codex/skills/`, `.codex/agents/`, `.codex/SNEAKOSCOPE.md`, `AGENTS.md`
|
|
113
67
|
````
|
|
114
68
|
|
|
69
|
+
Run `sks install-prompt --project` for a project-only prompt, or `sks install-prompt --full` for the longer recovery-oriented installer prompt.
|
|
70
|
+
|
|
115
71
|
## Repository
|
|
116
72
|
|
|
117
73
|
```bash
|
|
@@ -409,10 +365,11 @@ All terminal examples below use `sks`, but the same commands can be run with the
|
|
|
409
365
|
```bash
|
|
410
366
|
sks help [topic]
|
|
411
367
|
sks update-check [--json]
|
|
368
|
+
sks wizard
|
|
412
369
|
sks commands [--json]
|
|
413
|
-
sks usage [install|setup|team|ralph|research|db|codex-app|df|dollar|eval|gx]
|
|
370
|
+
sks usage [install|setup|team|ralph|research|db|codex-app|df|dollar|eval|gx|wiki]
|
|
414
371
|
sks quickstart
|
|
415
|
-
sks install-prompt [--project]
|
|
372
|
+
sks install-prompt [--project] [--full]
|
|
416
373
|
sks codex-app
|
|
417
374
|
sks dollar-commands [--json]
|
|
418
375
|
sks df
|
|
@@ -449,6 +406,10 @@ sks eval run [--json] [--out report.json] [--iterations N]
|
|
|
449
406
|
sks eval compare --baseline old.json --candidate new.json [--json]
|
|
450
407
|
sks eval thresholds
|
|
451
408
|
|
|
409
|
+
sks wiki coords --rgba 12,34,56,255
|
|
410
|
+
sks wiki pack [--json] [--role worker|verifier] [--max-anchors N]
|
|
411
|
+
sks wiki validate [context-pack.json]
|
|
412
|
+
|
|
452
413
|
sks hproof check [mission-id|latest]
|
|
453
414
|
sks team "task" [--json]
|
|
454
415
|
sks gx init [name]
|
|
@@ -622,7 +583,7 @@ sks hproof check latest
|
|
|
622
583
|
`sks init` creates the local control surface:
|
|
623
584
|
|
|
624
585
|
```text
|
|
625
|
-
.sneakoscope/ mission state, policy, retention, logs, GX cartridges
|
|
586
|
+
.sneakoscope/ mission state, policy, retention, logs, wiki packs, GX cartridges
|
|
626
587
|
.codex/config.toml Codex profiles used by Sneakoscope Codex
|
|
627
588
|
.codex/hooks.json hook entrypoints
|
|
628
589
|
.codex/skills/ Codex App local project skills
|
|
@@ -684,7 +645,28 @@ vgraph.json
|
|
|
684
645
|
|
|
685
646
|
## TriWiki Context Compression
|
|
686
647
|
|
|
687
|
-
TriWiki is a harness-level context selection strategy, not a model-internal modification. It scores claims and memory entries by geometric distance, authority, freshness, risk, and token cost, then builds
|
|
648
|
+
TriWiki is a harness-level context selection strategy, not a model-internal modification. It scores claims and memory entries by geometric distance, authority, freshness, risk, and token cost, then builds context capsules for the current mission.
|
|
649
|
+
|
|
650
|
+
The default model is anchor-first rather than lossy-summary-first. Selected claims are included as text, while non-selected claims are preserved as LLM Wiki anchors with id, source path, hash, RGBA key, and a compact coordinate tuple. Later turns can hydrate the missing context from the project wiki instead of depending on a one-way summary.
|
|
651
|
+
|
|
652
|
+
RGBA wiki coordinates use four channels:
|
|
653
|
+
|
|
654
|
+
```text
|
|
655
|
+
R -> domain angle
|
|
656
|
+
G -> layer radius through sin()
|
|
657
|
+
B -> phase angle
|
|
658
|
+
A -> concentration/confidence
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
The derived coordinate is `[domain, layer, phase, concentration]`, with an internal `xyzw` vector computed through sine/cosine. GX renders expose the same anchors through SVG data attributes and an RGBA coordinate strip, so visual context and text claims share one retrieval space.
|
|
662
|
+
|
|
663
|
+
Useful commands:
|
|
664
|
+
|
|
665
|
+
```bash
|
|
666
|
+
sks wiki coords --rgba 12,34,56,255
|
|
667
|
+
sks wiki pack
|
|
668
|
+
sks wiki validate
|
|
669
|
+
```
|
|
688
670
|
|
|
689
671
|
Default context layers:
|
|
690
672
|
|
package/docs/PERFORMANCE.md
CHANGED
|
@@ -6,8 +6,8 @@ Sneakoscope Codex v0.6 is designed to keep runtime, package size, RAM, and stora
|
|
|
6
6
|
|
|
7
7
|
- `codex exec` output is streamed to files and only a bounded tail is retained in memory.
|
|
8
8
|
- Ralph cycles run under a timeout and bounded max cycles.
|
|
9
|
-
- TriWiki claim selection uses bounded top-K selection instead of sorting unbounded context into prompts.
|
|
10
|
-
- GX visual context renders deterministic SVG/HTML from JSON sources, avoiding external image-generation latency, cost, and nondeterminism.
|
|
9
|
+
- TriWiki claim selection uses bounded top-K selection plus RGBA/trig wiki anchors instead of sorting unbounded context into prompts.
|
|
10
|
+
- GX visual context renders deterministic SVG/HTML from JSON sources, avoiding external image-generation latency, cost, and nondeterminism. Rendered nodes expose the same RGBA wiki-coordinate anchors used by TriWiki.
|
|
11
11
|
- `sks gc` runs after Ralph cycles by default.
|
|
12
12
|
|
|
13
13
|
## Evaluation metrics
|
|
@@ -30,6 +30,19 @@ Default meaningful-improvement thresholds are intentionally explicit: at least 2
|
|
|
30
30
|
|
|
31
31
|
The accuracy metric is not a live model task score. It is a deterministic proxy for whether the context handed to a model is smaller, better supported, and less contaminated by unsupported critical claims.
|
|
32
32
|
|
|
33
|
+
## LLM Wiki coordinate continuity
|
|
34
|
+
|
|
35
|
+
TriWiki does not treat compression as permanent deletion. The visible context pack includes selected claim text plus a compact LLM Wiki coordinate index:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
R channel -> domain angle
|
|
39
|
+
G channel -> layer radius via sin()
|
|
40
|
+
B channel -> phase angle
|
|
41
|
+
A channel -> concentration/confidence
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Each anchor stores id, RGBA key, `[domain, layer, phase, concentration]`, source path, status/risk, and a text hash. This keeps non-selected claims hydratable across turns while keeping raw Q0 logs and large Q1 evidence out of the prompt until verification needs them.
|
|
45
|
+
|
|
33
46
|
## Package size
|
|
34
47
|
|
|
35
48
|
- The npm package has zero runtime dependencies.
|
|
@@ -66,3 +79,5 @@ Blocked classes include destructive SQL, direct remote SQL mutation, `supabase d
|
|
|
66
79
|
Sneakoscope Codex v0.4 replaces model-rendered visual cartridges with deterministic code-rendered context sheets. `vgraph.json` and `beta.json` are the inputs, `render.svg` and `render.html` are reproducible outputs, and `drift.json` records whether the rendered source hash still matches the current graph.
|
|
67
80
|
|
|
68
81
|
This keeps visual context cheap to regenerate, diffable in normal tooling, and safe to validate during npm packaging without network calls or model access.
|
|
82
|
+
|
|
83
|
+
GX snapshots include `wiki_coordinates`, and `render.svg` nodes include `data-wiki-rgba` and `data-wiki-coord` attributes. This makes the visual context sheet and LLM Wiki pack share one deterministic coordinate system.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "Sneakoscope Codex",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.6",
|
|
5
5
|
"description": "Sneakoscope Codex: update-aware, database-safe Codex CLI harness with multi-agent Team orchestration, Ralph no-question execution, autoresearch-style loops, and H-Proof gates.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"repo-audit": "node ./scripts/repo-audit.mjs",
|
|
35
|
+
"postinstall": "node ./bin/sks.mjs postinstall",
|
|
35
36
|
"selftest": "node ./bin/sks.mjs selftest --mock",
|
|
36
37
|
"doctor": "node ./bin/sks.mjs doctor",
|
|
37
38
|
"packcheck": "find bin src scripts -name '*.mjs' -print0 | xargs -0 -n1 node --check",
|
|
@@ -63,6 +64,8 @@
|
|
|
63
64
|
"hypothesis",
|
|
64
65
|
"discovery",
|
|
65
66
|
"llm-wiki",
|
|
67
|
+
"wiki-coordinate",
|
|
68
|
+
"context-compression",
|
|
66
69
|
"gx",
|
|
67
70
|
"svg",
|
|
68
71
|
"deterministic",
|
package/src/cli/main.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fsp from 'node:fs/promises';
|
|
3
|
+
import readline from 'node:readline/promises';
|
|
4
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
3
5
|
import { projectRoot, readJson, writeJsonAtomic, writeTextAtomic, appendJsonlBounded, nowIso, exists, ensureDir, tmpdir, packageRoot, dirSize, formatBytes, which, runProcess, PACKAGE_VERSION } from '../core/fsx.mjs';
|
|
4
6
|
import { initProject, normalizeInstallScope, sksCommandPrefix } from '../core/init.mjs';
|
|
5
7
|
import { getCodexInfo, runCodexExec } from '../core/codex-adapter.mjs';
|
|
@@ -13,13 +15,15 @@ import { storageReport, enforceRetention } from '../core/retention.mjs';
|
|
|
13
15
|
import { classifySql, classifyCommand, loadDbSafetyPolicy, safeSupabaseMcpConfig, checkSqlFile, checkDbOperation, scanDbSafety } from '../core/db-safety.mjs';
|
|
14
16
|
import { rustInfo } from '../core/rust-accelerator.mjs';
|
|
15
17
|
import { renderCartridge, validateCartridge, driftCartridge, snapshotCartridge } from '../core/gx-renderer.mjs';
|
|
16
|
-
import { DEFAULT_EVAL_THRESHOLDS, compareEvaluationReports, runEvaluationBenchmark } from '../core/evaluation.mjs';
|
|
18
|
+
import { DEFAULT_EVAL_THRESHOLDS, compareEvaluationReports, defaultEvaluationScenario, runEvaluationBenchmark } from '../core/evaluation.mjs';
|
|
17
19
|
import { buildResearchPrompt, evaluateResearchGate, writeMockResearchResult, writeResearchPlan } from '../core/research.mjs';
|
|
20
|
+
import { contextCapsule } from '../core/triwiki-attention.mjs';
|
|
21
|
+
import { rgbaKey, rgbaToWikiCoord, validateWikiCoordinateIndex } from '../core/wiki-coordinate.mjs';
|
|
18
22
|
|
|
19
23
|
const flag = (args, name) => args.includes(name);
|
|
20
24
|
const promptOf = (args) => args.filter((x) => !String(x).startsWith('--')).join(' ').trim();
|
|
21
25
|
const REPOSITORY_URL = 'https://github.com/mandarange/Sneakoscope-Codex.git';
|
|
22
|
-
const USAGE_TOPICS = 'install|setup|team|ralph|research|db|codex-app|df|dollar|eval|gx';
|
|
26
|
+
const USAGE_TOPICS = 'install|setup|team|ralph|research|db|codex-app|df|dollar|eval|gx|wiki';
|
|
23
27
|
|
|
24
28
|
const DOLLAR_COMMANDS = [
|
|
25
29
|
{ command: '$DF', route: 'fast design/content fix', description: 'Small UI/content edits such as text color, copy, label, spacing, or translation. Avoids heavy loops.' },
|
|
@@ -37,10 +41,11 @@ const COMMAND_CATALOG = [
|
|
|
37
41
|
{ name: 'help', usage: 'sks help [topic]', description: 'Show CLI help or focused help for a topic.' },
|
|
38
42
|
{ name: 'version', usage: 'sks version | sks --version', description: 'Print the installed Sneakoscope Codex version.' },
|
|
39
43
|
{ name: 'update-check', usage: 'sks update-check [--json]', description: 'Check npm for the latest Sneakoscope Codex version.' },
|
|
44
|
+
{ name: 'wizard', usage: 'sks wizard', description: 'Open an interactive setup UI for install scope, setup, doctor, and verification.' },
|
|
40
45
|
{ name: 'commands', usage: 'sks commands [--json]', description: 'List every user-facing command with a short description.' },
|
|
41
46
|
{ name: 'usage', usage: `sks usage [${USAGE_TOPICS}]`, description: 'Print copy-ready workflows for common tasks.' },
|
|
42
47
|
{ name: 'quickstart', usage: 'sks quickstart', description: 'Show the shortest safe setup and verification flow.' },
|
|
43
|
-
{ name: 'install-prompt', usage: 'sks install-prompt [--project]', description: 'Print
|
|
48
|
+
{ name: 'install-prompt', usage: 'sks install-prompt [--project] [--full]', description: 'Print a short LLM-ready prompt that installs and configures SKS automatically.' },
|
|
44
49
|
{ name: 'codex-app', usage: 'sks codex-app', description: 'Show Codex App setup files and example prompts.' },
|
|
45
50
|
{ name: 'dollar-commands', usage: 'sks dollar-commands [--json]', description: 'List Codex App $ commands such as $DF.' },
|
|
46
51
|
{ name: 'df', usage: 'sks df', description: 'Explain $DF fast design/content fix mode.' },
|
|
@@ -54,6 +59,7 @@ const COMMAND_CATALOG = [
|
|
|
54
59
|
{ name: 'research', usage: 'sks research prepare|run|status ...', description: 'Run frontier-style research missions with novelty and falsification gates.' },
|
|
55
60
|
{ name: 'db', usage: 'sks db policy|scan|mcp-config|classify|check ...', description: 'Inspect and enforce database/Supabase safety policy.' },
|
|
56
61
|
{ name: 'eval', usage: 'sks eval run|compare|thresholds ...', description: 'Run deterministic context-quality and performance evidence checks.' },
|
|
62
|
+
{ name: 'wiki', usage: 'sks wiki coords|pack|validate ...', description: 'Build and validate RGBA/trig LLM Wiki coordinate context packs.' },
|
|
57
63
|
{ name: 'hproof', usage: 'sks hproof check [mission-id|latest]', description: 'Evaluate the H-Proof done gate for a mission.' },
|
|
58
64
|
{ name: 'team', usage: 'sks team "task" [--json]', description: 'Create a Codex multi-agent Team mission with consensus and implementation phases.' },
|
|
59
65
|
{ name: 'gx', usage: 'sks gx init|render|validate|drift|snapshot [name]', description: 'Create and verify deterministic SVG/HTML visual context cartridges.' },
|
|
@@ -73,8 +79,11 @@ function installScopeFromArgs(args = [], fallback = 'global') {
|
|
|
73
79
|
export async function main(args) {
|
|
74
80
|
const [cmd, sub, ...rest] = args;
|
|
75
81
|
const tail = sub === undefined ? [] : [sub, ...rest];
|
|
76
|
-
if (!cmd
|
|
82
|
+
if (!cmd) return shouldShowWizard() ? wizard([]) : help();
|
|
83
|
+
if (cmd === '--help' || cmd === '-h') return help();
|
|
77
84
|
if (cmd === '--version' || cmd === '-v' || cmd === 'version') return version();
|
|
85
|
+
if (cmd === 'postinstall') return postinstall();
|
|
86
|
+
if (cmd === 'wizard' || cmd === 'ui') return wizard(tail);
|
|
78
87
|
if (cmd === 'update-check') return updateCheck(tail);
|
|
79
88
|
if (cmd === 'help') return help(tail);
|
|
80
89
|
if (cmd === 'commands') return commands(tail);
|
|
@@ -100,6 +109,7 @@ export async function main(args) {
|
|
|
100
109
|
if (cmd === 'team') return team(tail);
|
|
101
110
|
if (cmd === 'db') return db(sub, rest);
|
|
102
111
|
if (cmd === 'eval') return evalCommand(sub, rest);
|
|
112
|
+
if (cmd === 'wiki') return wiki(sub, rest);
|
|
103
113
|
if (cmd === 'gc') return gc(tail);
|
|
104
114
|
if (cmd === 'stats') return stats(tail);
|
|
105
115
|
console.error(`Unknown command: ${cmd}`);
|
|
@@ -115,10 +125,11 @@ Usage:
|
|
|
115
125
|
sks help [topic]
|
|
116
126
|
sks version
|
|
117
127
|
sks update-check [--json]
|
|
128
|
+
sks wizard
|
|
118
129
|
sks commands [--json]
|
|
119
130
|
sks usage [${USAGE_TOPICS}]
|
|
120
131
|
sks quickstart
|
|
121
|
-
sks install-prompt [--project]
|
|
132
|
+
sks install-prompt [--project] [--full]
|
|
122
133
|
sks codex-app
|
|
123
134
|
sks dollar-commands [--json]
|
|
124
135
|
sks df
|
|
@@ -143,6 +154,9 @@ Usage:
|
|
|
143
154
|
sks db check --command "supabase db reset"
|
|
144
155
|
sks eval run [--json] [--out report.json]
|
|
145
156
|
sks eval compare --baseline old.json --candidate new.json [--json]
|
|
157
|
+
sks wiki coords --rgba 12,34,56,255
|
|
158
|
+
sks wiki pack [--json] [--role worker|verifier] [--max-anchors N]
|
|
159
|
+
sks wiki validate [context-pack.json]
|
|
146
160
|
sks gx init [name]
|
|
147
161
|
sks gx render [name] [--format svg|html|all]
|
|
148
162
|
sks gx validate [name]
|
|
@@ -166,6 +180,75 @@ function version() {
|
|
|
166
180
|
console.log(`sneakoscope ${PACKAGE_VERSION}`);
|
|
167
181
|
}
|
|
168
182
|
|
|
183
|
+
function shouldShowWizard() {
|
|
184
|
+
return Boolean(input.isTTY && output.isTTY && process.env.SKS_NO_WIZARD !== '1' && process.env.CI !== 'true');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function postinstall() {
|
|
188
|
+
console.log('\nSneakoscope Codex installed.');
|
|
189
|
+
console.log('Run `sks` to open the interactive setup UI, or run `sks setup` for the default global setup.');
|
|
190
|
+
console.log('Project-only setup: `sks wizard` -> choose project, or `npx sks setup --install-scope project`.\n');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function wizard(args = []) {
|
|
194
|
+
if (!shouldShowWizard() && !flag(args, '--force')) return help();
|
|
195
|
+
const rl = readline.createInterface({ input, output });
|
|
196
|
+
try {
|
|
197
|
+
console.log('Sneakoscope Codex Setup UI\n');
|
|
198
|
+
console.log(`Current package: ${PACKAGE_VERSION}`);
|
|
199
|
+
const latest = await npmPackageVersion('sneakoscope');
|
|
200
|
+
if (latest.version) {
|
|
201
|
+
const needsUpdate = compareVersions(latest.version, PACKAGE_VERSION) > 0;
|
|
202
|
+
console.log(`Latest on npm: ${latest.version}${needsUpdate ? ' (update available)' : ''}`);
|
|
203
|
+
if (needsUpdate) {
|
|
204
|
+
const update = await askChoice(rl, 'Update SKS before setup?', ['yes', 'no'], 'yes');
|
|
205
|
+
if (update === 'yes') {
|
|
206
|
+
console.log('\nRun this update command, then rerun `sks`:');
|
|
207
|
+
console.log(' npm i -g sneakoscope\n');
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
console.log('Skipping update for this setup run.\n');
|
|
211
|
+
}
|
|
212
|
+
} else if (latest.error) {
|
|
213
|
+
console.log(`Latest on npm: unknown (${latest.error})`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const scope = await askChoice(rl, 'Install scope for this project?', ['global', 'project', 'commands', 'quit'], 'global');
|
|
217
|
+
if (scope === 'quit') return;
|
|
218
|
+
if (scope === 'commands') {
|
|
219
|
+
quickstart();
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (scope === 'project') {
|
|
223
|
+
console.log('\nProject-only setup needs the package installed in this project:');
|
|
224
|
+
console.log(' npm i -D sneakoscope');
|
|
225
|
+
const proceed = await askChoice(rl, 'Continue with project setup after that dependency exists?', ['yes', 'no'], 'no');
|
|
226
|
+
if (proceed !== 'yes') return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const runSetup = await askChoice(rl, `Run sks setup with ${scope} scope now?`, ['yes', 'no'], 'yes');
|
|
230
|
+
if (runSetup === 'yes') await setup(['--install-scope', scope]);
|
|
231
|
+
const runDoctor = await askChoice(rl, 'Run sks doctor --fix now?', ['yes', 'no'], 'yes');
|
|
232
|
+
if (runDoctor === 'yes') await doctor(['--fix', '--install-scope', scope]);
|
|
233
|
+
const runSelftest = await askChoice(rl, 'Run sks selftest --mock now?', ['yes', 'no'], 'yes');
|
|
234
|
+
if (runSelftest === 'yes') await selftest(['--mock']);
|
|
235
|
+
console.log('\nSetup UI complete. Useful next commands:');
|
|
236
|
+
console.log(' sks commands');
|
|
237
|
+
console.log(' sks dollar-commands');
|
|
238
|
+
console.log(' sks codex-app');
|
|
239
|
+
} finally {
|
|
240
|
+
rl.close();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function askChoice(rl, question, choices, fallback) {
|
|
245
|
+
const suffix = choices.map((c) => c === fallback ? c.toUpperCase() : c).join('/');
|
|
246
|
+
const raw = (await rl.question(`${question} [${suffix}] `)).trim().toLowerCase();
|
|
247
|
+
const value = raw || fallback;
|
|
248
|
+
const hit = choices.find((c) => c.toLowerCase() === value || c[0].toLowerCase() === value);
|
|
249
|
+
return hit || fallback;
|
|
250
|
+
}
|
|
251
|
+
|
|
169
252
|
async function updateCheck(args = []) {
|
|
170
253
|
const latest = await npmPackageVersion('sneakoscope');
|
|
171
254
|
const result = {
|
|
@@ -226,6 +309,7 @@ function quickstart() {
|
|
|
226
309
|
|
|
227
310
|
Install from npm:
|
|
228
311
|
npm i -g sneakoscope
|
|
312
|
+
sks
|
|
229
313
|
|
|
230
314
|
Initialize this project for CLI and Codex App:
|
|
231
315
|
sks setup
|
|
@@ -249,9 +333,48 @@ GitHub install for unreleased commits:
|
|
|
249
333
|
|
|
250
334
|
function installPrompt(args = []) {
|
|
251
335
|
const projectOnly = flag(args, '--project');
|
|
336
|
+
const full = flag(args, '--full');
|
|
252
337
|
const install = projectOnly
|
|
253
338
|
? `npm i -D sneakoscope\nnpx sks setup --install-scope project`
|
|
254
339
|
: `npm i -g sneakoscope\nsks setup`;
|
|
340
|
+
if (full) return installPromptFull({ projectOnly, install });
|
|
341
|
+
const command = projectOnly ? 'npx sks' : 'sks';
|
|
342
|
+
const doctor = projectOnly ? 'npx sks doctor --fix --install-scope project' : 'sks doctor --fix';
|
|
343
|
+
const fallback = projectOnly
|
|
344
|
+
? 'If `npx sks` is unavailable, run `npm i -D sneakoscope` again and stop with the error.'
|
|
345
|
+
: 'If `sks` is unavailable after install, replace `sks` with `npx -y -p sneakoscope sks` and continue.';
|
|
346
|
+
|
|
347
|
+
console.log(`Copy this prompt into an LLM coding assistant:
|
|
348
|
+
|
|
349
|
+
Install Sneakoscope Codex in this project.
|
|
350
|
+
|
|
351
|
+
Rules:
|
|
352
|
+
- Do not modify application source files.
|
|
353
|
+
- Ask only when a command requires user approval.
|
|
354
|
+
- If Node.js is below 20.11, stop and report it.
|
|
355
|
+
- If Codex CLI is missing, report: install @openai/codex or set SKS_CODEX_BIN.
|
|
356
|
+
|
|
357
|
+
Run exactly:
|
|
358
|
+
\`\`\`bash
|
|
359
|
+
node -v
|
|
360
|
+
${install}
|
|
361
|
+
${command} update-check
|
|
362
|
+
${doctor}
|
|
363
|
+
${command} selftest --mock
|
|
364
|
+
${command} commands
|
|
365
|
+
${command} dollar-commands
|
|
366
|
+
\`\`\`
|
|
367
|
+
|
|
368
|
+
${fallback}
|
|
369
|
+
|
|
370
|
+
Finish with only:
|
|
371
|
+
- setup passed/failed
|
|
372
|
+
- Codex CLI present/missing
|
|
373
|
+
- generated files: \`.sneakoscope/\`, \`.codex/config.toml\`, \`.codex/hooks.json\`, \`.codex/skills/\`, \`.codex/agents/\`, \`.codex/SNEAKOSCOPE.md\`, \`AGENTS.md\`
|
|
374
|
+
`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function installPromptFull({ projectOnly, install }) {
|
|
255
378
|
console.log(`Copy this prompt into an LLM coding assistant:
|
|
256
379
|
|
|
257
380
|
Install Sneakoscope Codex in the current project end to end. Do not ask follow-up questions unless a command requires user approval.
|
|
@@ -404,6 +527,7 @@ function usage(args = []) {
|
|
|
404
527
|
Discovery:
|
|
405
528
|
sks help
|
|
406
529
|
sks update-check
|
|
530
|
+
sks wizard
|
|
407
531
|
sks commands
|
|
408
532
|
sks quickstart
|
|
409
533
|
sks install-prompt
|
|
@@ -416,6 +540,7 @@ Common workflows:
|
|
|
416
540
|
sks usage ralph
|
|
417
541
|
sks usage research
|
|
418
542
|
sks usage db
|
|
543
|
+
sks usage wiki
|
|
419
544
|
sks usage df
|
|
420
545
|
`,
|
|
421
546
|
install: `Install and Setup
|
|
@@ -561,8 +686,10 @@ CLI help:
|
|
|
561
686
|
Use inside Codex App or an agent prompt:
|
|
562
687
|
$DF fast design/content fix
|
|
563
688
|
$SKS general Sneakoscope route
|
|
689
|
+
$Team multi-agent team route
|
|
564
690
|
$Ralph Ralph mission route
|
|
565
691
|
$Research research mission route
|
|
692
|
+
$AutoResearch iterative experiment route
|
|
566
693
|
$DB database safety route
|
|
567
694
|
$GX visual context route
|
|
568
695
|
$Help command help route
|
|
@@ -582,6 +709,27 @@ Compare reports:
|
|
|
582
709
|
|
|
583
710
|
Show thresholds:
|
|
584
711
|
sks eval thresholds
|
|
712
|
+
`,
|
|
713
|
+
wiki: `LLM Wiki Context Continuity
|
|
714
|
+
|
|
715
|
+
Convert RGBA channels to deterministic wiki coordinates:
|
|
716
|
+
sks wiki coords --rgba 12,34,56,255
|
|
717
|
+
|
|
718
|
+
Build a hydratable context pack:
|
|
719
|
+
sks wiki pack
|
|
720
|
+
sks wiki pack --json --role verifier --max-anchors 48
|
|
721
|
+
|
|
722
|
+
Validate a saved pack:
|
|
723
|
+
sks wiki validate
|
|
724
|
+
sks wiki validate .sneakoscope/wiki/context-pack.json
|
|
725
|
+
|
|
726
|
+
Model:
|
|
727
|
+
R -> domain angle
|
|
728
|
+
G -> layer radius through sin()
|
|
729
|
+
B -> phase angle
|
|
730
|
+
A -> concentration/confidence
|
|
731
|
+
|
|
732
|
+
TriWiki keeps selected claims as text and preserves the rest as anchor ids, RGBA keys, coordinate tuples, source pointers, and hashes so later turns can hydrate the needed context instead of relying on lossy summaries.
|
|
585
733
|
`,
|
|
586
734
|
gx: `GX Visual Context
|
|
587
735
|
|
|
@@ -1122,6 +1270,20 @@ async function selftest() {
|
|
|
1122
1270
|
if (nonDbDecision.action !== 'allow') throw new Error('selftest failed: non-DB command blocked by DB guard');
|
|
1123
1271
|
const evalReport = runEvaluationBenchmark({ iterations: 5 });
|
|
1124
1272
|
if (!evalReport.comparison.meaningful_improvement) throw new Error('selftest failed: evaluation benchmark did not show meaningful improvement');
|
|
1273
|
+
if (!evalReport.candidate.wiki?.valid) throw new Error('selftest failed: wiki coordinate index invalid in eval');
|
|
1274
|
+
const coord = rgbaToWikiCoord({ r: 12, g: 34, b: 56, a: 255 });
|
|
1275
|
+
if (coord.schema !== 'sks.wiki-coordinate.v1' || coord.xyzw.length !== 4) throw new Error('selftest failed: RGBA wiki coordinate conversion');
|
|
1276
|
+
const wikiPack = contextCapsule({
|
|
1277
|
+
mission: { id: 'selftest-wiki', coord: { rgba: { r: 48, g: 132, b: 212, a: 240 } } },
|
|
1278
|
+
role: 'verifier',
|
|
1279
|
+
claims: await projectWikiClaims(tmp),
|
|
1280
|
+
q4: { mode: 'selftest' },
|
|
1281
|
+
q3: ['sks', 'llm-wiki', 'wiki-coordinate'],
|
|
1282
|
+
budget: { maxWikiAnchors: 48 }
|
|
1283
|
+
});
|
|
1284
|
+
const wikiValidation = validateWikiCoordinateIndex(wikiPack.wiki);
|
|
1285
|
+
if (!wikiValidation.ok) throw new Error('selftest failed: wiki coordinate pack invalid');
|
|
1286
|
+
if (!(wikiPack.wiki.anchors || wikiPack.wiki.a || []).some((anchor) => (Array.isArray(anchor) ? anchor[0] : anchor.id) === 'wiki-trig')) throw new Error('selftest failed: wiki trig anchor missing');
|
|
1125
1287
|
const { dir: researchDir, mission: researchMission } = await createMission(tmp, { mode: 'research', prompt: '새로운 코드 리뷰 방법론 연구' });
|
|
1126
1288
|
const researchPlan = await writeResearchPlan(researchDir, researchMission.prompt, {});
|
|
1127
1289
|
const researchGate = await writeMockResearchResult(researchDir, researchPlan);
|
|
@@ -1136,10 +1298,12 @@ async function selftest() {
|
|
|
1136
1298
|
if (!render.outputs.includes('render.svg')) throw new Error('selftest failed: gx svg not rendered');
|
|
1137
1299
|
const validation = await validateCartridge(gxDir);
|
|
1138
1300
|
if (!validation.ok) throw new Error('selftest failed: gx validation rejected');
|
|
1301
|
+
if (!validateWikiCoordinateIndex(validation.wiki_coordinates).ok) throw new Error('selftest failed: gx wiki coordinate validation rejected');
|
|
1139
1302
|
const drift = await driftCartridge(gxDir);
|
|
1140
1303
|
if (drift.status !== 'low') throw new Error('selftest failed: gx drift is high');
|
|
1141
1304
|
const snapshot = await snapshotCartridge(gxDir);
|
|
1142
1305
|
if (!snapshot.files.svg || !snapshot.files.html) throw new Error('selftest failed: gx snapshot incomplete');
|
|
1306
|
+
if (!validateWikiCoordinateIndex(snapshot.wiki_coordinates).ok) throw new Error('selftest failed: gx snapshot wiki coordinates invalid');
|
|
1143
1307
|
const gc = await enforceRetention(tmp, { dryRun: true });
|
|
1144
1308
|
if (!gc.report.exists) throw new Error('selftest failed: storage report');
|
|
1145
1309
|
console.log('Sneakoscope Codex selftest passed.');
|
|
@@ -1191,6 +1355,91 @@ async function evalCommand(sub, args) {
|
|
|
1191
1355
|
process.exitCode = 1;
|
|
1192
1356
|
}
|
|
1193
1357
|
|
|
1358
|
+
async function wiki(sub, args = []) {
|
|
1359
|
+
if (!sub || sub === 'help' || sub === '--help') {
|
|
1360
|
+
console.log('Usage: sks wiki coords --rgba R,G,B,A | sks wiki pack [--json] [--role worker|verifier] [--max-anchors N] | sks wiki validate [context-pack.json] [--json]');
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
if (sub === 'coords') {
|
|
1364
|
+
const raw = readFlagValue(args, '--rgba', positionalArgs(args)[0] || '');
|
|
1365
|
+
const parts = String(raw).split(/[,\s]+/).filter(Boolean).map((x) => Number.parseInt(x, 10));
|
|
1366
|
+
if (parts.length < 3) throw new Error('Usage: sks wiki coords --rgba R,G,B,A');
|
|
1367
|
+
const coord = rgbaToWikiCoord({ r: parts[0], g: parts[1], b: parts[2], a: parts[3] ?? 255 });
|
|
1368
|
+
console.log(JSON.stringify({ rgba: coord.rgba, rgba_key: rgbaKey(coord.rgba), coord }, null, 2));
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
if (sub === 'pack') {
|
|
1372
|
+
const root = await projectRoot();
|
|
1373
|
+
const role = readFlagValue(args, '--role', 'worker');
|
|
1374
|
+
const maxAnchors = Number(readFlagValue(args, '--max-anchors', role.includes('verifier') ? 48 : 32));
|
|
1375
|
+
const pack = contextCapsule({
|
|
1376
|
+
mission: { id: 'project-wiki', coord: { rgba: { r: 48, g: 132, b: 212, a: 240 } } },
|
|
1377
|
+
role,
|
|
1378
|
+
contractHash: null,
|
|
1379
|
+
claims: await projectWikiClaims(root),
|
|
1380
|
+
q4: { mode: 'project-continuity', package: PACKAGE_VERSION, hydrate: 'anchor-first' },
|
|
1381
|
+
q3: ['sks', 'llm-wiki', 'wiki-coordinate', 'gx', 'skills'],
|
|
1382
|
+
budget: { maxWikiAnchors: maxAnchors }
|
|
1383
|
+
});
|
|
1384
|
+
const file = path.join(root, '.sneakoscope', 'wiki', 'context-pack.json');
|
|
1385
|
+
await ensureDir(path.dirname(file));
|
|
1386
|
+
await writeJsonAtomic(file, pack);
|
|
1387
|
+
if (flag(args, '--json')) return console.log(JSON.stringify({ ...pack, path: file }, null, 2));
|
|
1388
|
+
console.log('Sneakoscope LLM Wiki Context Pack');
|
|
1389
|
+
console.log(`Path: ${path.relative(root, file)}`);
|
|
1390
|
+
console.log(`Claims: ${pack.claims.length} hydrated text claims`);
|
|
1391
|
+
console.log(`Anchors: ${(pack.wiki.anchors || pack.wiki.a || []).length} coordinate anchors (${pack.wiki.overflow_count ?? pack.wiki.o ?? 0} overflow)`);
|
|
1392
|
+
console.log(`Schema: ${pack.wiki.schema}`);
|
|
1393
|
+
console.log(`Validate: sks wiki validate ${path.relative(root, file)}`);
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
if (sub === 'validate') {
|
|
1397
|
+
const root = await projectRoot();
|
|
1398
|
+
const target = positionalArgs(args)[0] || path.join(root, '.sneakoscope', 'wiki', 'context-pack.json');
|
|
1399
|
+
const pack = await readJson(path.resolve(target));
|
|
1400
|
+
const result = validateWikiCoordinateIndex(pack.wiki || pack);
|
|
1401
|
+
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
1402
|
+
console.log(`Wiki coordinate index: ${result.ok ? 'ok' : 'failed'}`);
|
|
1403
|
+
console.log(`Anchors checked: ${result.checked}`);
|
|
1404
|
+
for (const issue of result.issues) console.log(`- ${issue.severity}: ${issue.id}${issue.anchor ? ` ${issue.anchor}` : ''}`);
|
|
1405
|
+
process.exitCode = result.ok ? 0 : 2;
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
console.error('Usage: sks wiki coords|pack|validate');
|
|
1409
|
+
process.exitCode = 1;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
async function projectWikiClaims(root) {
|
|
1413
|
+
const claims = [
|
|
1414
|
+
['wiki-hooks', '.codex/hooks.json routes UserPromptSubmit, tool, permission, and Stop events through SKS guards.', '.codex/hooks.json', 'code', 'high'],
|
|
1415
|
+
['wiki-config', '.codex/config.toml enables Codex App profiles, multi-agent support, and Team agent limits.', '.codex/config.toml', 'code', 'high'],
|
|
1416
|
+
['wiki-skills', '.codex/skills and .agents/skills provide local routes for DF, Team, Ralph, Research, AutoResearch, DB, GX, wiki, and evaluation workflows.', '.codex/skills', 'code', 'medium'],
|
|
1417
|
+
['wiki-agents', '.codex/agents defines Team planning, implementation, DB safety, and QA reviewer roles.', '.codex/agents', 'code', 'medium'],
|
|
1418
|
+
['wiki-policy', '.sneakoscope/policy.json stores update-check, honest-mode, retention, database, performance, and prompt-pipeline policy.', '.sneakoscope/policy.json', 'contract', 'high'],
|
|
1419
|
+
['wiki-memory', '.sneakoscope/memory stores Q0 raw, Q1 evidence, Q2 facts, Q3 tags, and Q4 control bits for hydratable context.', '.sneakoscope/memory', 'wiki', 'high'],
|
|
1420
|
+
['wiki-gx', 'GX cartridges keep vgraph.json and beta.json as deterministic visual context sources with render, validation, drift, and snapshot outputs.', '.sneakoscope/gx/cartridges', 'vgraph', 'medium'],
|
|
1421
|
+
['wiki-db', 'Database safety blocks destructive SQL, risky Supabase commands, unsafe MCP writes, and production data mutation.', '.sneakoscope/db-safety.json', 'code', 'critical'],
|
|
1422
|
+
['wiki-hproof', 'H-Proof blocks completion when unsupported critical claims, DB safety issues, missing tests, or high visual/wiki drift remain.', '.sneakoscope/hproof', 'test', 'critical'],
|
|
1423
|
+
['wiki-eval', 'sks eval run measures token savings, evidence-weighted accuracy proxy, required recall, unsupported critical filtering, and build runtime.', 'src/core/evaluation.mjs', 'test', 'medium'],
|
|
1424
|
+
['wiki-trig', 'TriWiki maps RGBA channels to domain angle, layer radius, phase, and concentration using deterministic trigonometric coordinates.', 'src/core/wiki-coordinate.mjs', 'code', 'high']
|
|
1425
|
+
];
|
|
1426
|
+
const out = [];
|
|
1427
|
+
for (const [id, text, file, authority, risk] of claims) {
|
|
1428
|
+
out.push({
|
|
1429
|
+
id,
|
|
1430
|
+
text,
|
|
1431
|
+
authority,
|
|
1432
|
+
risk,
|
|
1433
|
+
status: await exists(path.join(root, file)) ? 'supported' : 'unknown',
|
|
1434
|
+
freshness: 'fresh',
|
|
1435
|
+
source: file,
|
|
1436
|
+
file,
|
|
1437
|
+
evidence_count: await exists(path.join(root, file)) ? 1 : 0
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
return out;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1194
1443
|
async function saveEvalReport(root, args, report, prefix) {
|
|
1195
1444
|
if (flag(args, '--no-save')) return null;
|
|
1196
1445
|
const requested = readFlagValue(args, '--out', null);
|
|
@@ -1214,6 +1463,7 @@ function printEvalRun(report, saved) {
|
|
|
1214
1463
|
console.log(`Accuracy: ${report.baseline.quality.accuracy_proxy} -> ${report.candidate.quality.accuracy_proxy} (${c.accuracy_delta >= 0 ? '+' : ''}${c.accuracy_delta})`);
|
|
1215
1464
|
console.log(`Recall: ${report.candidate.quality.required_recall}`);
|
|
1216
1465
|
console.log(`Precision: ${report.baseline.quality.relevance_precision} -> ${report.candidate.quality.relevance_precision}`);
|
|
1466
|
+
if (report.candidate.wiki) console.log(`Wiki: ${report.candidate.wiki.anchors} anchors, valid=${report.candidate.wiki.valid}`);
|
|
1217
1467
|
console.log(`Build ms: ${report.baseline.context_build_ms_per_run} -> ${report.candidate.context_build_ms_per_run}`);
|
|
1218
1468
|
console.log(`Meaningful improvement: ${c.meaningful_improvement ? 'yes' : 'no'}`);
|
|
1219
1469
|
if (saved) console.log(`Report: ${saved}`);
|
package/src/core/evaluation.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { nowIso, sha256 } from './fsx.mjs';
|
|
2
2
|
import { contextCapsule } from './triwiki-attention.mjs';
|
|
3
|
+
import { validateWikiCoordinateIndex } from './wiki-coordinate.mjs';
|
|
3
4
|
|
|
4
5
|
export const DEFAULT_EVAL_THRESHOLDS = Object.freeze({
|
|
5
6
|
min_token_savings_pct: 0.25,
|
|
@@ -34,6 +35,7 @@ export function defaultEvaluationScenario() {
|
|
|
34
35
|
['req-db-block', 'destructive database operations such as DROP, TRUNCATE, db reset, and live execute_sql writes are blocked.', 'code', 'critical', 1.4],
|
|
35
36
|
['req-hook', '.codex/hooks.json routes PreToolUse, PostToolUse, PermissionRequest, UserPromptSubmit, and Stop to sks hook handlers.', 'code', 'high', 1],
|
|
36
37
|
['req-gx-hash', 'GX render output embeds the normalized vgraph.json source hash so drift can be detected deterministically.', 'test', 'medium', 0.9],
|
|
38
|
+
['req-wiki-rgba', 'TriWiki stores RGBA-derived trigonometric wiki anchors so compressed context remains hydratable by id, hash, source, and coordinate.', 'code', 'high', 1.2],
|
|
37
39
|
['req-retention', 'runtime logs and mission artifacts are bounded through retention policy and sks gc.', 'code', 'medium', 0.85],
|
|
38
40
|
['req-selftest', 'selftest covers contract sealing, DB guard blocking, done-gate evaluation, GX render/validate/drift, snapshot, and retention report.', 'test', 'high', 1.1],
|
|
39
41
|
['req-skill', 'sks init installs local skills under .agents/skills so project workflows can trigger domain-specific guidance.', 'code', 'medium', 0.9],
|
|
@@ -43,6 +45,7 @@ export function defaultEvaluationScenario() {
|
|
|
43
45
|
['noise-rust-default', 'Rust acceleration is required at runtime for every npm installation.', 'unsupported', 'model', 'high'],
|
|
44
46
|
['noise-live-write', 'Live Supabase execute_sql writes are acceptable when the prompt sounds urgent.', 'unsupported', 'model', 'critical'],
|
|
45
47
|
['noise-image', 'GX visual context requires an external image generation service.', 'unsupported', 'model', 'medium'],
|
|
48
|
+
['noise-wiki-lossy', 'TriWiki deletes non-selected claims permanently when it compresses context.', 'unsupported', 'wiki', 'high'],
|
|
46
49
|
['noise-unbounded', 'Raw child process output is kept entirely in memory for later summarization.', 'unsupported', 'model', 'high'],
|
|
47
50
|
['noise-hooks-only', 'Hooks are the only source of truth and mission files are optional.', 'unsupported', 'wiki', 'high'],
|
|
48
51
|
['noise-package', 'The npm package bundles @openai/codex and native Rust binaries.', 'unsupported', 'model', 'medium'],
|
|
@@ -134,11 +137,18 @@ function scoreSelection(allClaims, selectedIds) {
|
|
|
134
137
|
|
|
135
138
|
function metricBlock({ label, context, scenario, msPerRun }) {
|
|
136
139
|
const selectedIds = (context.claims || []).map((claim) => claim.id);
|
|
140
|
+
const wikiValidation = context.wiki ? validateWikiCoordinateIndex(context.wiki) : null;
|
|
137
141
|
return {
|
|
138
142
|
label,
|
|
139
143
|
context_hash: sha256(JSON.stringify(context)),
|
|
140
144
|
estimated_tokens: estimateTokens(context),
|
|
141
145
|
context_build_ms_per_run: Number(msPerRun.toFixed(4)),
|
|
146
|
+
wiki: context.wiki ? {
|
|
147
|
+
schema: context.wiki.schema,
|
|
148
|
+
anchors: (context.wiki.anchors || context.wiki.a || []).length,
|
|
149
|
+
overflow_count: context.wiki.overflow_count ?? context.wiki.o ?? 0,
|
|
150
|
+
valid: wikiValidation.ok
|
|
151
|
+
} : null,
|
|
142
152
|
quality: scoreSelection(scenario.claims, selectedIds)
|
|
143
153
|
};
|
|
144
154
|
}
|
|
@@ -158,6 +168,7 @@ export function compareMetricBlocks(baseline, candidate, thresholds = DEFAULT_EV
|
|
|
158
168
|
unsupported_critical: candidate.quality.unsupported_critical_selected <= thresholds.max_unsupported_critical_selected,
|
|
159
169
|
candidate_build_time: candidate.context_build_ms_per_run <= thresholds.max_candidate_build_ms_per_run
|
|
160
170
|
};
|
|
171
|
+
if (candidate.wiki) checks.wiki_index = candidate.wiki.valid === true;
|
|
161
172
|
return {
|
|
162
173
|
token_savings_pct: Number(tokenSavingsPct.toFixed(4)),
|
|
163
174
|
accuracy_delta: Number(accuracyDelta.toFixed(4)),
|
package/src/core/fsx.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.6.
|
|
8
|
+
export const PACKAGE_VERSION = '0.6.6';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
package/src/core/gx-renderer.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { exists, nowIso, readJson, readText, sha256, writeJsonAtomic, writeTextAtomic } from './fsx.mjs';
|
|
3
|
+
import { buildWikiCoordinateIndex, normalizeWikiCoord, rgbaKey } from './wiki-coordinate.mjs';
|
|
3
4
|
|
|
4
5
|
const SVG_WIDTH = 1280;
|
|
5
6
|
const SVG_HEIGHT = 820;
|
|
@@ -89,6 +90,22 @@ export function vgraphHash(vgraph = {}) {
|
|
|
89
90
|
return sha256(stableJson(normalizeVGraph(vgraph)));
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
function nodeWikiCoord(node) {
|
|
94
|
+
return normalizeWikiCoord(node?.coord || {}, `node:${node?.id || node?.label || 'node'}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function nodeClaims(graph) {
|
|
98
|
+
return graph.nodes.map((node) => ({
|
|
99
|
+
id: `node:${node.id}`,
|
|
100
|
+
text: node.label,
|
|
101
|
+
authority: 'vgraph',
|
|
102
|
+
source: 'vgraph.json',
|
|
103
|
+
status: node.status === 'blocked' ? 'unsupported' : 'supported',
|
|
104
|
+
risk: node.risk === 'critical' ? 'critical' : (node.risk === 'high' ? 'high' : 'medium'),
|
|
105
|
+
coord: nodeWikiCoord(node)
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
|
|
92
109
|
function nodePalette(node) {
|
|
93
110
|
if (node.risk === 'critical' || node.status === 'blocked') return { fill: '#fee2e2', stroke: '#b91c1c', text: '#3b0a0a' };
|
|
94
111
|
if (node.risk === 'high' || node.status === 'warn') return { fill: '#ffedd5', stroke: '#c2410c', text: '#431407' };
|
|
@@ -166,13 +183,20 @@ export function renderVGraphSvg(vgraph = {}, beta = {}) {
|
|
|
166
183
|
const palette = nodePalette(node);
|
|
167
184
|
const labelLines = splitLabel(node.label);
|
|
168
185
|
const tag = `${node.kind} / ${node.status}`;
|
|
169
|
-
|
|
186
|
+
const wikiCoord = nodeWikiCoord(node);
|
|
187
|
+
return `<g class="node" data-wiki-rgba="${rgbaKey(wikiCoord.rgba)}" data-wiki-coord="${wikiCoord.domainAngle},${wikiCoord.layerRadius},${wikiCoord.phase},${wikiCoord.concentration}" transform="translate(${pos.x - pos.w / 2} ${pos.y - pos.h / 2})">
|
|
170
188
|
<rect width="${pos.w}" height="${pos.h}" rx="14" fill="${palette.fill}" stroke="${palette.stroke}" stroke-width="3"/>
|
|
171
189
|
<text x="18" y="27" class="node-title" fill="${palette.text}">${escapeXml(labelLines[0])}</text>
|
|
172
190
|
${labelLines.slice(1).map((line, i) => `<text x="18" y="${49 + i * 18}" class="node-title small" fill="${palette.text}">${escapeXml(line)}</text>`).join('\n')}
|
|
173
191
|
<text x="18" y="${pos.h - 14}" class="node-meta" fill="${palette.text}">${escapeXml(shortText(tag, 34))}</text>
|
|
174
192
|
</g>`;
|
|
175
193
|
}).join('\n');
|
|
194
|
+
const wikiStrip = graph.nodes.slice(0, 24).map((node, index) => {
|
|
195
|
+
const coord = nodeWikiCoord(node);
|
|
196
|
+
const x = 1010 + (index % 12) * 18;
|
|
197
|
+
const y = 36 + Math.floor(index / 12) * 18;
|
|
198
|
+
return `<rect x="${x}" y="${y}" width="14" height="14" rx="2" fill="rgb(${coord.rgba.r},${coord.rgba.g},${coord.rgba.b})" fill-opacity="${(coord.rgba.a / 255).toFixed(3)}" data-node="${escapeXml(node.id)}" data-rgba="${rgbaKey(coord.rgba)}"/>`;
|
|
199
|
+
}).join('\n');
|
|
176
200
|
const emptyState = graph.nodes.length ? '' : `<g class="empty">
|
|
177
201
|
<rect x="332" y="214" width="616" height="178" rx="18"/>
|
|
178
202
|
<text x="640" y="291" text-anchor="middle" class="empty-title">No graph nodes defined</text>
|
|
@@ -206,6 +230,7 @@ export function renderVGraphSvg(vgraph = {}, beta = {}) {
|
|
|
206
230
|
<rect x="0" y="0" width="${SVG_WIDTH}" height="108" fill="#f8fafc"/>
|
|
207
231
|
<text x="42" y="56" class="title">${escapeXml(graph.title)}</text>
|
|
208
232
|
<text x="42" y="86" class="subtitle">vgraph:${escapeXml(graph.id)} hash:${hash.slice(0, 12)} nodes:${graph.nodes.length} edges:${graph.edges.length} generated:${generatedAt}</text>
|
|
233
|
+
<g aria-label="RGBA wiki coordinate atlas">${wikiStrip}</g>
|
|
209
234
|
${layerBands}
|
|
210
235
|
${edgeLines}
|
|
211
236
|
${nodeCards}
|
|
@@ -268,6 +293,13 @@ export function validateVGraph(vgraph = {}, beta = {}) {
|
|
|
268
293
|
graph_id: graph.id,
|
|
269
294
|
source_hash: vgraphHash(graph),
|
|
270
295
|
counts: { nodes: graph.nodes.length, edges: graph.edges.length, invariants: graph.invariants.length, tests: graph.tests.length },
|
|
296
|
+
wiki_coordinates: buildWikiCoordinateIndex({
|
|
297
|
+
mission: { id: graph.id, coord: beta?.mission_coord || {} },
|
|
298
|
+
claims: nodeClaims(graph),
|
|
299
|
+
q4: { vgraph_hash: vgraphHash(graph) },
|
|
300
|
+
q3: ['gx', 'vgraph', 'wiki-coordinate'],
|
|
301
|
+
maxAnchors: Math.min(32, graph.nodes.length)
|
|
302
|
+
}),
|
|
271
303
|
issues,
|
|
272
304
|
warnings
|
|
273
305
|
};
|
|
@@ -344,6 +376,13 @@ export async function snapshotCartridge(dir) {
|
|
|
344
376
|
},
|
|
345
377
|
validation,
|
|
346
378
|
drift,
|
|
379
|
+
wiki_coordinates: buildWikiCoordinateIndex({
|
|
380
|
+
mission: { id: normalizeVGraph(vgraph).id, coord: beta?.mission_coord || {} },
|
|
381
|
+
claims: nodeClaims(normalizeVGraph(vgraph)),
|
|
382
|
+
q4: { vgraph_hash: vgraphHash(vgraph) },
|
|
383
|
+
q3: ['gx', 'vgraph', 'wiki-coordinate'],
|
|
384
|
+
maxAnchors: 32
|
|
385
|
+
}),
|
|
347
386
|
vgraph: normalizeVGraph(vgraph),
|
|
348
387
|
beta
|
|
349
388
|
};
|
|
@@ -65,6 +65,7 @@ function promptPipelineContext(prompt) {
|
|
|
65
65
|
const command = dollarCommand(prompt);
|
|
66
66
|
const fastDesign = command === 'DF' || looksLikeFastDesignFix(prompt);
|
|
67
67
|
const team = command === 'TEAM';
|
|
68
|
+
const wiki = command === 'GX' || /\b(llm wiki|wiki|context compression|context pack|hydrate|rgba|coordinate|좌표|컨텍스트|압축)\b/i.test(String(prompt || ''));
|
|
68
69
|
const autoresearch = command === 'AUTORESEARCH' || command === 'RESEARCH' || /\b(autoresearch|experiment|benchmark|hypothesis|research|optimi[sz]e|improve metric|falsify|novelty|SEO|GEO)\b/i.test(String(prompt || ''));
|
|
69
70
|
const route = command ? `$${command}` : (fastDesign ? '$DF inferred' : 'default');
|
|
70
71
|
const dfLine = fastDesign
|
|
@@ -76,7 +77,10 @@ function promptPipelineContext(prompt) {
|
|
|
76
77
|
const autoresearchLine = autoresearch
|
|
77
78
|
? '\nAutoResearch route: use an experiment loop with a clear program, fixed budget, metric, keep/discard decision, ledger, falsification, and next experiment.'
|
|
78
79
|
: '';
|
|
79
|
-
|
|
80
|
+
const wikiLine = wiki
|
|
81
|
+
? '\nLLM Wiki route: preserve context through TriWiki RGBA/trig coordinate anchors. Prefer sks wiki pack for hydratable context; keep ids, hashes, source paths, and coordinates for non-selected claims instead of lossy summaries.'
|
|
82
|
+
: '';
|
|
83
|
+
return `SKS prompt pipeline active. Route: ${route}. Before work, respect the SKS update-check context if present. Optimize the user request before acting: extract intent, target files/surfaces, constraints, acceptance criteria, and the smallest safe execution path. Use explicit $ commands when present: $DF fast design/content edit, $Team multi-agent orchestration, $Ralph clarification-gated mission, $Research discovery run, $AutoResearch iterative experiment loop, $DB database safety, $GX visual context, $SKS general SKS help. Without a command, infer the lightest matching route and avoid heavy loops unless the task requires them. Preserve multi-turn context with LLM Wiki coordinate packs when compression or continuity matters. Do not stop at a plan when implementation was requested; continue until the stated goal is actually handled or a hard blocker is honestly reported. Before final answer, perform SKS Honest Mode: verify evidence, list tests run or gaps, call out uncertainty, and confirm the goal is actually complete.${dfLine}${teamLine}${autoresearchLine}${wikiLine}`;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
export async function hookMain(name) {
|
package/src/core/init.mjs
CHANGED
|
@@ -64,6 +64,10 @@ When creating HTML, UI, prototype, deck-like, or visual artifacts, use the local
|
|
|
64
64
|
|
|
65
65
|
Every user prompt enters the SKS prompt optimization pipeline even when the user does not type a command. Extract intent, target files or surfaces, constraints, acceptance criteria, risks, and the smallest safe execution path before acting. Choose the lightest matching route: fast edit, normal implementation, Ralph, Research, DB safety, GX, or evaluation. Do not run heavy Ralph/research/evaluation loops for simple direct edits.
|
|
66
66
|
|
|
67
|
+
## LLM Wiki Continuity
|
|
68
|
+
|
|
69
|
+
TriWiki context is anchor-first, not lossy-summary-first. Important claims, visual nodes, policy facts, and evidence pointers should receive deterministic RGBA wiki coordinates: R maps to domain angle, G maps to layer radius through sine, B maps to phase angle, and A maps to concentration/confidence. Use those trigonometric coordinates to preserve stable retrieval anchors across turns. Selected claims may be pasted as text, but non-selected claims must remain hydratable through id, hash, source path, and RGBA coordinate anchors instead of disappearing from the workflow.
|
|
70
|
+
|
|
67
71
|
## Dollar Commands
|
|
68
72
|
|
|
69
73
|
Codex App users may invoke local SKS modes with skill-style dollar commands. \`$DF\` is the fast design/content fix route for small changes such as text color, copy edits, label changes, spacing tweaks, or translating visible text. \`$DF\` should avoid broad redesign, avoid unnecessary planning loops, and make the requested change directly with only cheap verification when useful.
|
|
@@ -79,7 +83,7 @@ When this repository is opened in Codex App, use the local Sneakoscope files as
|
|
|
79
83
|
3. vgraph.json
|
|
80
84
|
4. beta.json
|
|
81
85
|
5. GX render/snapshot metadata
|
|
82
|
-
6. LLM Wiki
|
|
86
|
+
6. LLM Wiki coordinate index
|
|
83
87
|
7. model knowledge only if explicitly allowed
|
|
84
88
|
|
|
85
89
|
## Database Safety
|
|
@@ -97,7 +101,7 @@ export async function initProject(root, opts = {}) {
|
|
|
97
101
|
const hookCommandPrefix = opts.hookCommandPrefix || sksCommandPrefix(installScope, { globalCommand: opts.globalCommand });
|
|
98
102
|
const sine = path.join(root, '.sneakoscope');
|
|
99
103
|
const dirs = [
|
|
100
|
-
'.sneakoscope/state', '.sneakoscope/missions', '.sneakoscope/db', '.sneakoscope/bus', '.sneakoscope/hproof', '.sneakoscope/db', '.sneakoscope/memory/q0_raw', '.sneakoscope/memory/q1_evidence', '.sneakoscope/memory/q2_facts', '.sneakoscope/memory/q3_tags', '.sneakoscope/memory/q4_bits', '.sneakoscope/gx/cartridges', '.sneakoscope/model/fingerprints', '.sneakoscope/genome/candidates', '.sneakoscope/trajectories/raw', '.sneakoscope/locks', '.sneakoscope/tmp', '.sneakoscope/arenas', '.sneakoscope/reports', '.codex', '.codex/skills', '.codex/agents', '.agents/skills'
|
|
104
|
+
'.sneakoscope/state', '.sneakoscope/missions', '.sneakoscope/db', '.sneakoscope/bus', '.sneakoscope/hproof', '.sneakoscope/db', '.sneakoscope/wiki', '.sneakoscope/memory/q0_raw', '.sneakoscope/memory/q1_evidence', '.sneakoscope/memory/q2_facts', '.sneakoscope/memory/q3_tags', '.sneakoscope/memory/q4_bits', '.sneakoscope/gx/cartridges', '.sneakoscope/model/fingerprints', '.sneakoscope/genome/candidates', '.sneakoscope/trajectories/raw', '.sneakoscope/locks', '.sneakoscope/tmp', '.sneakoscope/arenas', '.sneakoscope/reports', '.codex', '.codex/skills', '.codex/agents', '.agents/skills'
|
|
101
105
|
];
|
|
102
106
|
for (const d of dirs) await ensureDir(path.join(root, d));
|
|
103
107
|
|
|
@@ -129,6 +133,11 @@ export async function initProject(root, opts = {}) {
|
|
|
129
133
|
dollar_commands: ['$DF', '$SKS', '$Team', '$Ralph', '$Research', '$AutoResearch', '$DB', '$GX', '$Help'],
|
|
130
134
|
fast_design_command: '$DF'
|
|
131
135
|
},
|
|
136
|
+
llm_wiki: {
|
|
137
|
+
coordinate_schema: 'sks.wiki-coordinate.v1',
|
|
138
|
+
channel_map: { r: 'domainAngle', g: 'layerRadius', b: 'phase', a: 'concentration' },
|
|
139
|
+
continuity_model: 'selected_text_plus_hydratable_rgba_trig_anchors'
|
|
140
|
+
},
|
|
132
141
|
database_safety: 'destructive_db_operations_denied_always',
|
|
133
142
|
gx_renderer: 'deterministic_svg_html'
|
|
134
143
|
});
|
|
@@ -179,6 +188,12 @@ export async function initProject(root, opts = {}) {
|
|
|
179
188
|
min_required_recall: 0.95
|
|
180
189
|
}
|
|
181
190
|
},
|
|
191
|
+
llm_wiki: {
|
|
192
|
+
coordinate_schema: 'sks.wiki-coordinate.v1',
|
|
193
|
+
default_pack: '.sneakoscope/wiki/context-pack.json',
|
|
194
|
+
compression_policy: 'preserve_ids_hashes_sources_rgba_coordinates_for_hydration',
|
|
195
|
+
channel_map: { r: 'domainAngle', g: 'layerRadius_sin', b: 'phase', a: 'concentration' }
|
|
196
|
+
},
|
|
182
197
|
package: {
|
|
183
198
|
zero_runtime_dependencies: true,
|
|
184
199
|
rust_default_runtime: false,
|
|
@@ -270,6 +285,7 @@ This project has been initialized for both the SKS CLI and Codex App.
|
|
|
270
285
|
- App skills: \`.codex/skills/\`
|
|
271
286
|
- App agents: \`.codex/agents/\`
|
|
272
287
|
- Mission state: \`.sneakoscope/missions/\`
|
|
288
|
+
- LLM Wiki pack: \`.sneakoscope/wiki/context-pack.json\`
|
|
273
289
|
- Current state: \`.sneakoscope/state/current.json\`
|
|
274
290
|
|
|
275
291
|
## Installed Command
|
|
@@ -290,6 +306,7 @@ ${commandPrefix} usage ralph
|
|
|
290
306
|
${commandPrefix} quickstart
|
|
291
307
|
${commandPrefix} install-prompt
|
|
292
308
|
${commandPrefix} codex-app
|
|
309
|
+
${commandPrefix} wiki pack
|
|
293
310
|
\`\`\`
|
|
294
311
|
|
|
295
312
|
## Dollar Commands
|
|
@@ -331,6 +348,8 @@ ${commandPrefix} ralph prepare "task"
|
|
|
331
348
|
${commandPrefix} ralph status latest
|
|
332
349
|
${commandPrefix} research prepare "topic"
|
|
333
350
|
${commandPrefix} team "task"
|
|
351
|
+
${commandPrefix} wiki pack
|
|
352
|
+
${commandPrefix} wiki validate
|
|
334
353
|
${commandPrefix} db scan --migrations
|
|
335
354
|
\`\`\`
|
|
336
355
|
|
|
@@ -349,18 +368,18 @@ async function installSkills(root) {
|
|
|
349
368
|
'DB': `---\nname: DB\ndescription: Dollar-command route for database and Supabase safety checks.\n---\n\nUse when the user invokes $DB or the task touches SQL, Supabase, Postgres, migrations, Prisma, Drizzle, Knex, MCP database tools, or production data. Run or follow sks db policy, sks db scan, sks db classify, and sks db check. Destructive database operations remain forbidden.\n`,
|
|
350
369
|
'GX': `---\nname: GX\ndescription: Dollar-command route for deterministic GX visual context cartridges.\n---\n\nUse when the user invokes $GX or asks for architecture/context visualization through SKS. Prefer sks gx init, render, validate, drift, and snapshot. vgraph.json remains the source of truth.\n`,
|
|
351
370
|
'Help': `---\nname: Help\ndescription: Dollar-command route for explaining installed SKS commands and workflows.\n---\n\nUse when the user invokes $Help or asks what commands exist. Prefer concise output from sks commands, sks usage <topic>, sks quickstart, sks aliases, and sks codex-app.\n`,
|
|
352
|
-
'prompt-pipeline': `---\nname: prompt-pipeline\ndescription: Default SKS prompt optimization pipeline that runs even without an explicit command.\n---\n\nFor every user request, silently extract intent, target surface, constraints, acceptance criteria, risk level, and the smallest safe route. Infer $DF for simple design/content edits. Use Ralph only for work that needs clarification gates, Research only for discovery work, DB only for database-risk work, GX only for visual context artifacts, and eval only when performance or context-quality claims need evidence.\n`,
|
|
371
|
+
'prompt-pipeline': `---\nname: prompt-pipeline\ndescription: Default SKS prompt optimization pipeline that runs even without an explicit command.\n---\n\nFor every user request, silently extract intent, target surface, constraints, acceptance criteria, risk level, and the smallest safe route. Infer $DF for simple design/content edits. Use Ralph only for work that needs clarification gates, Research only for discovery work, DB only for database-risk work, GX only for visual context artifacts, and eval only when performance or context-quality claims need evidence.\n\nContext continuity:\n- Prefer TriWiki coordinate context packs over ad hoc summaries when a task spans turns.\n- Use \`sks wiki pack\` when context continuity, compression quality, or LLM Wiki state matters.\n- Treat RGBA wiki anchors as hydratable pointers: selected text is only the visible slice; non-selected claims remain recoverable by id, hash, source path, and trigonometric coordinate.\n`,
|
|
353
372
|
'honest-mode': `---\nname: honest-mode\ndescription: Required final SKS verification pass before claiming a task is complete.\n---\n\nUse before every final answer.\n\nChecklist:\n- Restate the actual user goal in one sentence.\n- Verify the implemented result against that goal.\n- List tests, commands, screenshots, or inspections that prove it.\n- State any missing verification, uncertainty, or hard blocker plainly.\n- Do not claim complete if the evidence does not support it.\n- If implementation was requested, do not stop at a plan.\n\nThe final response should include a concise SKS Honest Mode or 솔직모드 note when the hook requires it.\n`,
|
|
354
373
|
'autoresearch-loop': `---\nname: autoresearch-loop\ndescription: Iterative AutoResearch-style loop for open-ended improvement, discovery, prompt, ranking, SEO/GEO, and workflow-quality tasks.\n---\n\nUse when the task asks for research, better ranking, SEO/GEO, prompt or workflow improvement, benchmark gains, non-obvious ideas, or repeated refinement.\n\nLoop:\n1. Program: define the objective, constraints, and budget.\n2. Hypothesis: propose one concrete change or experiment.\n3. Experiment: run the smallest local or documented check that can falsify it.\n4. Measure: record the metric, evidence, and artifact paths.\n5. Decision: keep, discard, or revise the hypothesis.\n6. Falsify: actively search for why the result could be wrong.\n7. Next: choose the next experiment or stop with an honest conclusion.\n\nRules:\n- Prefer small decisive experiments over broad speculation.\n- Keep a ledger in the mission/report when relevant.\n- Do not claim improvement without evidence.\n- End with Honest Mode: what improved, what did not, what remains unverified.\n`,
|
|
355
374
|
'ralph-supervisor': `---\nname: ralph-supervisor\ndescription: Run the Ralph no-question loop after a decision contract is sealed.\n---\n\nYou are the Ralph Supervisor.\n\nRules:\n- Never ask the user during Ralph run.\n- Use decision-contract.json and the decision ladder.\n- Continue until done-gate.json passes or safe scope is completed with explicit limitation.\n- Keep outputs bounded. Write raw logs to files and summarize only tails.\n- Database destructive operations are never allowed.\n- Write progress to .sneakoscope mission files.\n`,
|
|
356
375
|
'ralph-resolver': `---\nname: ralph-resolver\ndescription: Resolve newly discovered ambiguity during Ralph using the sealed decision ladder, without asking the user.\n---\n\nResolve ambiguity in this order: seed contract, explicit answers, approved defaults, AGENTS.md, current code/tests, smallest reversible change, defer optional scope. Never ask the user. If database risk is involved, prefer read-only, no-op, local-only migration file, or safe limitation; never run destructive SQL.\n`,
|
|
357
376
|
'hproof-claim-ledger': `---\nname: hproof-claim-ledger\ndescription: Extract atomic claims and classify support status.\n---\n\nEvery factual statement must become an atomic claim. Unsupported critical claims cannot be used for implementation or final answer. Database claims require DB safety evidence.\n`,
|
|
358
|
-
'hproof-evidence-bind': `---\nname: hproof-evidence-bind\ndescription: Bind claims to code, tests, decision contract, vgraph, beta, wiki, or GX render evidence.\n---\n\nEvidence priority: current code/tests, decision-contract.json, vgraph.json, beta.json, GX snapshot/render metadata,
|
|
377
|
+
'hproof-evidence-bind': `---\nname: hproof-evidence-bind\ndescription: Bind claims to code, tests, decision contract, vgraph, beta, wiki, or GX render evidence.\n---\n\nEvidence priority: current code/tests, decision-contract.json, vgraph.json, beta.json, GX snapshot/render metadata, LLM Wiki coordinate index, user prompt. Database claims must respect .sneakoscope/db-safety.json. Wiki claims should carry id, hash, source path, and RGBA/trig coordinate anchors so they can be hydrated instead of treated as unsupported summaries.\n`,
|
|
359
378
|
'db-safety-guard': `---\nname: db-safety-guard\ndescription: Enforce Sneakoscope Codex database safety before using SQL, Supabase MCP, Postgres, Prisma, Drizzle, Knex, or migration commands.\n---\n\nRules:\n- Never run DROP, TRUNCATE, mass DELETE/UPDATE, db reset, db push, project deletion, branch reset/merge/delete, or RLS-disabling operations.\n- Supabase MCP must be read-only and project-scoped by default.\n- Live writes through execute_sql are blocked; use migration files and only local/preview branches if explicitly allowed.\n- Production writes are forbidden.\n- If unsure, read-only only.\n`,
|
|
360
|
-
'gx-visual-generate': `---\nname: gx-visual-generate\ndescription: Render a deterministic SVG/HTML visual sheet from vgraph.json and beta.json.\n---\n\nUse sks gx render. Do not use external image generation. vgraph.json is the source of truth and the SVG embeds its source hash.\n`,
|
|
361
|
-
'gx-visual-read': `---\nname: gx-visual-read\ndescription: Read a Sneakoscope Codex deterministic visual sheet and produce context notes.\n---\n\nExtract nodes, edges, invariants, tests, risks, and
|
|
362
|
-
'gx-visual-validate': `---\nname: gx-visual-validate\ndescription: Validate render metadata against vgraph.json and beta.json.\n---\n\nRun sks gx validate and sks gx drift. If critical nodes, edges,
|
|
363
|
-
'turbo-context-pack': `---\nname: turbo-context-pack\ndescription: Build ultra-low-token context packet with Q4 bits, Q3 tags, top-K claims, and minimal evidence.\n---\n\nDefault to Q4/Q3
|
|
379
|
+
'gx-visual-generate': `---\nname: gx-visual-generate\ndescription: Render a deterministic SVG/HTML visual sheet from vgraph.json and beta.json.\n---\n\nUse sks gx render. Do not use external image generation. vgraph.json is the source of truth and the SVG embeds its source hash. GX renders also expose RGBA wiki-coordinate pixels/data attributes for nodes so visual context and LLM Wiki anchors share one coordinate system.\n`,
|
|
380
|
+
'gx-visual-read': `---\nname: gx-visual-read\ndescription: Read a Sneakoscope Codex deterministic visual sheet and produce context notes.\n---\n\nExtract nodes, edges, invariants, tests, risks, uncertainties, and RGBA wiki-coordinate anchors from vgraph.json, beta.json, render.svg, or snapshot.json. Do not infer hidden nodes.\n`,
|
|
381
|
+
'gx-visual-validate': `---\nname: gx-visual-validate\ndescription: Validate render metadata against vgraph.json and beta.json.\n---\n\nRun sks gx validate and sks gx drift. If critical nodes, edges, invariants, source hash, or wiki-coordinate anchors are missing or stale, mark validation failed.\n`,
|
|
382
|
+
'turbo-context-pack': `---\nname: turbo-context-pack\ndescription: Build ultra-low-token context packet with Q4 bits, Q3 tags, top-K claims, and minimal evidence.\n---\n\nDefault to Q4/Q3 plus TriWiki RGBA coordinate anchors. Add Q2 or Q1 text only when needed for support or verification. Non-selected claims should not disappear: keep id, hash, source path, RGBA key, and [domain, layer, phase, concentration] tuple so the harness can hydrate them later.\n`,
|
|
364
383
|
'research-discovery': `---\nname: research-discovery\ndescription: Run SKS Research Mode for frontier-style research, new hypothesis generation, novelty ledgers, falsification, and testable experiments.\n---\n\nUse when the user asks for research, new discoveries, frontier exploration, deep investigation, hypothesis generation, or non-obvious insights.\n\nMethod:\n1. Frame what would count as a discovery and what evidence would be required.\n2. Map nearby concepts, assumptions, baselines, and constraints.\n3. Generate competing hypotheses across mechanisms, analogies, edge cases, and failure modes.\n4. Falsify aggressively: counterexamples, missing evidence, alternate explanations, and safety boundaries.\n5. Synthesize only the surviving pieces into candidate insights.\n6. For every candidate insight, write novelty, confidence, falsifiability, evidence, falsifiers, and next_experiment to novelty-ledger.json.\n7. Produce research-report.md with concise findings and uncertainty.\n8. Pass research-gate.json only when at least one candidate insight survived falsification and has a testable prediction or experiment.\n\nQuality bar:\n- Do not summarize only; produce mechanisms, predictions, experiments, or implementation probes.\n- Do not claim breakthrough novelty without ledger evidence and uncertainty.\n- Prefer small decisive tests over broad speculation.\n- Keep raw notes bounded and cite artifact paths in final output.\n`,
|
|
365
384
|
'performance-evaluator': `---\nname: performance-evaluator\ndescription: Evaluate whether SKS changes create meaningful performance, token-saving, accuracy-proxy, context-compression, or workflow improvements.\n---\n\nUse when a task claims faster execution, smaller prompts, better context quality, higher accuracy, or lower token cost.\n\nWorkflow:\n- Run sks eval run for the deterministic built-in benchmark.\n- Use sks eval compare --baseline old.json --candidate new.json for before/after report comparisons.\n- Report token_savings_pct, accuracy_delta, required_recall, unsupported_critical_selected, and meaningful_improvement.\n- Treat accuracy_proxy as evidence-weighted context quality, not live model task accuracy, unless an explicitly scored dataset was used.\n- For performance-sensitive work, set done-gate.json performance_evaluation_required/present fields and include the eval report path as evidence.\n`,
|
|
366
385
|
'design-artifact-expert': `---\nname: design-artifact-expert\ndescription: Create or revise high-fidelity HTML, UI, prototype, deck-like, or visual design artifacts using project design context, variations, and rendered verification.\n---\n\nUse when the user asks for design, UI, prototype, HTML artifact, landing page, deck-like visual work, interaction design, or visual refinement.\n\nWorkflow:\n1. Understand the artifact, audience, constraints, fidelity, variants, and existing brand/design system.\n2. Inspect local code, assets, screenshots, or design-system docs before inventing visuals. If context exists, follow its visual vocabulary.\n3. Build the actual usable screen or artifact first; avoid empty landing-page framing unless the task is explicitly marketing.\n4. Use descriptive HTML filenames. Keep large artifacts split into small support files when needed.\n5. For screens/slides, add data-screen-label attributes for comment context. Slide labels are 1-indexed.\n6. Preserve state for decks, videos, or multi-step prototypes with localStorage when refresh continuity matters.\n7. Expose a small Tweaks surface for useful variants such as layout, density, color, copy, or interaction options.\n8. Verify the artifact renders cleanly in a browser or preview. For design tasks, set done-gate.json design_verification_required/present fields and cite evidence.\n\nQuality bar:\n- Root design decisions in available assets and components.\n- Use restrained, domain-appropriate layout and typography.\n- Avoid text overlap, unreadable controls, decorative clutter, one-note palettes, and placeholder-only deliverables.\n- Prefer icons and familiar controls for tool actions, and make repeated UI dimensions stable.\n`
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
+
import { buildWikiCoordinateIndex, compactWikiCoordinateIndex, normalizeWikiCoord, wikiCoordSimilarity } from './wiki-coordinate.mjs';
|
|
2
|
+
|
|
1
3
|
const TAU = 2 * Math.PI;
|
|
2
4
|
|
|
3
5
|
export function clamp01(x) { return Math.max(0, Math.min(1, Number.isFinite(x) ? x : 0)); }
|
|
4
6
|
export function wave(theta, phi) { return 0.5 + 0.5 * Math.cos(theta - phi); }
|
|
5
7
|
|
|
6
8
|
export function trigScore(missionCoord = {}, claimCoord = {}) {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
9
|
+
const missionWikiCoord = normalizeWikiCoord(missionCoord, 'mission');
|
|
10
|
+
const claimWikiCoord = normalizeWikiCoord(claimCoord, 'claim');
|
|
11
|
+
const domainDelta = (missionWikiCoord.domainAngle || 0) - (claimWikiCoord.domainAngle || 0);
|
|
12
|
+
const layerDelta = (missionWikiCoord.layerRadius || 0) - (claimWikiCoord.layerRadius || 0);
|
|
13
|
+
const phaseDelta = (missionWikiCoord.phase || 0) - (claimWikiCoord.phase || 0);
|
|
10
14
|
return (
|
|
11
|
-
0.
|
|
12
|
-
0.
|
|
13
|
-
0.
|
|
15
|
+
0.32 * wave(domainDelta, 0) +
|
|
16
|
+
0.18 * wave(layerDelta * 0.7, 0) +
|
|
17
|
+
0.22 * wave(phaseDelta, 0) +
|
|
18
|
+
0.28 * wikiCoordSimilarity(missionWikiCoord, claimWikiCoord)
|
|
14
19
|
);
|
|
15
20
|
}
|
|
16
21
|
|
|
@@ -45,7 +50,20 @@ function topKByScore(items, k) {
|
|
|
45
50
|
export function selectClaims(mission, claims, budget = {}) {
|
|
46
51
|
const maxClaims = Math.max(0, budget.maxClaims ?? 12);
|
|
47
52
|
const scored = (claims || []).map((claim) => ({ claim, score: claimScore(mission, claim) }));
|
|
48
|
-
|
|
53
|
+
const selected = [];
|
|
54
|
+
const selectedIds = new Set();
|
|
55
|
+
const required = scored
|
|
56
|
+
.filter((x) => Number(x.claim.required_weight) > 0)
|
|
57
|
+
.sort((a, b) => b.score - a.score);
|
|
58
|
+
for (const item of required) {
|
|
59
|
+
if (selected.length >= maxClaims) break;
|
|
60
|
+
selected.push(item);
|
|
61
|
+
selectedIds.add(item.claim.id);
|
|
62
|
+
}
|
|
63
|
+
const fill = topKByScore(scored.filter((x) => !selectedIds.has(x.claim.id)), maxClaims - selected.length);
|
|
64
|
+
return [...selected, ...fill]
|
|
65
|
+
.sort((a, b) => b.score - a.score)
|
|
66
|
+
.map((x) => ({ ...x.claim, triwiki_score: Number(x.score.toFixed(4)) }));
|
|
49
67
|
}
|
|
50
68
|
|
|
51
69
|
export function geometricOffsets(max = 65536) {
|
|
@@ -54,15 +72,30 @@ export function geometricOffsets(max = 65536) {
|
|
|
54
72
|
return out;
|
|
55
73
|
}
|
|
56
74
|
|
|
57
|
-
export function contextCapsule({ mission, role = 'worker', contractHash = null, claims = [], q4 = {}, q3 = [] }) {
|
|
58
|
-
const selected = selectClaims(mission, claims, { maxClaims: role.includes('verifier') ? 16 :
|
|
75
|
+
export function contextCapsule({ mission, role = 'worker', contractHash = null, claims = [], q4 = {}, q3 = [], budget = {} }) {
|
|
76
|
+
const selected = selectClaims(mission, claims, { maxClaims: budget.maxClaims ?? (role.includes('verifier') ? 16 : 9) });
|
|
77
|
+
const fullWiki = buildWikiCoordinateIndex({
|
|
78
|
+
mission,
|
|
79
|
+
claims,
|
|
80
|
+
q4,
|
|
81
|
+
q3,
|
|
82
|
+
maxAnchors: budget.maxWikiAnchors ?? (role.includes('verifier') ? 16 : 7)
|
|
83
|
+
});
|
|
84
|
+
const wiki = budget.verboseWiki ? fullWiki : compactWikiCoordinateIndex(fullWiki);
|
|
85
|
+
const anchorRows = Array.isArray(wiki.a) ? wiki.a : [];
|
|
86
|
+
const anchorsById = new Map((wiki.anchors || []).map((anchor) => [anchor.id, anchor]));
|
|
87
|
+
for (const row of anchorRows) anchorsById.set(row[0], { id: row[0], rgba: row[1], c: row[2], h: row[7] });
|
|
59
88
|
return {
|
|
60
89
|
mission: mission.id,
|
|
61
90
|
role,
|
|
62
91
|
contract_hash: contractHash,
|
|
63
|
-
token_policy: '
|
|
92
|
+
token_policy: 'Q4_Q3_DEFAULT_WITH_RGBA_TRIG_WIKI_ANCHORS_Q2_Q1_HYDRATED_ON_DEMAND',
|
|
64
93
|
q4,
|
|
65
94
|
q3,
|
|
66
|
-
|
|
95
|
+
wiki,
|
|
96
|
+
claims: selected.map((c) => {
|
|
97
|
+
const anchor = anchorsById.get(c.id);
|
|
98
|
+
return { id: c.id, text: c.text, status: c.status, risk: c.risk, source: c.source, score: c.triwiki_score, rgba: anchor?.rgba, h: anchor?.h };
|
|
99
|
+
})
|
|
67
100
|
};
|
|
68
101
|
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { sha256 } from './fsx.mjs';
|
|
2
|
+
|
|
3
|
+
export const WIKI_COORD_SCHEMA = 'sks.wiki-coordinate.v1';
|
|
4
|
+
export const WIKI_TAU = Math.PI * 2;
|
|
5
|
+
|
|
6
|
+
export function clamp01(x) {
|
|
7
|
+
return Math.max(0, Math.min(1, Number.isFinite(x) ? x : 0));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function round6(x) {
|
|
11
|
+
return Number((Number.isFinite(x) ? x : 0).toFixed(6));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function byte(x) {
|
|
15
|
+
return Math.max(0, Math.min(255, Math.round(Number.isFinite(Number(x)) ? Number(x) : 0)));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function wrapTau(x) {
|
|
19
|
+
const value = Number.isFinite(Number(x)) ? Number(x) : 0;
|
|
20
|
+
return ((value % WIKI_TAU) + WIKI_TAU) % WIKI_TAU;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function rgbaFromHash(seed = '') {
|
|
24
|
+
const hex = sha256(String(seed || 'wiki-anchor'));
|
|
25
|
+
return {
|
|
26
|
+
r: Number.parseInt(hex.slice(0, 2), 16),
|
|
27
|
+
g: Number.parseInt(hex.slice(2, 4), 16),
|
|
28
|
+
b: Number.parseInt(hex.slice(4, 6), 16),
|
|
29
|
+
a: 160 + (Number.parseInt(hex.slice(6, 8), 16) % 96)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function rgbaKey(rgba = {}) {
|
|
34
|
+
const c = normalizeRgba(rgba);
|
|
35
|
+
return [c.r, c.g, c.b, c.a].map((x) => x.toString(16).padStart(2, '0')).join('');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function normalizeRgba(rgba = {}) {
|
|
39
|
+
if (Array.isArray(rgba)) return { r: byte(rgba[0]), g: byte(rgba[1]), b: byte(rgba[2]), a: byte(rgba[3] ?? 255) };
|
|
40
|
+
if (typeof rgba === 'string') {
|
|
41
|
+
const clean = rgba.replace(/^#/, '').replace(/[^0-9a-f]/gi, '');
|
|
42
|
+
if (clean.length >= 6) {
|
|
43
|
+
return {
|
|
44
|
+
r: Number.parseInt(clean.slice(0, 2), 16),
|
|
45
|
+
g: Number.parseInt(clean.slice(2, 4), 16),
|
|
46
|
+
b: Number.parseInt(clean.slice(4, 6), 16),
|
|
47
|
+
a: clean.length >= 8 ? Number.parseInt(clean.slice(6, 8), 16) : 255
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { r: byte(rgba.r), g: byte(rgba.g), b: byte(rgba.b), a: byte(rgba.a ?? 255) };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function rgbaToWikiCoord(rgba = {}) {
|
|
55
|
+
const c = normalizeRgba(rgba);
|
|
56
|
+
const alpha = c.a / 255;
|
|
57
|
+
const domainAngle = WIKI_TAU * ((c.r + 0.5) / 256);
|
|
58
|
+
const layerRadius = Math.sin(((c.g + 0.5) / 256) * (Math.PI / 2));
|
|
59
|
+
const phase = WIKI_TAU * ((c.b + 0.5) / 256);
|
|
60
|
+
const concentration = 0.05 + 0.95 * alpha;
|
|
61
|
+
return compactWikiCoord({ domainAngle, layerRadius, phase, concentration, rgba: c });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function wikiCoordToRgba(coord = {}) {
|
|
65
|
+
const domainAngle = wrapTau(coord.domainAngle);
|
|
66
|
+
const phase = wrapTau(coord.phase);
|
|
67
|
+
const layerRadius = clamp01(coord.layerRadius);
|
|
68
|
+
const concentration = clamp01(coord.concentration ?? 1);
|
|
69
|
+
return {
|
|
70
|
+
r: byte((domainAngle / WIKI_TAU) * 256 - 0.5),
|
|
71
|
+
g: byte((Math.asin(layerRadius) / (Math.PI / 2)) * 256 - 0.5),
|
|
72
|
+
b: byte((phase / WIKI_TAU) * 256 - 0.5),
|
|
73
|
+
a: byte(((concentration - 0.05) / 0.95) * 255)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function normalizeWikiCoord(coord = {}, seed = '') {
|
|
78
|
+
if (coord?.rgba) return rgbaToWikiCoord(coord.rgba);
|
|
79
|
+
if (coord && ['domainAngle', 'layerRadius', 'phase'].some((key) => Number.isFinite(Number(coord[key])))) {
|
|
80
|
+
return compactWikiCoord({
|
|
81
|
+
domainAngle: wrapTau(coord.domainAngle),
|
|
82
|
+
layerRadius: clamp01(Number(coord.layerRadius)),
|
|
83
|
+
phase: wrapTau(coord.phase),
|
|
84
|
+
concentration: clamp01(Number(coord.concentration ?? 0.85)),
|
|
85
|
+
rgba: coord.rgba || wikiCoordToRgba(coord)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return rgbaToWikiCoord(rgbaFromHash(seed));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function compactWikiCoord(coord = {}) {
|
|
92
|
+
const domainAngle = wrapTau(coord.domainAngle);
|
|
93
|
+
const layerRadius = clamp01(Number(coord.layerRadius));
|
|
94
|
+
const phase = wrapTau(coord.phase);
|
|
95
|
+
const concentration = clamp01(Number(coord.concentration ?? 1));
|
|
96
|
+
const rgba = normalizeRgba(coord.rgba || wikiCoordToRgba({ domainAngle, layerRadius, phase, concentration }));
|
|
97
|
+
return {
|
|
98
|
+
schema: WIKI_COORD_SCHEMA,
|
|
99
|
+
rgba,
|
|
100
|
+
domainAngle: round6(domainAngle),
|
|
101
|
+
layerRadius: round6(layerRadius),
|
|
102
|
+
phase: round6(phase),
|
|
103
|
+
concentration: round6(concentration),
|
|
104
|
+
xyzw: [
|
|
105
|
+
round6(concentration * Math.cos(domainAngle)),
|
|
106
|
+
round6(concentration * Math.sin(domainAngle)),
|
|
107
|
+
round6(layerRadius * Math.cos(phase)),
|
|
108
|
+
round6(layerRadius * Math.sin(phase))
|
|
109
|
+
]
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function wikiCoordSimilarity(a = {}, b = {}) {
|
|
114
|
+
const ca = normalizeWikiCoord(a, 'a');
|
|
115
|
+
const cb = normalizeWikiCoord(b, 'b');
|
|
116
|
+
const domain = 0.5 + 0.5 * Math.cos(ca.domainAngle - cb.domainAngle);
|
|
117
|
+
const phase = 0.5 + 0.5 * Math.cos(ca.phase - cb.phase);
|
|
118
|
+
const layer = 1 - Math.min(1, Math.abs(ca.layerRadius - cb.layerRadius));
|
|
119
|
+
const concentration = 1 - Math.min(1, Math.abs(ca.concentration - cb.concentration));
|
|
120
|
+
return clamp01((0.42 * domain) + (0.26 * layer) + (0.24 * phase) + (0.08 * concentration));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function wikiAnchorFromClaim(claim = {}, index = 0) {
|
|
124
|
+
const id = String(claim.id || `claim-${index + 1}`);
|
|
125
|
+
const text = String(claim.text || claim.label || id);
|
|
126
|
+
const coord = normalizeWikiCoord(claim.coord || {}, `${id}:${text}`);
|
|
127
|
+
const source = claim.source || claim.authority || 'wiki';
|
|
128
|
+
return {
|
|
129
|
+
id,
|
|
130
|
+
rgba: rgbaKey(coord.rgba),
|
|
131
|
+
c: [coord.domainAngle, coord.layerRadius, coord.phase, coord.concentration],
|
|
132
|
+
k: claim.authority || source,
|
|
133
|
+
st: claim.status || 'unknown',
|
|
134
|
+
r: claim.risk || 'medium',
|
|
135
|
+
src: source,
|
|
136
|
+
h: sha256(`${id}\n${text}`).slice(0, 16),
|
|
137
|
+
tc: Math.max(1, Math.ceil(Number(claim.tokenCost) || text.length / 4)),
|
|
138
|
+
p: claim.hydrate || claim.path || claim.evidence_path || claim.file || null
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function buildWikiCoordinateIndex({ mission = {}, claims = [], q4 = {}, q3 = [], maxAnchors = 24 } = {}) {
|
|
143
|
+
const missionCoord = normalizeWikiCoord(mission.coord || {}, mission.id || JSON.stringify(q3 || []));
|
|
144
|
+
const anchors = (claims || [])
|
|
145
|
+
.map((claim, index) => {
|
|
146
|
+
const anchor = wikiAnchorFromClaim(claim, index);
|
|
147
|
+
const coord = { domainAngle: anchor.c[0], layerRadius: anchor.c[1], phase: anchor.c[2], concentration: anchor.c[3] };
|
|
148
|
+
return { ...anchor, sim: round6(wikiCoordSimilarity(missionCoord, coord)) };
|
|
149
|
+
})
|
|
150
|
+
.sort((a, b) => b.sim - a.sim || a.id.localeCompare(b.id))
|
|
151
|
+
.slice(0, Math.max(0, Number(maxAnchors) || 0));
|
|
152
|
+
return {
|
|
153
|
+
schema: WIKI_COORD_SCHEMA,
|
|
154
|
+
channel_map: {
|
|
155
|
+
r: 'domainAngle',
|
|
156
|
+
g: 'layerRadius',
|
|
157
|
+
b: 'phase',
|
|
158
|
+
a: 'concentration'
|
|
159
|
+
},
|
|
160
|
+
transform: 'domain=2pi*r/256; layer=sin(g*pi/512); phase=2pi*b/256; xyzw=[a*cos(domain),a*sin(domain),layer*cos(phase),layer*sin(phase)]',
|
|
161
|
+
mission: {
|
|
162
|
+
id: mission.id || 'mission',
|
|
163
|
+
rgba: rgbaKey(missionCoord.rgba),
|
|
164
|
+
c: [missionCoord.domainAngle, missionCoord.layerRadius, missionCoord.phase, missionCoord.concentration]
|
|
165
|
+
},
|
|
166
|
+
q4_hash: sha256(JSON.stringify(q4 || {})).slice(0, 16),
|
|
167
|
+
q3,
|
|
168
|
+
anchors,
|
|
169
|
+
overflow_count: Math.max(0, (claims || []).length - anchors.length),
|
|
170
|
+
hydration_policy: 'anchor_ids_hashes_and_paths_keep_context_hydratable_without_pasting_raw_q0'
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function compactWikiCoordinateIndex(index = {}) {
|
|
175
|
+
return {
|
|
176
|
+
schema: WIKI_COORD_SCHEMA,
|
|
177
|
+
ch: 'r=domain,g=sin-layer,b=phase,a=concentration',
|
|
178
|
+
m: [index.mission?.rgba || '000000ff', index.mission?.c || [0, 0, 0, 1]],
|
|
179
|
+
q: index.q4_hash || null,
|
|
180
|
+
q3: index.q3 || [],
|
|
181
|
+
a: (index.anchors || []).map((anchor) => [
|
|
182
|
+
anchor.id,
|
|
183
|
+
anchor.rgba,
|
|
184
|
+
anchor.c,
|
|
185
|
+
anchor.k,
|
|
186
|
+
anchor.st,
|
|
187
|
+
anchor.r,
|
|
188
|
+
anchor.src,
|
|
189
|
+
anchor.h,
|
|
190
|
+
anchor.p
|
|
191
|
+
]),
|
|
192
|
+
o: index.overflow_count || 0,
|
|
193
|
+
hp: 'id+rgba+coord+source+hash hydrate Q2/Q1/Q0 on demand'
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function expandedAnchors(index = {}) {
|
|
198
|
+
if (Array.isArray(index.anchors)) return index.anchors;
|
|
199
|
+
if (!Array.isArray(index.a)) return [];
|
|
200
|
+
return index.a.map((row) => ({
|
|
201
|
+
id: row[0],
|
|
202
|
+
rgba: row[1],
|
|
203
|
+
c: row[2],
|
|
204
|
+
k: row[3],
|
|
205
|
+
st: row[4],
|
|
206
|
+
r: row[5],
|
|
207
|
+
src: row[6],
|
|
208
|
+
h: row[7],
|
|
209
|
+
p: row[8]
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function validateWikiCoordinateIndex(index = {}) {
|
|
214
|
+
const issues = [];
|
|
215
|
+
if (index.schema !== WIKI_COORD_SCHEMA) issues.push({ id: 'schema_mismatch', severity: 'error' });
|
|
216
|
+
if (!index.channel_map && !index.ch) issues.push({ id: 'channel_map_missing', severity: 'error' });
|
|
217
|
+
const anchors = expandedAnchors(index);
|
|
218
|
+
if (!anchors.length && !Array.isArray(index.anchors) && !Array.isArray(index.a)) issues.push({ id: 'anchors_missing', severity: 'error' });
|
|
219
|
+
const seen = new Set();
|
|
220
|
+
for (const anchor of anchors) {
|
|
221
|
+
if (!anchor.id) issues.push({ id: 'anchor_id_missing', severity: 'error' });
|
|
222
|
+
if (seen.has(anchor.id)) issues.push({ id: 'duplicate_anchor', severity: 'error', anchor: anchor.id });
|
|
223
|
+
seen.add(anchor.id);
|
|
224
|
+
if (!/^[0-9a-f]{8}$/i.test(String(anchor.rgba || ''))) issues.push({ id: 'invalid_rgba_key', severity: 'error', anchor: anchor.id });
|
|
225
|
+
if (!Array.isArray(anchor.c) || anchor.c.length !== 4) issues.push({ id: 'invalid_coord_tuple', severity: 'error', anchor: anchor.id });
|
|
226
|
+
}
|
|
227
|
+
return { ok: issues.length === 0, checked: anchors.length, issues };
|
|
228
|
+
}
|