sneakoscope 0.9.12 → 0.9.14

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.
Files changed (105) hide show
  1. package/README.md +31 -34
  2. package/crates/sks-core/Cargo.lock +99 -1
  3. package/crates/sks-core/Cargo.toml +2 -1
  4. package/crates/sks-core/src/main.rs +176 -2
  5. package/package.json +8 -3
  6. package/src/cli/command-registry.mjs +75 -116
  7. package/src/cli/feature-commands.mjs +81 -11
  8. package/src/cli/install-helpers.mjs +15 -8
  9. package/src/cli/router.mjs +2 -3
  10. package/src/commands/aliases.mjs +2 -0
  11. package/src/commands/auto-review.mjs +5 -0
  12. package/src/commands/autoresearch.mjs +5 -0
  13. package/src/commands/bootstrap.mjs +2 -0
  14. package/src/commands/code-structure.mjs +5 -0
  15. package/src/commands/codex-app.mjs +30 -0
  16. package/src/commands/codex-lb.mjs +78 -4
  17. package/src/commands/commands.mjs +5 -0
  18. package/src/commands/commit-and-push.mjs +5 -0
  19. package/src/commands/commit.mjs +5 -0
  20. package/src/commands/computer-use.mjs +31 -0
  21. package/src/commands/conflicts.mjs +13 -0
  22. package/src/commands/context7.mjs +5 -0
  23. package/src/commands/db.mjs +6 -0
  24. package/src/commands/deps.mjs +5 -0
  25. package/src/commands/dfix.mjs +2 -0
  26. package/src/commands/doctor.mjs +46 -0
  27. package/src/commands/dollar-commands.mjs +2 -0
  28. package/src/commands/eval.mjs +5 -0
  29. package/src/commands/fix-path.mjs +2 -0
  30. package/src/commands/gc.mjs +2 -0
  31. package/src/commands/goal.mjs +5 -0
  32. package/src/commands/guard.mjs +10 -0
  33. package/src/commands/gx.mjs +5 -0
  34. package/src/commands/harness.mjs +5 -0
  35. package/src/commands/help.mjs +3 -74
  36. package/src/commands/hook.mjs +8 -0
  37. package/src/commands/hproof.mjs +5 -0
  38. package/src/commands/image-ux-review.mjs +38 -0
  39. package/src/commands/init.mjs +2 -0
  40. package/src/commands/mad-sks.mjs +14 -0
  41. package/src/commands/memory.mjs +5 -0
  42. package/src/commands/openclaw.mjs +2 -0
  43. package/src/commands/perf.mjs +2 -2
  44. package/src/commands/pipeline.mjs +25 -0
  45. package/src/commands/postinstall.mjs +2 -0
  46. package/src/commands/ppt.mjs +44 -0
  47. package/src/commands/profile.mjs +5 -0
  48. package/src/commands/proof-field.mjs +5 -0
  49. package/src/commands/proof.mjs +59 -2
  50. package/src/commands/qa-loop.mjs +5 -0
  51. package/src/commands/quickstart.mjs +2 -0
  52. package/src/commands/reasoning.mjs +2 -0
  53. package/src/commands/recallpulse.mjs +5 -0
  54. package/src/commands/research.mjs +5 -0
  55. package/src/commands/selftest.mjs +2 -0
  56. package/src/commands/setup.mjs +2 -0
  57. package/src/commands/skill-dream.mjs +5 -0
  58. package/src/commands/stats.mjs +2 -0
  59. package/src/commands/team.mjs +2 -0
  60. package/src/commands/tmux.mjs +5 -0
  61. package/src/commands/update-check.mjs +2 -0
  62. package/src/commands/usage.mjs +2 -0
  63. package/src/commands/validate-artifacts.mjs +2 -0
  64. package/src/commands/versioning.mjs +16 -0
  65. package/src/commands/wiki.mjs +55 -4
  66. package/src/core/codex-lb-circuit.mjs +63 -1
  67. package/src/core/commands/basic-cli.mjs +315 -0
  68. package/src/{cli/maintenance-commands.mjs → core/commands/route-cli.mjs} +37 -37
  69. package/src/core/db-safety.mjs +17 -2
  70. package/src/core/feature-fixture-runner.mjs +109 -0
  71. package/src/core/feature-fixtures.mjs +40 -2
  72. package/src/core/feature-registry.mjs +103 -8
  73. package/src/core/fsx.mjs +1 -1
  74. package/src/core/git-simple.mjs +118 -0
  75. package/src/core/hooks-runtime.mjs +12 -3
  76. package/src/core/pipeline.mjs +19 -1
  77. package/src/core/proof/evidence-collector.mjs +7 -0
  78. package/src/core/proof/proof-reader.mjs +11 -0
  79. package/src/core/proof/proof-redaction.test-helper.mjs +9 -0
  80. package/src/core/proof/route-adapter.mjs +74 -0
  81. package/src/core/proof/route-finalizer-fixtures.mjs +21 -0
  82. package/src/core/proof/route-finalizer-policy.mjs +13 -0
  83. package/src/core/proof/route-finalizer.mjs +82 -0
  84. package/src/core/proof/route-proof-gate.mjs +33 -0
  85. package/src/core/proof/route-proof-policy.mjs +96 -0
  86. package/src/core/proof/selftest-proof-fixtures.mjs +54 -0
  87. package/src/core/proof/validation.mjs +1 -0
  88. package/src/core/proof-field.mjs +2 -2
  89. package/src/core/routes.mjs +31 -1
  90. package/src/core/rust-accelerator.mjs +35 -8
  91. package/src/core/version.mjs +1 -1
  92. package/src/core/wiki-image/before-after-relation.mjs +1 -0
  93. package/src/core/wiki-image/callout-parser.mjs +16 -0
  94. package/src/core/wiki-image/computer-use-evidence.mjs +1 -0
  95. package/src/core/wiki-image/computer-use-ledger.mjs +38 -0
  96. package/src/core/wiki-image/generated-review-parser.mjs +1 -0
  97. package/src/core/wiki-image/image-relation.mjs +2 -0
  98. package/src/core/wiki-image/image-ux-evidence.mjs +1 -0
  99. package/src/core/wiki-image/image-voxel-ledger.mjs +51 -1
  100. package/src/core/wiki-image/ppt-image-evidence.mjs +1 -0
  101. package/src/core/wiki-image/proof-linker.mjs +12 -0
  102. package/src/core/wiki-image/route-image-evidence.mjs +107 -0
  103. package/src/core/wiki-image/validation.mjs +16 -5
  104. package/src/core/wiki-image/visual-anchor.mjs +14 -1
  105. package/src/cli/legacy-main.mjs +0 -4146
package/README.md CHANGED
@@ -1,8 +1,27 @@
1
1
  # Sneakoscope Codex
2
2
 
3
- Fast proof-first Codex trust layer with image-based Voxel TriWiki.
3
+ Fast legacy-free proof-first Codex trust layer with image-based Voxel TriWiki.
4
4
 
5
- Sneakoscope Codex (`sks`) is a Codex CLI/App harness for repeatable workflows. It adds terminal commands, Codex App `$` commands, tmux workspaces, Team/QA/Research routes, pipeline plans, Computer Use, imagegen UI/UX review, Goal, Context7, DB safety, Voxel TriWiki, design-system routing, skill dreaming, completion proof, and Honest Mode.
5
+ Sneakoscope Codex (`sks`) is a Codex CLI/App harness that makes repeatable Codex work auditable. `0.9.14` runs through split command modules, automatically seals serious routes with Completion Proof, binds visual/UI claims to Image Voxel TriWiki anchors and relations, and release-gates hooks, codex-lb, executable fixtures, Rust parity, and DB safety evidence.
6
+
7
+ ## 0.9.14 Current Release
8
+
9
+ `0.9.14` turns SKS into a legacy-free proof-first trust layer for Codex work:
10
+
11
+ - Command registry entries load independent command modules without runtime fallback to archived 0.9.13 CLI bundles.
12
+ - Serious routes write Completion Proof through the central route finalizer before completion is claimed.
13
+ - Visual/UI routes require Image Voxel TriWiki anchors, with before/after relations for visual fix claims.
14
+ - Executable feature fixtures validate expected artifact existence and schema contracts.
15
+ - Hook replay strictly matches decision, reason, gate, issues, continuation, and redaction policy.
16
+ - codex-lb launch failures feed both global and active-project circuit health; stateless `previous_response_not_found` stays a warning.
17
+ - Rust `image-hash` and semantic `voxel-validate` commands match JS fallback behavior.
18
+ - `$Commit` and `$Commit-And-Push` provide a simple git-only route for staging, committing, and optionally pushing without the full SKS pipeline.
19
+
20
+ Learn more:
21
+ - Completion Proof: [docs/completion-proof.md](docs/completion-proof.md)
22
+ - Image Voxel TriWiki: [docs/image-voxel-ledger.md](docs/image-voxel-ledger.md)
23
+ - Codex App Hooks/PAT: [docs/hooks-pat.md](docs/hooks-pat.md)
24
+ - codex-lb: [docs/codex-lb.md](docs/codex-lb.md)
6
25
 
7
26
  ## 60-second start
8
27
 
@@ -16,22 +35,20 @@ sks selftest --mock
16
35
 
17
36
  ## Three core promises
18
37
 
19
- 1. Image-based Voxel TriWiki memory
20
- 2. Codex App / codex-lb operational readiness
21
- 3. Completion proof for every serious route
38
+ 1. Completion Proof for every serious route
39
+ 2. Image Voxel TriWiki anchors and relations for every visual route
40
+ 3. Codex App, codex-lb, hooks, Rust, DB, and fixtures verified by release gates
22
41
 
23
- ## Quick Start
42
+ ## Install Options
24
43
 
25
44
  Install globally, then run `sks` from either a project or any global shell location:
26
45
 
27
46
  ```sh
28
47
  npm i -g sneakoscope
29
48
  sks root
30
- sks
49
+ sks doctor
31
50
  ```
32
51
 
33
- `0.9.12` adds the lazy CLI architecture foundation, `sks proof`, image voxel ledger commands, cold-start perf checks, hook trust reports, codex-lb circuit metrics, and feature fixture contracts. Rust accelerator source is included in the npm package; until prebuilt binaries ship, SKS uses JS fallbacks unless `SKS_RS_BIN` or a source-checkout `sks-rs` binary is available.
34
-
35
52
  `npm i -g sneakoscope` automatically refreshes the `sks` command shim, global Codex App `$` skills, and SKS bootstrap surface. When the install is run from a project, postinstall bootstraps that project. When it is run outside a repo/project marker, postinstall bootstraps the per-user global runtime root instead of writing `.sneakoscope` into a random current directory. `sks root` tells you which root SKS will use.
36
53
 
37
54
  If you only want a one-shot run without keeping `sks` installed globally:
@@ -53,6 +70,7 @@ Check that the install is usable:
53
70
  sks deps check
54
71
  sks codex-app check
55
72
  sks dollar-commands
73
+ sks commit --json
56
74
  sks selftest --mock
57
75
  ```
58
76
 
@@ -60,32 +78,11 @@ sks selftest --mock
60
78
 
61
79
  `sks` adds a tmux Codex CLI runtime, Codex App `$` commands, Team/QA/PPT/Research/DB/GX/Wiki routes, OpenClaw skill generation, Context7-gated current docs, TriWiki context packs, DB safety, design SSOT policy, skill dreaming, release checks, and Honest Mode.
62
80
 
63
- ## 0.8.0 Massive Upgrade
64
-
65
- Sneakoscope 0.8.0 introduces the RecallPulse planning spine: a report-only active-recall layer that records what the pipeline should remember before a stage proceeds. RecallPulse maps TriWiki into L1/L2/L3 cache behavior, writes durable status ledgers instead of relying on ephemeral hook text, suppresses duplicate reminder loops, and emits RouteProofCapsule plus EvidenceEnvelope artifacts for later gate comparison. These artifacts are evidence surfaces first; speed or accuracy gains remain benchmark-gated until scored evals prove them.
66
-
67
- Inspect the new report-only artifacts with:
68
-
69
- ```sh
70
- sks recallpulse run latest
71
- sks recallpulse status latest --json
72
- sks recallpulse eval latest --json
73
- sks recallpulse governance latest --json
74
- sks recallpulse checklist latest --json
75
- sks recallpulse checklist latest --task T001 --apply --evidence src/core/recallpulse.mjs
76
- ```
77
-
78
- Research scouts now use named persona-inspired cognitive lenses: Einstein Scout, Feynman Scout, Turing Scout, von Neumann Scout, and Skeptic Scout. They are not impersonations; each scout ledger row must carry `display_name`, `persona`, `persona_boundary`, `reasoning_effort=xhigh`, a literal `Eureka!` idea, falsifiers, cheap probes, and debate participation evidence.
79
-
80
- For existing 0.7.x users, the visible change is new report-only evidence, not a route personality rewrite. Team still feels like Team, DFix stays ultralight, DB remains conservative, QA-LOOP still dogfoods, PPT stays information-first, imagegen still requires real raster evidence, and Honest Mode remains the final truth pass. The original strong reminder idea became neutral RecallPulse so user-facing prompts stay short, professional, and non-repetitive; hook messages can point at status, but `mission-status-ledger.json` is the durable source when app-visible text disappears. The planning source is `docs/RECALLPULSE_0_8_0_TASKS.md`, and implementation is designed to land in safe task-sized slices before any enforcement promotion.
81
-
82
- ## 0.9.0 Report-Only Decision Lattice
83
-
84
- Sneakoscope 0.9.0 adds a report-only Decision Lattice planner that uses A* over proof-debt signals to explain which route or verification path the pipeline would prefer. It is an evidence and planning surface, not a runtime shortcut: SKS must not claim speedup, fast-lane accuracy, or reduced verification cost from the lattice until replay or scored eval evidence demonstrates those outcomes.
81
+ ## Report-Only Planning Surfaces
85
82
 
86
- The lattice integrates with the existing proof-field and `sks pipeline plan` surfaces. Its reports are expected to show the explored frontier, the selected path, and rejected paths with their proof-debt reasons, so reviewers can audit why a route stayed on the full Team/Honest path or why a smaller verification plan was only proposed. Like RecallPulse, this is designed to land as report-only evidence first; route enforcement and performance claims remain gated by later validation.
83
+ Decision Lattice and RecallPulse remain report-only planning and evidence surfaces. They can explain route choices and proof-debt signals, but SKS does not claim speedup, fast-lane accuracy, or reduced verification cost from them until scored evals prove those outcomes.
87
84
 
88
- Quick checks:
85
+ Useful checks:
89
86
 
90
87
  ```bash
91
88
  sks proof-field scan --json --intent "small CLI change"
@@ -207,7 +204,7 @@ sks codex-lb repair
207
204
  sks
208
205
  ```
209
206
 
210
- Bare `sks` can also prompt for codex-lb auth; SKS stores the base URL/key in `~/.codex/sks-codex-lb.env`, writes the upstream codex-lb Codex CLI / IDE Extension provider block into `~/.codex/config.toml` for Codex App routing, loads the provider env key for tmux launches, and syncs the macOS user launch environment so the Codex App can see `CODEX_LB_API_KEY` after restart. If the provider block disappears but the stored env file is still recoverable, bare `sks`, npm postinstall upgrades, `sks doctor --fix`, and `sks codex-lb repair` restore it with `env_key = "CODEX_LB_API_KEY"`, `supports_websockets = true`, and `requires_openai_auth = true` as documented by codex-lb. If an older SKS release left the codex-lb dashboard key only in the shared Codex `auth.json` login cache, SKS migrates that key back into `~/.codex/sks-codex-lb.env` when a codex-lb provider or env base URL is already recoverable. It does not rewrite the shared Codex `auth.json` login cache by default; set `SKS_CODEX_LB_SYNC_CODEX_LOGIN=1` only if you intentionally want the old API-key login-cache behavior. When codex-lb is active, SKS opens a fresh `sks-codex-lb-*` tmux session and sweeps older detached codex-lb sessions for the same repo before launch so stale Responses API chains are not reused. Configured launch paths, including non-interactive runs, verify that codex-lb can continue a Responses API chain with `previous_response_id`; if that check fails, SKS bypasses codex-lb for that launch with `model_provider="openai"` instead of letting the Codex session fail mid-work.
207
+ Bare `sks` can also prompt for codex-lb auth; SKS stores the base URL/key in `~/.codex/sks-codex-lb.env`, writes the upstream codex-lb Codex CLI / IDE Extension provider block into `~/.codex/config.toml` for Codex App routing, loads the provider env key for tmux launches, and syncs the macOS user launch environment so the Codex App can see `CODEX_LB_API_KEY` after restart. If the provider block disappears but the stored env file is still recoverable, bare `sks`, npm postinstall upgrades, `sks doctor --fix`, and `sks codex-lb repair` restore it with `env_key = "CODEX_LB_API_KEY"`, `supports_websockets = true`, and `requires_openai_auth = true` as documented by codex-lb. If an older SKS release left the codex-lb dashboard key only in the shared Codex `auth.json` login cache, SKS migrates that key back into `~/.codex/sks-codex-lb.env` when a codex-lb provider or env base URL is already recoverable. It does not rewrite the shared Codex `auth.json` login cache by default; set `SKS_CODEX_LB_SYNC_CODEX_LOGIN=1` only if you intentionally want the old API-key login-cache behavior. When codex-lb is active, SKS opens a fresh `sks-codex-lb-*` tmux session and sweeps older detached codex-lb sessions for the same repo before launch so stale Responses API chains are not reused. Configured launch paths run a response-chain health check. `previous_response_not_found` is treated as a stateless-LB warning and keeps codex-lb active. Hard failures are surfaced to the user; SKS only bypasses codex-lb when the user chooses OAuth fallback or `SKS_CODEX_LB_AUTOBYPASS=1` is set.
211
208
 
212
209
  If codex-lb provider auth drifts after launch/reinstall, run `sks doctor --fix` or `sks codex-lb repair`; to replace it, run `sks codex-lb reconfigure --host <domain> --api-key <key>`.
213
210
 
@@ -2,6 +2,104 @@
2
2
  # It is not intended for manual editing.
3
3
  version = 4
4
4
 
5
+ [[package]]
6
+ name = "itoa"
7
+ version = "1.0.18"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
10
+
11
+ [[package]]
12
+ name = "memchr"
13
+ version = "2.8.0"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
16
+
17
+ [[package]]
18
+ name = "proc-macro2"
19
+ version = "1.0.106"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
22
+ dependencies = [
23
+ "unicode-ident",
24
+ ]
25
+
26
+ [[package]]
27
+ name = "quote"
28
+ version = "1.0.45"
29
+ source = "registry+https://github.com/rust-lang/crates.io-index"
30
+ checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
31
+ dependencies = [
32
+ "proc-macro2",
33
+ ]
34
+
35
+ [[package]]
36
+ name = "serde"
37
+ version = "1.0.228"
38
+ source = "registry+https://github.com/rust-lang/crates.io-index"
39
+ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
40
+ dependencies = [
41
+ "serde_core",
42
+ ]
43
+
44
+ [[package]]
45
+ name = "serde_core"
46
+ version = "1.0.228"
47
+ source = "registry+https://github.com/rust-lang/crates.io-index"
48
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
49
+ dependencies = [
50
+ "serde_derive",
51
+ ]
52
+
53
+ [[package]]
54
+ name = "serde_derive"
55
+ version = "1.0.228"
56
+ source = "registry+https://github.com/rust-lang/crates.io-index"
57
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
58
+ dependencies = [
59
+ "proc-macro2",
60
+ "quote",
61
+ "syn",
62
+ ]
63
+
64
+ [[package]]
65
+ name = "serde_json"
66
+ version = "1.0.149"
67
+ source = "registry+https://github.com/rust-lang/crates.io-index"
68
+ checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
69
+ dependencies = [
70
+ "itoa",
71
+ "memchr",
72
+ "serde",
73
+ "serde_core",
74
+ "zmij",
75
+ ]
76
+
5
77
  [[package]]
6
78
  name = "sks-core"
7
- version = "0.9.12"
79
+ version = "0.9.14"
80
+ dependencies = [
81
+ "serde_json",
82
+ ]
83
+
84
+ [[package]]
85
+ name = "syn"
86
+ version = "2.0.117"
87
+ source = "registry+https://github.com/rust-lang/crates.io-index"
88
+ checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
89
+ dependencies = [
90
+ "proc-macro2",
91
+ "quote",
92
+ "unicode-ident",
93
+ ]
94
+
95
+ [[package]]
96
+ name = "unicode-ident"
97
+ version = "1.0.24"
98
+ source = "registry+https://github.com/rust-lang/crates.io-index"
99
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
100
+
101
+ [[package]]
102
+ name = "zmij"
103
+ version = "1.0.21"
104
+ source = "registry+https://github.com/rust-lang/crates.io-index"
105
+ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
@@ -1,9 +1,10 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "0.9.12"
3
+ version = "0.9.14"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
7
+ serde_json = "1"
7
8
 
8
9
  [[bin]]
9
10
  name = "sks-rs"
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 0.9.12"),
7
+ Some("--version") => println!("sks-rs 0.9.14"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -28,6 +28,36 @@ fn main() {
28
28
  }
29
29
  }
30
30
  }
31
+ Some("image-hash") => {
32
+ let path = args.next().unwrap_or_default();
33
+ match image_hash(&path) {
34
+ Ok((sha, bytes)) => println!("{{\"ok\":true,\"engine\":\"rust\",\"path\":\"{}\",\"sha256\":\"{}\",\"bytes\":{}}}", json_escape(&path), sha, bytes),
35
+ Err(err) => {
36
+ println!("{{\"ok\":false,\"engine\":\"rust\",\"path\":\"{}\",\"error\":\"{}\"}}", json_escape(&path), json_escape(&err.to_string()));
37
+ std::process::exit(1);
38
+ }
39
+ }
40
+ }
41
+ Some("voxel-validate") => {
42
+ let path = args.next().unwrap_or_default();
43
+ let mut require_anchors = false;
44
+ let mut require_relations = false;
45
+ while let Some(arg) = args.next() {
46
+ if arg == "--require-anchors" { require_anchors = true; }
47
+ if arg == "--require-relations" { require_relations = true; }
48
+ }
49
+ match std::fs::read_to_string(&path) {
50
+ Ok(text) => {
51
+ let report = voxel_validate(&text, require_anchors, require_relations);
52
+ println!("{}", report);
53
+ if report.contains("\"ok\":false") { std::process::exit(1); }
54
+ }
55
+ Err(err) => {
56
+ println!("{{\"ok\":false,\"engine\":\"rust\",\"issues\":[\"read_error\"],\"error\":\"{}\"}}", json_escape(&err.to_string()));
57
+ std::process::exit(1);
58
+ }
59
+ }
60
+ }
31
61
  Some("secret-scan") => {
32
62
  let path = args.next().unwrap_or_default();
33
63
  match std::fs::read_to_string(&path) {
@@ -45,12 +75,156 @@ fn main() {
45
75
  }
46
76
  }
47
77
  _ => {
48
- eprintln!("sks-rs optional accelerator. Commands: --version, compact-info, jsonl-tail, secret-scan");
78
+ eprintln!("sks-rs optional accelerator. Commands: --version, compact-info, jsonl-tail, secret-scan, image-hash, voxel-validate");
49
79
  std::process::exit(2);
50
80
  }
51
81
  }
52
82
  }
53
83
 
84
+ fn image_hash(path: &str) -> io::Result<(String, u64)> {
85
+ let mut file = File::open(path)?;
86
+ let mut data = Vec::new();
87
+ file.read_to_end(&mut data)?;
88
+ let bytes = data.len() as u64;
89
+ Ok((sha256_hex(&data), bytes))
90
+ }
91
+
92
+ fn sha256_hex(data: &[u8]) -> String {
93
+ const K: [u32; 64] = [
94
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
95
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
96
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
97
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
98
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
99
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
100
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
101
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
102
+ ];
103
+ let mut h: [u32; 8] = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
104
+ let bit_len = (data.len() as u64) * 8;
105
+ let mut msg = data.to_vec();
106
+ msg.push(0x80);
107
+ while (msg.len() % 64) != 56 { msg.push(0); }
108
+ msg.extend_from_slice(&bit_len.to_be_bytes());
109
+ for chunk in msg.chunks(64) {
110
+ let mut w = [0u32; 64];
111
+ for i in 0..16 {
112
+ w[i] = u32::from_be_bytes([chunk[i * 4], chunk[i * 4 + 1], chunk[i * 4 + 2], chunk[i * 4 + 3]]);
113
+ }
114
+ for i in 16..64 {
115
+ let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
116
+ let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
117
+ w[i] = w[i - 16].wrapping_add(s0).wrapping_add(w[i - 7]).wrapping_add(s1);
118
+ }
119
+ let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) = (h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
120
+ for i in 0..64 {
121
+ let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
122
+ let ch = (e & f) ^ ((!e) & g);
123
+ let temp1 = hh.wrapping_add(s1).wrapping_add(ch).wrapping_add(K[i]).wrapping_add(w[i]);
124
+ let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
125
+ let maj = (a & b) ^ (a & c) ^ (b & c);
126
+ let temp2 = s0.wrapping_add(maj);
127
+ hh = g;
128
+ g = f;
129
+ f = e;
130
+ e = d.wrapping_add(temp1);
131
+ d = c;
132
+ c = b;
133
+ b = a;
134
+ a = temp1.wrapping_add(temp2);
135
+ }
136
+ h[0] = h[0].wrapping_add(a);
137
+ h[1] = h[1].wrapping_add(b);
138
+ h[2] = h[2].wrapping_add(c);
139
+ h[3] = h[3].wrapping_add(d);
140
+ h[4] = h[4].wrapping_add(e);
141
+ h[5] = h[5].wrapping_add(f);
142
+ h[6] = h[6].wrapping_add(g);
143
+ h[7] = h[7].wrapping_add(hh);
144
+ }
145
+ h.iter().map(|x| format!("{:08x}", x)).collect::<String>()
146
+ }
147
+
148
+ fn voxel_validate(text: &str, require_anchors: bool, require_relations: bool) -> String {
149
+ let parsed: serde_json::Value = match serde_json::from_str(text) {
150
+ Ok(value) => value,
151
+ Err(err) => {
152
+ return format!("{{\"ok\":false,\"engine\":\"rust\",\"schema\":\"sks.image-voxel-ledger.v1\",\"images\":0,\"anchors\":0,\"relations\":0,\"issues\":[\"json_parse\"],\"error\":\"{}\"}}", json_escape(&err.to_string()));
153
+ }
154
+ };
155
+ let mut issues: Vec<String> = Vec::new();
156
+ if parsed.get("schema").and_then(|v| v.as_str()) != Some("sks.image-voxel-ledger.v1") { issues.push("schema".to_string()); }
157
+ let images = parsed.get("images").and_then(|v| v.as_array()).cloned().unwrap_or_default();
158
+ let anchors = parsed.get("anchors").and_then(|v| v.as_array()).cloned().unwrap_or_default();
159
+ let relations = parsed.get("relations").and_then(|v| v.as_array()).cloned().unwrap_or_default();
160
+ if !parsed.get("images").map(|v| v.is_array()).unwrap_or(false) { issues.push("missing_images".to_string()); }
161
+ if !parsed.get("anchors").map(|v| v.is_array()).unwrap_or(false) { issues.push("missing_anchors".to_string()); }
162
+ if require_anchors && anchors.is_empty() { issues.push("missing_anchors:visual-route".to_string()); }
163
+ if require_relations && relations.is_empty() { issues.push("missing_relations:visual-route".to_string()); }
164
+ let mut image_ids: Vec<String> = Vec::new();
165
+ let mut anchor_ids: Vec<String> = Vec::new();
166
+ for image in &images {
167
+ let id = image.get("id").and_then(|v| v.as_str()).unwrap_or("");
168
+ if id.is_empty() { issues.push("image_id".to_string()); }
169
+ if !id.is_empty() && image_ids.iter().any(|x| x == id) { issues.push(format!("duplicate_image:{}", id)); }
170
+ if !id.is_empty() { image_ids.push(id.to_string()); }
171
+ if image.get("path").and_then(|v| v.as_str()).unwrap_or("").is_empty() { issues.push(format!("image_path:{}", if id.is_empty() { "unknown" } else { id })); }
172
+ if image.get("sha256").and_then(|v| v.as_str()).unwrap_or("").is_empty() { issues.push(format!("image_sha256:{}", if id.is_empty() { "unknown" } else { id })); }
173
+ let w = image.get("width").and_then(|v| v.as_f64());
174
+ let h = image.get("height").and_then(|v| v.as_f64());
175
+ if !w.map(|n| n.is_finite()).unwrap_or(false) || !h.map(|n| n.is_finite()).unwrap_or(false) {
176
+ issues.push(format!("image_dimensions:{}", if id.is_empty() { "unknown" } else { id }));
177
+ }
178
+ }
179
+ for anchor in &anchors {
180
+ let id = anchor.get("id").and_then(|v| v.as_str()).unwrap_or("");
181
+ if id.is_empty() { issues.push("anchor_id".to_string()); }
182
+ if !id.is_empty() && anchor_ids.iter().any(|x| x == id) { issues.push(format!("duplicate_anchor:{}", id)); }
183
+ if !id.is_empty() { anchor_ids.push(id.to_string()); }
184
+ let image_id = anchor.get("image_id").and_then(|v| v.as_str()).unwrap_or("");
185
+ if image_id.is_empty() || !image_ids.iter().any(|x| x == image_id) { issues.push(format!("anchor_image_ref:{}", if id.is_empty() { "unknown" } else { id })); }
186
+ let image = images.iter().find(|img| img.get("id").and_then(|v| v.as_str()) == Some(image_id));
187
+ let w = image.and_then(|img| img.get("width")).and_then(|v| v.as_f64()).unwrap_or(f64::NAN);
188
+ let h = image.and_then(|img| img.get("height")).and_then(|v| v.as_f64()).unwrap_or(f64::NAN);
189
+ match anchor.get("bbox").and_then(|v| v.as_array()) {
190
+ Some(bbox) if bbox.len() == 4 => {
191
+ let vals: Vec<f64> = bbox.iter().map(|v| v.as_f64().unwrap_or(f64::NAN)).collect();
192
+ if vals.iter().any(|n| !n.is_finite()) { issues.push(format!("bbox_number:{}", if id.is_empty() { "unknown" } else { id })); }
193
+ if vals[2] <= 0.0 || vals[3] <= 0.0 { issues.push(format!("bbox_positive:{}", if id.is_empty() { "unknown" } else { id })); }
194
+ if w.is_finite() && vals[0] + vals[2] > w {
195
+ issues.push(format!("bbox_width_out_of_bounds:{}", if id.is_empty() { "unknown" } else { id }));
196
+ }
197
+ if h.is_finite() && vals[1] + vals[3] > h {
198
+ issues.push(format!("bbox_height_out_of_bounds:{}", if id.is_empty() { "unknown" } else { id }));
199
+ }
200
+ }
201
+ Some(_) => issues.push(format!("bbox_shape:{}", if id.is_empty() { "unknown" } else { id })),
202
+ None => issues.push(format!("anchor_bbox:{}", if id.is_empty() { "unknown" } else { id })),
203
+ }
204
+ }
205
+ for relation in &relations {
206
+ if let Some(before) = relation.get("before_image_id").and_then(|v| v.as_str()) {
207
+ if !image_ids.iter().any(|x| x == before) { issues.push(format!("relation_before:{}", before)); }
208
+ }
209
+ if let Some(after) = relation.get("after_image_id").and_then(|v| v.as_str()) {
210
+ if !image_ids.iter().any(|x| x == after) { issues.push(format!("relation_after:{}", after)); }
211
+ }
212
+ let changed = relation.get("changed_anchor_ids").or_else(|| relation.get("anchors")).and_then(|v| v.as_array()).cloned().unwrap_or_default();
213
+ for anchor_id in changed.iter().filter_map(|v| v.as_str()) {
214
+ if !anchor_ids.iter().any(|x| x == anchor_id) { issues.push(format!("relation_anchor:{}", anchor_id)); }
215
+ }
216
+ }
217
+ issues.sort();
218
+ issues.dedup();
219
+ let ok = issues.is_empty();
220
+ let issue_json = issues.iter().map(|x| format!("\"{}\"", json_escape(x))).collect::<Vec<_>>().join(",");
221
+ format!("{{\"ok\":{},\"engine\":\"rust\",\"schema\":\"sks.image-voxel-ledger.v1\",\"images\":{},\"anchors\":{},\"relations\":{},\"issues\":[{}]}}", if ok { "true" } else { "false" }, images.len(), anchors.len(), relations.len(), issue_json)
222
+ }
223
+
224
+ fn json_escape(value: &str) -> String {
225
+ value.replace('\\', "\\\\").replace('"', "\\\"").replace('\n', "\\n").replace('\r', "\\r")
226
+ }
227
+
54
228
  fn tail_file(path: &str, bytes: u64) -> io::Result<String> {
55
229
  let mut file = File::open(path)?;
56
230
  let len = file.metadata()?.len();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "0.9.12",
4
+ "version": "0.9.14",
5
5
  "description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
@@ -37,20 +37,25 @@
37
37
  "postinstall": "node ./bin/sks.mjs postinstall",
38
38
  "selftest": "node ./bin/sks.mjs selftest --mock",
39
39
  "doctor": "node ./bin/sks.mjs doctor",
40
- "packcheck": "find bin src scripts -name '*.mjs' -print0 | xargs -0 -n1 node --check",
40
+ "packcheck": "find bin src scripts test -name '*.mjs' -print0 | xargs -0 -n1 node --check",
41
41
  "changelog:check": "node ./scripts/changelog-check.mjs",
42
42
  "cli-entrypoint:check": "node ./scripts/check-cli-entrypoint.mjs",
43
+ "legacy-free:check": "node ./scripts/check-legacy-free.mjs",
43
44
  "sizecheck": "node ./scripts/sizecheck.mjs",
44
45
  "registry:check": "node ./scripts/release-registry-check.mjs",
45
46
  "feature:check": "node ./bin/sks.mjs features check --json",
46
47
  "all-features:selftest": "node ./bin/sks.mjs all-features selftest --mock --json",
48
+ "all-features:execute-fixtures": "node ./bin/sks.mjs all-features selftest --mock --execute-fixtures --strict-artifacts --json",
47
49
  "perf:cold-start": "node ./bin/sks.mjs perf cold-start --json",
48
50
  "perf:gate": "node ./scripts/perf-gate.mjs",
49
51
  "test": "node --test \"test/**/*.test.mjs\"",
50
52
  "test:unit": "node --test \"test/unit/**/*.test.mjs\"",
51
53
  "test:integration:mock": "node --test \"test/integration/**/*.test.mjs\"",
54
+ "test:e2e:mock": "node --test \"test/e2e/**/*.test.mjs\"",
55
+ "rust:check": "cargo check --manifest-path crates/sks-core/Cargo.toml",
56
+ "rust:smoke": "node ./scripts/rust-smoke.mjs",
52
57
  "coverage": "node --experimental-test-coverage --test \"test/**/*.test.mjs\"",
53
- "release:check": "npm run repo-audit && npm run changelog:check && npm run cli-entrypoint:check && npm run packcheck && npm run feature:check && npm run all-features:selftest && npm run selftest && npm run test:unit && npm run test:integration:mock && npm run perf:gate && npm run sizecheck && npm run registry:check",
58
+ "release:check": "npm run repo-audit && npm run changelog:check && npm run cli-entrypoint:check && npm run legacy-free:check && npm run packcheck && npm run feature:check && npm run all-features:selftest && npm run all-features:execute-fixtures && npm run selftest && npm run test:unit && npm run test:integration:mock && npm run test:e2e:mock && npm run rust:check && npm run rust:smoke && npm run perf:gate && npm run sizecheck && npm run registry:check",
54
59
  "publish:dry": "npm run release:check && npm --cache /tmp/sks-npm-cache publish --dry-run --registry https://registry.npmjs.org/ --access public",
55
60
  "publish:npm": "npm --cache /tmp/sks-npm-cache publish --registry https://registry.npmjs.org/ --access public",
56
61
  "prepublishOnly": "npm run release:check && node ./scripts/release-registry-check.mjs --require-unpublished"