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 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 the whole block below into the agent from your target project directory. The agent should install SKS, initialize the project, verify the setup, and show the available commands in one pass.
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 the current project end to end. Do not ask follow-up questions unless a command requires user approval.
32
-
33
- Repository:
34
- https://github.com/mandarange/Sneakoscope-Codex.git
41
+ Install Sneakoscope Codex in this project.
35
42
 
36
- Requirements:
37
- - Node.js must be >=20.11.
38
- - Codex CLI is installed separately. If it is missing, report that `@openai/codex` must be installed or `SKS_CODEX_BIN` must be set.
39
- - Use the published npm package for normal installs.
40
- - Do not modify application source files unless needed for SKS setup.
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 npm reports `ENOTEMPTY`, `EEXIST`, or a broken old global package:
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
- For project-only install:
71
- ```bash
72
- npm i -D sneakoscope
73
- npx sks setup --install-scope project
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 small context capsules for the current mission.
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
 
@@ -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.3",
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 an LLM-ready prompt that installs and configures SKS automatically.' },
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 || cmd === '--help' || cmd === '-h') return help();
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}`);
@@ -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.3';
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
 
@@ -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
- return `<g class="node" transform="translate(${pos.x - pos.w / 2} ${pos.y - pos.h / 2})">
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
- 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. 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}`;
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, wiki, user prompt. Database claims must respect .sneakoscope/db-safety.json.\n`,
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 uncertainties from vgraph.json, beta.json, render.svg, or snapshot.json. Do not infer hidden nodes.\n`,
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, or invariants are missing or the render hash is stale, mark validation failed.\n`,
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 only. Add Q2 or Q1 only when needed for support or verification.\n`,
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 domainDelta = (missionCoord.domainAngle || 0) - (claimCoord.domainAngle || 0);
8
- const layerDelta = (missionCoord.layerRadius || 0) - (claimCoord.layerRadius || 0);
9
- const phaseDelta = (missionCoord.phase || 0) - (claimCoord.phase || 0);
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.45 * wave(domainDelta, 0) +
12
- 0.25 * wave(layerDelta * 0.7, 0) +
13
- 0.30 * wave(phaseDelta, 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
- return topKByScore(scored, maxClaims).map((x) => ({ ...x.claim, triwiki_score: Number(x.score.toFixed(4)) }));
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 : 10 });
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: 'Q4_Q3_DEFAULT_Q2_ON_DEMAND_Q1_FOR_VERIFICATION_ONLY',
92
+ token_policy: 'Q4_Q3_DEFAULT_WITH_RGBA_TRIG_WIKI_ANCHORS_Q2_Q1_HYDRATED_ON_DEMAND',
64
93
  q4,
65
94
  q3,
66
- claims: selected.map((c) => ({ id: c.id, text: c.text, status: c.status, risk: c.risk, source: c.source, score: c.triwiki_score }))
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
+ }