sneakoscope 6.0.2 → 6.1.0
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 +5 -3
- package/config/codex-releases/{rust-v0.142.0.json → rust-v0.144.1.json} +6 -6
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/install-helpers.js +255 -440
- package/dist/cli/router.js +53 -4
- package/dist/commands/codex-app.js +8 -3
- package/dist/commands/codex-lb.js +169 -40
- package/dist/commands/codex.js +6 -5
- package/dist/commands/doctor.js +60 -26
- package/dist/commands/versioning.js +7 -0
- package/dist/config/skills-manifest.json +57 -57
- package/dist/core/agents/agent-central-ledger.js +9 -0
- package/dist/core/agents/agent-cleanup-executor.js +334 -41
- package/dist/core/agents/agent-cleanup.js +20 -1
- package/dist/core/agents/agent-command-surface.js +2 -1
- package/dist/core/agents/agent-effort-policy.js +46 -33
- package/dist/core/agents/agent-janitor.js +9 -10
- package/dist/core/agents/agent-namespace-safety.js +56 -0
- package/dist/core/agents/agent-orchestrator.js +26 -4
- package/dist/core/agents/agent-output-validator.js +1 -1
- package/dist/core/agents/agent-proof-evidence.js +16 -2
- package/dist/core/agents/agent-role-config.js +3 -6
- package/dist/core/agents/agent-roster.js +2 -2
- package/dist/core/agents/agent-runner-codex-exec.js +2 -0
- package/dist/core/agents/agent-runner-process.js +1 -0
- package/dist/core/agents/fast-mode-policy.js +3 -5
- package/dist/core/agents/native-cli-session-swarm.js +18 -1
- package/dist/core/agents/native-worker-backend-router.js +180 -108
- package/dist/core/agents/parallel-write-fixture.js +15 -10
- package/dist/core/agents/runtime-proof-summary.js +21 -3
- package/dist/core/agents/work-partition/repo-inventory.js +68 -7
- package/dist/core/code-structure.js +5 -4
- package/dist/core/codex/codex-config-guard.js +31 -9
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +51 -20
- package/dist/core/codex-app/codex-app-restart.js +38 -8
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +11 -3
- package/dist/core/codex-app/sks-menubar.js +100 -40
- package/dist/core/codex-app.js +60 -17
- package/dist/core/codex-compat/codex-hook-warning-detector.js +10 -1
- package/dist/core/codex-compat/codex-release-manifest.js +6 -6
- package/dist/core/codex-control/codex-0139-doctor-real-probe.js +7 -1
- package/dist/core/codex-control/codex-0139-image-path-real-probe.js +14 -8
- package/dist/core/codex-control/codex-0139-probe-runner.js +103 -28
- package/dist/core/codex-control/codex-0139-real-probe-summary.js +7 -3
- package/dist/core/codex-control/codex-0139-real-probes.js +21 -6
- package/dist/core/codex-control/codex-0139-sandbox-real-probe.js +39 -13
- package/dist/core/codex-control/codex-0139-web-search-probe.js +11 -8
- package/dist/core/codex-control/{codex-0142-capability.js → codex-0144-capability.js} +22 -22
- package/dist/core/codex-control/codex-0144-collab-agent-real-probe.js +63 -0
- package/dist/core/codex-control/codex-model-metadata.js +8 -4
- package/dist/core/codex-control/codex-multi-agent-event-normalizer.js +5 -1
- package/dist/core/codex-control/codex-reliability-shield.js +4 -1
- package/dist/core/codex-control/codex-sdk-adapter.js +53 -6
- package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
- package/dist/core/codex-control/codex-sdk-sandbox-policy.js +6 -3
- package/dist/core/codex-lb/codex-lb-env.js +107 -0
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +478 -0
- package/dist/core/codex-native/codex-native-feature-broker.js +22 -22
- package/dist/core/codex-native/codex-native-feature-matrix.js +2 -2
- package/dist/core/codex-native/native-capability-postcheck.js +16 -8
- package/dist/core/codex-native/native-capability-repair-matrix.js +76 -27
- package/dist/core/codex-plugins/codex-plugin-json.js +67 -21
- package/dist/core/codex-plugins/codex-plugin-repair.js +118 -0
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +306 -0
- package/dist/core/commands/agent-command.js +19 -0
- package/dist/core/commands/basic-cli.js +45 -24
- package/dist/core/commands/gc-command.js +27 -4
- package/dist/core/commands/image-ux-review-command.js +55 -24
- package/dist/core/commands/mad-sks-command.js +152 -132
- package/dist/core/commands/naruto-command.js +41 -10
- package/dist/core/commands/pipeline-command.js +1 -0
- package/dist/core/commands/wiki-command.js +179 -83
- package/dist/core/computer-use-live-evidence.js +166 -3
- package/dist/core/computer-use-status.js +5 -2
- package/dist/core/daemon/sksd-hook-daemon-entrypoint.js +2 -2
- package/dist/core/daemon/sksd-hook-daemon.js +171 -28
- package/dist/core/daemon/sksd-hook-dispatch.js +2 -2
- package/dist/core/dfix/verification-selector.js +6 -4
- package/dist/core/doctor/browser-use-repair.js +54 -13
- package/dist/core/doctor/codex-0139-doctor.js +3 -2
- package/dist/core/doctor/computer-use-repair.js +28 -39
- package/dist/core/doctor/doctor-codex-startup-repair.js +19 -9
- package/dist/core/doctor/doctor-context7-repair.js +1 -1
- package/dist/core/doctor/doctor-dirty-planner.js +1 -1
- package/dist/core/doctor/doctor-native-capability-repair.js +74 -3
- package/dist/core/doctor/imagegen-repair.js +47 -12
- package/dist/core/doctor/legacy-global-hook-cleanup.js +179 -0
- package/dist/core/feature-registry.js +25 -31
- package/dist/core/fsx.js +54 -6
- package/dist/core/harness-conflicts.js +1 -1
- package/dist/core/hooks-runtime/code-pack-freshness-preflight.js +5 -4
- package/dist/core/hooks-runtime/hook-invocation-dedupe.js +147 -0
- package/dist/core/hooks-runtime/hook-io.js +6 -0
- package/dist/core/hooks-runtime.js +17 -2
- package/dist/core/image/image-artifact-path-contract.js +8 -6
- package/dist/core/image/image-artifact-registry.js +5 -1
- package/dist/core/image-ux-review/imagegen-adapter.js +5 -2
- package/dist/core/imagegen/imagegen-capability.js +10 -13
- package/dist/core/imagegen/require-imagegen.js +37 -11
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -5
- package/dist/core/naruto/naruto-work-graph.js +70 -4
- package/dist/core/naruto/naruto-write-e2e.js +3 -4
- package/dist/core/ops/reporting.js +3 -0
- package/dist/core/pipeline-internals/runtime-core.js +12 -7
- package/dist/core/pipeline-internals/runtime-gates.js +17 -1
- package/dist/core/ppt/style-tokens.js +1 -1
- package/dist/core/ppt-review/index.js +1 -1
- package/dist/core/ppt-review/slide-exporter.js +23 -17
- package/dist/core/ppt-review/slide-imagegen-review.js +1 -1
- package/dist/core/ppt-review/slide-issue-extraction.js +4 -2
- package/dist/core/product-design-plugin.js +2 -2
- package/dist/core/proof/route-finalizer.js +1 -1
- package/dist/core/proof-field.js +3 -3
- package/dist/core/provider/model-router.js +68 -3
- package/dist/core/release/gate-manifest.js +18 -12
- package/dist/core/release/gate-pack-manifest.js +1 -1
- package/dist/core/release/package-size-budget.js +2 -0
- package/dist/core/release/release-gate-affected-selector.js +1 -1
- package/dist/core/release/release-gate-dag.js +43 -3
- package/dist/core/release/release-gate-hermetic-env.js +28 -17
- package/dist/core/release/release-real-contract.js +416 -0
- package/dist/core/retention/retention-budget.js +25 -6
- package/dist/core/retention.js +980 -101
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/evidence.js +4 -4
- package/dist/core/routes/model-mode-router.js +3 -0
- package/dist/core/routes/ppt-policy.js +1 -1
- package/dist/core/routes.js +11 -12
- package/dist/core/secret-redaction.js +4 -0
- package/dist/core/triwiki/code-pack.js +8 -2
- package/dist/core/triwiki/triwiki-cache-key.js +108 -29
- package/dist/core/triwiki-attention.js +13 -6
- package/dist/core/triwiki-provenance.js +274 -0
- package/dist/core/triwiki-runtime.js +21 -7
- package/dist/core/triwiki-wrongness/wrongness-ledger.js +178 -59
- package/dist/core/triwiki-wrongness/wrongness-retrieval.js +11 -2
- package/dist/core/triwiki-wrongness/wrongness-trust-policy.js +2 -0
- package/dist/core/update/update-migration-state.js +1 -1
- package/dist/core/update/update-notice.js +23 -0
- package/dist/core/update-check.js +69 -25
- package/dist/core/version.js +1 -1
- package/dist/core/wiki-coordinate.js +128 -12
- package/dist/core/work-order-ledger.js +168 -9
- package/dist/core/zellij/zellij-command.js +12 -1
- package/dist/core/zellij/zellij-dashboard-pane.js +3 -3
- package/dist/core/zellij/zellij-launcher.js +15 -14
- package/dist/core/zellij/zellij-pane-proof.js +32 -6
- package/dist/core/zellij/zellij-worker-pane-manager.js +5 -3
- package/dist/scripts/agent-fast-mode-default-check.js +48 -29
- package/dist/scripts/agent-role-config-repair-check.js +25 -4
- package/dist/scripts/agent-wiki-context-proof-check.js +16 -2
- package/dist/scripts/all-feature-deep-completion-check.js +14 -4
- package/dist/scripts/build-dist.js +17 -5
- package/dist/scripts/canonical-test-runner.js +188 -0
- package/dist/scripts/check-command-module-budget.js +38 -16
- package/dist/scripts/check-legacy-free.js +1 -1
- package/dist/scripts/codex-0139-interrupt-agent-check.js +5 -1
- package/dist/scripts/{codex-0142-app-server-v2-check.js → codex-0144-app-server-v2-check.js} +11 -10
- package/dist/scripts/{codex-0142-binary-identity-check.js → codex-0144-binary-identity-check.js} +4 -4
- package/dist/scripts/codex-0144-capability-check.js +21 -0
- package/dist/scripts/{codex-0139-real-probes-check.js → codex-0144-core-real-probes-check.js} +19 -5
- package/dist/scripts/{codex-0142-doctor-wiring-check.js → codex-0144-doctor-wiring-check.js} +6 -6
- package/dist/scripts/{codex-0142-manifest-check.js → codex-0144-manifest-check.js} +11 -3
- package/dist/scripts/{codex-0142-policy-check.js → codex-0144-policy-check.js} +2 -2
- package/dist/scripts/{codex-0142-thread-store-check.js → codex-0144-thread-store-check.js} +3 -3
- package/dist/scripts/codex-app-provider-model-ui-check.js +57 -8
- package/dist/scripts/codex-app-ui-preservation-check.js +35 -4
- package/dist/scripts/codex-control-side-effect-scope-check.js +2 -2
- package/dist/scripts/codex-effort-auto-discovery-check.js +1 -1
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +1 -0
- package/dist/scripts/{codex-lb-gpt55-fast-profile-check.js → codex-lb-gpt56-fast-profile-check.js} +56 -5
- package/dist/scripts/codex-model-metadata-check.js +1 -1
- package/dist/scripts/codex-sdk-sandbox-policy-check.js +5 -1
- package/dist/scripts/codex-sdk-version-compat-check.js +2 -1
- package/dist/scripts/dfix-fixture-check.js +1 -1
- package/dist/scripts/docs-truthfulness-check.js +1 -1
- package/dist/scripts/doctor-codex-startup-repair-check.js +35 -10
- package/dist/scripts/doctor-context7-repair-check.js +2 -1
- package/dist/scripts/doctor-fix-proves-codex-read-check.js +1 -1
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +34 -7
- package/dist/scripts/doctor-imagegen-repair-check.js +14 -4
- package/dist/scripts/doctor-startup-config-repair-blackbox.js +1 -1
- package/dist/scripts/doctor-startup-config-repair-check.js +1 -1
- package/dist/scripts/gate-policy-audit-check.js +2 -2
- package/dist/scripts/install-update-preserves-config-check.js +1 -1
- package/dist/scripts/legacy-gate-inventory-check.js +7 -5
- package/dist/scripts/legacy-strong-inventory-check.js +7 -5
- package/dist/scripts/legacy-upgrade-matrix-check.js +5 -5
- package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +175 -14
- package/dist/scripts/mad-sks-app-ui-no-mutation-check.js +166 -6
- package/dist/scripts/mutation-callsite-coverage-check.js +0 -11
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +88 -50
- package/dist/scripts/npm-publish-performance-check.js +2 -1
- package/dist/scripts/package-published-contract-check.js +9 -0
- package/dist/scripts/packlist-performance-check.js +5 -2
- package/dist/scripts/postinstall-safe-side-effects-check.js +8 -3
- package/dist/scripts/ppt-no-mock-as-real-check.js +3 -1
- package/dist/scripts/product-design-plugin-routing-check.js +1 -1
- package/dist/scripts/release-check-stamp.js +182 -2
- package/dist/scripts/release-dag-full-coverage-check.js +7 -7
- package/dist/scripts/release-dynamic-presets-check.js +8 -0
- package/dist/scripts/release-gate-dag-runner-check.js +3 -2
- package/dist/scripts/release-gate-dag-runner.js +24 -11
- package/dist/scripts/release-gate-existence-audit.js +8 -9
- package/dist/scripts/release-gate-script-parity-check.js +1 -1
- package/dist/scripts/release-real-check.js +368 -157
- package/dist/scripts/release-runtime-truth-matrix-check.js +22 -0
- package/dist/scripts/route-proof-artifact-structure-check.js +1 -0
- package/dist/scripts/scheduler-batch-dispatch-check.js +1 -1
- package/dist/scripts/search-visibility-gate-lib.js +1 -1
- package/dist/scripts/seo-geo-route-identity-check.js +30 -4
- package/dist/scripts/sizecheck.js +7 -2
- package/dist/scripts/sks-1-11-gate-lib.js +53 -6
- package/dist/scripts/sks-menubar-install-check.js +7 -0
- package/dist/scripts/sks-uninstall-regression-check.js +1 -1
- package/dist/scripts/sksd-daemon-check.js +16 -6
- package/dist/scripts/update-default-command-check.js +4 -2
- package/dist/scripts/write-build-manifest.js +2 -1
- package/dist/scripts/zellij-layout-valid-check.js +31 -6
- package/dist/scripts/zellij-pane-proof-check.js +33 -2
- package/dist/scripts/zellij-real-session-cleanup-check.js +122 -2
- package/dist/scripts/zellij-real-session-launch-check.js +76 -5
- package/dist/scripts/zellij-slot-pane-renderer-check.js +4 -4
- package/package.json +27 -30
- package/schemas/codex/{app-server-0.142 → app-server-0.144}/codex_app_server_protocol.v2.schemas.json +1146 -781
- package/schemas/codex/codex-0139-real-probe-result.schema.json +39 -5
- package/schemas/codex/ppt-slide-issue-ledger.schema.json +2 -1
- package/dist/core/codex-control/codex-0139-multi-agent-real-probe.js +0 -107
- package/dist/scripts/agent-native-release-gate.js +0 -274
- package/dist/scripts/codex-0142-capability-check.js +0 -21
- package/dist/scripts/command-performance-scorecard-check.js +0 -204
- package/dist/scripts/config-managed-merge-callsite-coverage-check.js +0 -212
- package/dist/scripts/dollar-performance-scorecard-check.js +0 -257
- package/dist/scripts/gate-timing-check.js +0 -16
- package/dist/scripts/ops-maturity-scorecard-check.js +0 -12
- package/dist/scripts/performance-baseline-check.js +0 -12
- package/dist/scripts/performance-improvement-report-check.js +0 -58
- package/dist/scripts/release-parallel-check.js +0 -389
- package/dist/scripts/release-runner-efficiency-check.js +0 -15
- package/dist/scripts/retention-long-run-smoke-check.js +0 -299
- package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
|
@@ -29,31 +29,30 @@ assertGate(fullRoster.max_agents === 100, 'clone roster max_agents must be 100',
|
|
|
29
29
|
// 4) Over-cap is clamped, not crashed.
|
|
30
30
|
const overCap = roster.buildNarutoCloneRoster({ clones: 500, maxAgentCount: schema.MAX_NARUTO_AGENT_COUNT });
|
|
31
31
|
assertGate(overCap.agent_count === 100, 'clones above the ceiling clamp to 100', { agent_count: overCap.agent_count });
|
|
32
|
-
// 4b)
|
|
33
|
-
// A neutral/no-tool prompt yields a low/medium MIX: read-only personas -> low, writers -> medium.
|
|
32
|
+
// 4b) Naruto-only GPT-5.6 family policy. The old low/medium cap is gone.
|
|
34
33
|
const effortRoster = roster.buildNarutoCloneRoster({ clones: 24, prompt: 'review and summarize the current findings', maxAgentCount: schema.MAX_NARUTO_AGENT_COUNT });
|
|
35
34
|
const efforts = new Set(effortRoster.roster.map((row) => row.reasoning_effort));
|
|
36
35
|
const modelTiers = new Set(effortRoster.roster.map((row) => row.model_tier));
|
|
37
36
|
const modelEfforts = new Set(effortRoster.roster.map((row) => row.model_reasoning_effort));
|
|
38
37
|
const tiers = new Set(effortRoster.roster.map((row) => row.service_tier));
|
|
39
38
|
const fastFlags = new Set(effortRoster.roster.map((row) => row.fast_mode));
|
|
40
|
-
assertGate([...efforts].every((
|
|
41
|
-
assertGate(
|
|
42
|
-
assertGate([...modelTiers].every((tier) =>
|
|
43
|
-
assertGate(
|
|
44
|
-
assertGate([...modelEfforts].every((effort) => effort === 'low' || effort === 'medium'), 'ordinary Naruto clone model reasoning must stay low/medium', { modelEfforts: [...modelEfforts] });
|
|
39
|
+
assertGate([...efforts].every((effort) => effort === 'xhigh' || effort === 'max'), 'Naruto clone efforts must use only xhigh/max', { efforts: [...efforts] });
|
|
40
|
+
assertGate([...effortRoster.roster].every((row) => ['gpt-5.6-luna', 'gpt-5.6-terra', 'gpt-5.6-sol'].includes(row.model)), 'Naruto workers must use only the GPT-5.6 trio', { models: [...new Set(effortRoster.roster.map((row) => row.model))] });
|
|
41
|
+
assertGate([...modelTiers].every((tier) => /gpt-5\.6-(?:luna|terra|sol)-(?:xhigh|max)/.test(tier)), 'Naruto model tiers must record exact GPT-5.6 model and effort', { modelTiers: [...modelTiers] });
|
|
42
|
+
assertGate([...modelEfforts].every((effort) => effort === 'xhigh' || effort === 'max'), 'Naruto model reasoning must use xhigh/max', { modelEfforts: [...modelEfforts] });
|
|
45
43
|
assertGate([...tiers].length === 1 && tiers.has('fast'), 'every naruto clone must be fast service tier', { tiers: [...tiers] });
|
|
46
44
|
assertGate([...fastFlags].length === 1 && fastFlags.has(true), 'every naruto clone must have fast_mode=true', { fastFlags: [...fastFlags] });
|
|
47
|
-
// 4c)
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
assertGate(
|
|
54
|
-
assertGate(
|
|
55
|
-
assertGate(
|
|
56
|
-
assertGate(
|
|
45
|
+
// 4c) Deterministic work-kind matrix.
|
|
46
|
+
const coding = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'implementer', naruto_role: 'implementer' }, prompt: 'implement a normal coding feature' });
|
|
47
|
+
const criticalCoding = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'implementer', naruto_role: 'implementer' }, prompt: 'implement a critical security migration' });
|
|
48
|
+
const refactor = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'integrator', naruto_role: 'integrator' }, prompt: 'refactor the architecture and integration plan' });
|
|
49
|
+
const e2e = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'browser e2e verifier', naruto_role: 'verifier' }, prompt: 'run browser e2e tests' });
|
|
50
|
+
const forensicGui = effortPolicy.decideNarutoCloneEffort({ persona: { role: 'Computer Use GUI verifier', naruto_role: 'verifier' }, prompt: 'forensic cross-app failure verification' });
|
|
51
|
+
assertGate(coding.model === 'gpt-5.6-terra' && coding.model_reasoning_effort === 'xhigh', 'ordinary coding must use Terra xhigh', { coding });
|
|
52
|
+
assertGate(criticalCoding.model === 'gpt-5.6-terra' && criticalCoding.model_reasoning_effort === 'max', 'critical coding must use Terra max', { criticalCoding });
|
|
53
|
+
assertGate(refactor.model === 'gpt-5.6-sol' && refactor.model_reasoning_effort === 'max', 'refactor/architecture/integration must use Sol max', { refactor });
|
|
54
|
+
assertGate(e2e.model === 'gpt-5.6-luna' && e2e.model_reasoning_effort === 'xhigh', 'browser E2E must use Luna xhigh', { e2e });
|
|
55
|
+
assertGate(forensicGui.model === 'gpt-5.6-luna' && forensicGui.model_reasoning_effort === 'max', 'forensic Computer Use GUI verification must use Luna max', { forensicGui });
|
|
57
56
|
// 5) System-safe concurrency: never spawn the whole count at once; throttle to host capacity.
|
|
58
57
|
const fakeSafe = roster.systemSafeNarutoConcurrency({ backend: 'fake' });
|
|
59
58
|
const heavySafe = roster.systemSafeNarutoConcurrency({ backend: 'codex-sdk' });
|
|
@@ -91,47 +90,60 @@ const run = spawnSync(process.execPath, [
|
|
|
91
90
|
'--json',
|
|
92
91
|
'--no-open-zellij'
|
|
93
92
|
], { cwd: root, env: childEnv, encoding: 'utf8', timeout: 780000, maxBuffer: 8 * 1024 * 1024 });
|
|
94
|
-
assertGate(run.status === 0, 'sks naruto run must exit 0', { status: run.status, stderr: tail(run.stderr) });
|
|
95
93
|
const parsed = parseJson(run.stdout);
|
|
96
|
-
assertGate(parsed !== null, 'sks naruto run must emit JSON', { stdout: tail(run.stdout) });
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
assertGate(
|
|
100
|
-
assertGate(parsed.
|
|
101
|
-
assertGate(parsed.
|
|
102
|
-
|
|
103
|
-
assertGate(parsed.
|
|
104
|
-
assertGate(parsed.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
assertGate(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
assertGate(
|
|
112
|
-
assertGate(
|
|
113
|
-
assertGate(parsed.
|
|
114
|
-
assertGate(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
assertGate(
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
assertGate(
|
|
94
|
+
assertGate(parsed !== null, 'sks naruto run must emit JSON', { stdout: tail(run.stdout), stderr: tail(run.stderr) });
|
|
95
|
+
let state = {};
|
|
96
|
+
withMissionRouteClose(parsed, childEnv, () => {
|
|
97
|
+
assertGate(run.status === 0, 'sks naruto run must exit 0', { status: run.status, stderr: tail(run.stderr) });
|
|
98
|
+
assertGate(parsed.ok === true, 'naruto run must be ok', { ok: parsed.ok });
|
|
99
|
+
assertGate(parsed.mode === 'NARUTO' && parsed.jutsu === 'kage_bunshin_no_jutsu', 'naruto run must report NARUTO mode', { mode: parsed.mode, jutsu: parsed.jutsu });
|
|
100
|
+
assertGate(parsed.clones === proofClones, 'clone fan-out must use the requested queued count', { clones: parsed.clones });
|
|
101
|
+
assertGate(parsed.max_clones === 100, 'naruto run must advertise the 100 ceiling', { max_clones: parsed.max_clones });
|
|
102
|
+
assertGate(parsed.proof === 'passed', 'naruto run proof must pass', { proof: parsed.proof });
|
|
103
|
+
// Throttle invariant: active concurrency never exceeds the requested count nor the host cap.
|
|
104
|
+
assertGate(parsed.target_active_slots >= 1 && parsed.target_active_slots <= proofClones, 'active slots must be in [1, clones]', { target_active_slots: parsed.target_active_slots });
|
|
105
|
+
assertGate(parsed.target_active_slots <= fakeSafe.cap, 'active slots must be throttled to the system-safe cap', { target_active_slots: parsed.target_active_slots, cap: fakeSafe.cap });
|
|
106
|
+
assertGate(parsed.target_active_slots <= proofConcurrency && parsed.target_active_slots <= 4, 'release proof must keep Naruto live slots desktop-safe while queued fan-out remains larger', { target_active_slots: parsed.target_active_slots, proofConcurrency });
|
|
107
|
+
// Task 9.1: fan-out (clones) and live concurrency (target_active_slots) are reported
|
|
108
|
+
// distinctly, and concurrency_capped truthfully reflects "N clones, running M at a time".
|
|
109
|
+
assertGate(typeof parsed.concurrency_capped === 'boolean', 'naruto run must report concurrency_capped', { concurrency_capped: parsed.concurrency_capped });
|
|
110
|
+
assertGate(parsed.concurrency_capped === (parsed.clones > parsed.target_active_slots), 'concurrency_capped must reflect clones > live slots', { clones: parsed.clones, target_active_slots: parsed.target_active_slots, concurrency_capped: parsed.concurrency_capped });
|
|
111
|
+
assertGate(parsed.system && Number(parsed.system.safe_concurrency) >= 1, 'naruto run must report system safe_concurrency (host-derived cap)', { system: parsed.system });
|
|
112
|
+
assertGate(parsed.work_graph?.write_allowed_count > 0 && parsed.work_graph?.mixed_work_kinds?.length > 1, 'naruto run must report a mixed work graph with write-capable items', { work_graph: parsed.work_graph });
|
|
113
|
+
assertGate(Number(parsed.work_graph?.parallel_write_wave_count || 0) > 0, 'naruto run must expose at least one parallel write-capable wave with non-overlapping leases', { work_graph: parsed.work_graph });
|
|
114
|
+
assertGate(parsed.role_distribution?.verifier_only === false, 'naruto run proof/status must distinguish active worker roles beyond verifier-only', { role_distribution: parsed.role_distribution });
|
|
115
|
+
assertGate(Number(parsed.role_distribution?.implementation_like_ratio || 0) >= 0.4, 'naruto run must include implementation-like role distribution', { role_distribution: parsed.role_distribution });
|
|
116
|
+
assertGate(parsed.local_worker?.auto_select_eligible === false, 'normal Naruto must never auto-select a local/Ollama backend', { local_worker: parsed.local_worker });
|
|
117
|
+
const commandGraphPath = path.join(root, '.sneakoscope', 'missions', parsed.mission_id, 'agents', 'naruto-work-graph.json');
|
|
118
|
+
const commandGraph = JSON.parse(fs.readFileSync(commandGraphPath, 'utf8'));
|
|
119
|
+
const writePaths = commandGraph.work_items.filter((row) => row.write_allowed).flatMap((row) => row.write_paths);
|
|
120
|
+
assertGate(new Set(writePaths).size > 1, 'naruto command default patch-envelope leases must be per work item, not one shared write path', { sample: writePaths.slice(0, 8), unique: new Set(writePaths).size });
|
|
121
|
+
assertGate(commandGraph.active_waves.some((wave) => wave.write_paths.length > 1), 'naruto command graph must contain a parallel write wave when route-local patch envelopes do not overlap', { waves: commandGraph.active_waves.slice(0, 3) });
|
|
122
|
+
state = parsed.run?.scheduler?.state || parsed.run?.scheduler || {};
|
|
123
|
+
assertGate(Number(state.completed_count) === Number(parsed.work_graph?.total_work_items || 0) && Number(state.completed_count) >= proofClones, 'all clone work items must complete despite throttling', { completed_count: state.completed_count, total_work_items: parsed.work_graph?.total_work_items, proofClones });
|
|
124
|
+
assertGate(parsed.target_active_slots <= 4, 'explicit --concurrency cannot bypass the desktop-safe cap', { target_active_slots: parsed.target_active_slots });
|
|
125
|
+
});
|
|
123
126
|
// 7) A small request is never inflated; live pressure may still reduce it.
|
|
124
127
|
const small = spawnSync(process.execPath, [cli, 'naruto', 'run', 'tiny', '--clones', '2', '--backend', 'fake', '--work-items', '2', '--json', '--no-open-zellij'], { cwd: root, env: childEnv, encoding: 'utf8', timeout: 600000, maxBuffer: 4 * 1024 * 1024 });
|
|
125
128
|
const smallParsed = parseJson(small.stdout);
|
|
126
|
-
|
|
129
|
+
withMissionRouteClose(smallParsed, childEnv, () => {
|
|
130
|
+
assertGate(small.status === 0 && smallParsed?.target_active_slots >= 1 && smallParsed?.target_active_slots <= 2, 'a 2-clone run must stay within [1, 2] active workers', {
|
|
131
|
+
status: small.status,
|
|
132
|
+
target_active_slots: smallParsed?.target_active_slots,
|
|
133
|
+
stdout: tail(small.stdout),
|
|
134
|
+
stderr: tail(small.stderr)
|
|
135
|
+
});
|
|
136
|
+
});
|
|
127
137
|
// 8) Naruto clones always run fast. Per-route --no-fast / standard-tier requests
|
|
128
138
|
// are intentionally not honored for shadow clones.
|
|
129
139
|
const standardOptOut = spawnSync(process.execPath, [cli, 'naruto', 'run', 'fast opt-out ignored', '--clones', '2', '--backend', 'fake', '--work-items', '2', '--no-fast', '--service-tier', 'standard', '--json', '--no-open-zellij'], { cwd: root, env: childEnv, encoding: 'utf8', timeout: 600000, maxBuffer: 4 * 1024 * 1024 });
|
|
130
140
|
const standardOptOutParsed = parseJson(standardOptOut.stdout);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
withMissionRouteClose(standardOptOutParsed, childEnv, () => {
|
|
142
|
+
assertGate(standardOptOut.status === 0 && standardOptOutParsed?.fast_mode_policy?.fast_mode === true && standardOptOutParsed?.fast_mode_policy?.service_tier === 'fast', 'naruto --no-fast / --service-tier standard must still run clones in fast mode', {
|
|
143
|
+
status: standardOptOut.status,
|
|
144
|
+
fast_mode_policy: standardOptOutParsed?.fast_mode_policy,
|
|
145
|
+
stderr: tail(standardOptOut.stderr)
|
|
146
|
+
});
|
|
135
147
|
});
|
|
136
148
|
emitGate('naruto:shadow-clone-swarm', {
|
|
137
149
|
max_naruto_agent_count: schema.MAX_NARUTO_AGENT_COUNT,
|
|
@@ -162,4 +174,30 @@ function tail(value, limit = 2000) {
|
|
|
162
174
|
const text = String(value || '');
|
|
163
175
|
return text.length <= limit ? text : text.slice(-limit);
|
|
164
176
|
}
|
|
177
|
+
function closeMission(missionId, env) {
|
|
178
|
+
assertGate(Boolean(missionId), 'naruto proof mission id missing before route close', { missionId });
|
|
179
|
+
const closed = spawnSync(process.execPath, [cli, 'route', 'close', '--mission', String(missionId), '--json'], {
|
|
180
|
+
cwd: root,
|
|
181
|
+
env,
|
|
182
|
+
encoding: 'utf8',
|
|
183
|
+
timeout: 30000,
|
|
184
|
+
maxBuffer: 1024 * 1024
|
|
185
|
+
});
|
|
186
|
+
const parsedClose = parseJson(closed.stdout);
|
|
187
|
+
assertGate(closed.status === 0 && parsedClose?.ok === true, 'naruto proof mission route close must succeed', {
|
|
188
|
+
missionId,
|
|
189
|
+
status: closed.status,
|
|
190
|
+
stdout: tail(closed.stdout),
|
|
191
|
+
stderr: tail(closed.stderr)
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function withMissionRouteClose(parsedResult, env, verify) {
|
|
195
|
+
try {
|
|
196
|
+
return verify();
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
if (parsedResult?.mission_id)
|
|
200
|
+
closeMission(parsedResult.mission_id, env);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
165
203
|
//# sourceMappingURL=naruto-shadow-clone-swarm-check.js.map
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { DEFAULT_MAX_PACK_BYTES } from '../core/release/package-size-budget.js';
|
|
6
7
|
import { assertGate, emitGate, root } from './sks-1-18-gate-lib.js';
|
|
7
8
|
const MAX_FILES = Number(process.env.SKS_MAX_PACK_FILES || 2100);
|
|
8
|
-
const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES ||
|
|
9
|
+
const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || DEFAULT_MAX_PACK_BYTES);
|
|
9
10
|
const BUDGET_MS = Number(process.env.SKS_PACK_BUDGET_MS || 30000);
|
|
10
11
|
function runNpmPack() {
|
|
11
12
|
const npmCli = process.env.npm_execpath; // set when invoked via `npm run`
|
|
@@ -7,6 +7,14 @@ import { assertGate, emitGate, readJson, root } from './sks-1-18-gate-lib.js';
|
|
|
7
7
|
const pkg = readJson('package.json');
|
|
8
8
|
const scripts = pkg.scripts || {};
|
|
9
9
|
const cacheRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-package-published-contract-'));
|
|
10
|
+
let cacheCleaned = false;
|
|
11
|
+
const cleanupCacheRoot = () => {
|
|
12
|
+
if (cacheCleaned)
|
|
13
|
+
return;
|
|
14
|
+
cacheCleaned = true;
|
|
15
|
+
fs.rmSync(cacheRoot, { recursive: true, force: true });
|
|
16
|
+
};
|
|
17
|
+
process.once('exit', cleanupCacheRoot);
|
|
10
18
|
const dry = spawnSync('npm', ['pack', '--dry-run', '--json', '--ignore-scripts'], {
|
|
11
19
|
cwd: root,
|
|
12
20
|
encoding: 'utf8',
|
|
@@ -34,6 +42,7 @@ assertGate(missingTargets.length === 0, 'published package scripts must not refe
|
|
|
34
42
|
missingTargets: missingTargets.slice(0, 50),
|
|
35
43
|
missing_count: missingTargets.length
|
|
36
44
|
});
|
|
45
|
+
cleanupCacheRoot();
|
|
37
46
|
emitGate('package:published-contract', {
|
|
38
47
|
files: files.size,
|
|
39
48
|
script_count: Object.keys(scripts).length,
|
|
@@ -4,6 +4,7 @@ import fs from 'node:fs';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { DEFAULT_MAX_PACK_BYTES } from '../core/release/package-size-budget.js';
|
|
7
8
|
import { assertGate, emitGate, root } from './sks-1-18-gate-lib.js';
|
|
8
9
|
const MAX_FILES = Number(process.env.SKS_MAX_PACK_FILES || 2100);
|
|
9
10
|
const MAX_UNPACKED = Number(process.env.SKS_MAX_UNPACKED_BYTES || 10 * 1024 * 1024);
|
|
@@ -22,8 +23,10 @@ const MAX_UNPACKED = Number(process.env.SKS_MAX_UNPACKED_BYTES || 10 * 1024 * 10
|
|
|
22
23
|
// checks; keep those scripts publishable because package contract validation
|
|
23
24
|
// rejects scripts that point at files absent from the tarball. 5.12.0 adds ops
|
|
24
25
|
// maturity diagnostics, migration/recovery smokes, and their package-referenced
|
|
25
|
-
// gates, pushing the packed tarball to ~2400 KiB.
|
|
26
|
-
|
|
26
|
+
// gates, pushing the packed tarball to ~2400 KiB. 6.0.3 adds the menu restart
|
|
27
|
+
// lifecycle and Naruto GPT-5.6 routing enforcement, bringing the measured
|
|
28
|
+
// packed size to ~2411 KiB; keep the next 1 KiB boundary as the narrow cap.
|
|
29
|
+
const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || DEFAULT_MAX_PACK_BYTES);
|
|
27
30
|
const SURFACE_MAX_PACKED = Number(process.env.SKS_PACKAGE_SURFACE_MAX_PACK_BYTES || 25_000_000);
|
|
28
31
|
const SURFACE_MAX_FILES = Number(process.env.SKS_PACKAGE_SURFACE_MAX_FILES || 2500);
|
|
29
32
|
function runNpmPack() {
|
|
@@ -7,6 +7,8 @@ import { root, assertGate, emitGate, readText } from './sks-1-18-gate-lib.js';
|
|
|
7
7
|
// side-effects by default. This is STATIC source analysis only — we never execute
|
|
8
8
|
// `postinstall`, because doing so would mutate the global Codex App / shell environment.
|
|
9
9
|
const helpers = readText('src/cli/install-helpers.ts');
|
|
10
|
+
const desktopConfigPolicy = readText('src/core/codex-runtime/codex-desktop-config-policy.ts');
|
|
11
|
+
const codexConfigGuard = readText('src/core/codex/codex-config-guard.ts');
|
|
10
12
|
// Heavy CLI tool installs (brew / npm globals) are opt-in only.
|
|
11
13
|
assertGate(helpers.includes('SKS_POSTINSTALL_AUTO_INSTALL_CLI_TOOLS'), 'postinstall cli tool install must be gated behind SKS_POSTINSTALL_AUTO_INSTALL_CLI_TOOLS');
|
|
12
14
|
// Killing a third-party app's processes is opt-in only.
|
|
@@ -15,9 +17,12 @@ assertGate(helpers.includes('runPostinstallProjectRetentionCleanup') && helpers.
|
|
|
15
17
|
// Config writes are gated (unparseable preserved / unsafe rewrite skipped) and backed up.
|
|
16
18
|
assertGate(helpers.includes('unparseable_config_preserved'), 'postinstall must preserve unparseable config');
|
|
17
19
|
assertGate(helpers.includes('skipped_unsafe_rewrite'), 'postinstall must skip unsafe config rewrites');
|
|
18
|
-
assertGate(
|
|
19
|
-
//
|
|
20
|
-
|
|
20
|
+
assertGate(desktopConfigPolicy.includes('backupCodexConfig'), 'postinstall config writes must back up the existing config');
|
|
21
|
+
// Host-owned feature choices are set-if-absent, and model/reasoning keys are
|
|
22
|
+
// changed only when bounded SKS provenance proves SKS created them.
|
|
23
|
+
assertGate(desktopConfigPolicy.includes('upsertTomlTableKeyIfAbsent')
|
|
24
|
+
&& desktopConfigPolicy.includes('removeLegacyTopLevelCodexModeLocks')
|
|
25
|
+
&& codexConfigGuard.includes('hasSksModeLockProvenance'), 'postinstall must preserve host-owned feature/model choices');
|
|
21
26
|
// The postinstall() body must be wrapped in try/catch/finally so it never fails `npm install`.
|
|
22
27
|
const postinstallStart = helpers.indexOf('export async function postinstall');
|
|
23
28
|
assertGate(postinstallStart !== -1, 'postinstall function not found in install-helpers.ts');
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { assertGate, emitGate, readMissionJson, runPptReview } from './sks-1-11-gate-lib.js';
|
|
4
4
|
const result = runPptReview('review');
|
|
5
5
|
const proof = readMissionJson(result.mission_id, 'completion-proof.json');
|
|
6
|
-
assertGate(proof.status
|
|
6
|
+
assertGate(proof.status === 'mock_only'
|
|
7
|
+
&& result.proof_evidence.status === 'blocked'
|
|
8
|
+
&& (result.proof_evidence.blockers || []).some((blocker) => String(blocker).includes('mock_fixture')), 'ppt mock fixture was allowed to claim fully real evidence', { proof_status: proof.status, evidence_status: result.proof_evidence.status, blockers: result.proof_evidence.blockers || [] });
|
|
7
9
|
emitGate('ppt:no-mock-as-real', { mission_id: result.mission_id, proof_status: proof.status });
|
|
8
10
|
//# sourceMappingURL=ppt-no-mock-as-real-check.js.map
|
|
@@ -25,7 +25,7 @@ assertGate(PRODUCT_DESIGN_PLUGIN.app_server.install_params.remoteMarketplaceName
|
|
|
25
25
|
assertGate(PRODUCT_DESIGN_PLUGIN.app_server.read_params.pluginName === PRODUCT_DESIGN_PLUGIN.remote_plugin_id, 'plugin/read must use Product Design remote plugin id');
|
|
26
26
|
assertGate(PRODUCT_DESIGN_PLUGIN.app_server.install_params.pluginName === PRODUCT_DESIGN_PLUGIN.remote_plugin_id, 'plugin/install must use Product Design remote plugin id');
|
|
27
27
|
assertGate(PRODUCT_DESIGN_PLUGIN.app_server.list_params.marketplaceKinds.includes('vertical'), 'plugin/list must query vertical marketplaces');
|
|
28
|
-
for (const skill of ['audit', 'design-qa', 'get-context', 'ideate', 'image-to-code', '
|
|
28
|
+
for (const skill of ['audit', 'design-qa', 'get-context', 'ideate', 'image-to-code', 'index', 'research', 'share', 'url-to-code', 'user-context']) {
|
|
29
29
|
assertGate(PRODUCT_DESIGN_REQUIRED_SKILLS.includes(skill), `Product Design required skill missing: ${skill}`);
|
|
30
30
|
}
|
|
31
31
|
assertGate(PRODUCT_DESIGN_PIPELINE_STAGES.length >= 5, 'Product Design pipeline stage map too small');
|
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
8
8
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
9
9
|
const stampPath = process.env.SKS_RELEASE_STAMP_PATH || path.join(root, '.sneakoscope', 'reports', 'release-check-stamp.json');
|
|
10
10
|
const command = process.argv[2] || 'verify';
|
|
11
|
+
const commandArgs = process.argv.slice(3);
|
|
11
12
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
12
13
|
function fail(message, detail = '') {
|
|
13
14
|
console.error(`Release check stamp failed: ${message}`);
|
|
@@ -270,7 +271,7 @@ function currentStampPayload() {
|
|
|
270
271
|
include: (rel) => packageFileIncluded(`dist/${rel}`, packageEntries)
|
|
271
272
|
});
|
|
272
273
|
return {
|
|
273
|
-
schema: 'sks.release-check-stamp.
|
|
274
|
+
schema: 'sks.release-check-stamp.v2',
|
|
274
275
|
package_name: pkg.name,
|
|
275
276
|
package_version: pkg.version,
|
|
276
277
|
git_commit: gitCommit(),
|
|
@@ -285,8 +286,10 @@ function currentStampPayload() {
|
|
|
285
286
|
};
|
|
286
287
|
}
|
|
287
288
|
function writeStamp() {
|
|
289
|
+
const releaseGateProof = fullReleaseGateProofForWrite();
|
|
288
290
|
const payload = {
|
|
289
291
|
...currentStampPayload(),
|
|
292
|
+
release_gate_proof: releaseGateProof,
|
|
290
293
|
generated_at: new Date().toISOString()
|
|
291
294
|
};
|
|
292
295
|
fs.mkdirSync(path.dirname(stampPath), { recursive: true });
|
|
@@ -318,6 +321,9 @@ function inspectStamp() {
|
|
|
318
321
|
if (stamp[key] !== current[key])
|
|
319
322
|
mismatches.push(`${key}: stamp=${stamp[key] || 'missing'} current=${current[key] || 'missing'}`);
|
|
320
323
|
}
|
|
324
|
+
const proofInspection = inspectFullReleaseGateProof(stamp.release_gate_proof);
|
|
325
|
+
if (!proofInspection.ok)
|
|
326
|
+
mismatches.push(...proofInspection.blockers.map((blocker) => `release_gate_proof:${blocker}`));
|
|
321
327
|
if (mismatches.length) {
|
|
322
328
|
return {
|
|
323
329
|
ok: false,
|
|
@@ -328,6 +334,180 @@ function inspectStamp() {
|
|
|
328
334
|
}
|
|
329
335
|
return { ok: true, current };
|
|
330
336
|
}
|
|
337
|
+
function fullReleaseGateProofForWrite() {
|
|
338
|
+
const preset = argValue('--preset');
|
|
339
|
+
const full = commandArgs.includes('--full');
|
|
340
|
+
if (preset !== 'release' || !full) {
|
|
341
|
+
fail('full release proof required to write publish stamp', 'Use `npm run release:check:full`; affected/fast/confidence checks cannot authorize publish.');
|
|
342
|
+
}
|
|
343
|
+
const explicit = argValue('--summary');
|
|
344
|
+
const summaryPath = explicit ? path.resolve(root, explicit) : latestFullReleaseSummaryPath();
|
|
345
|
+
if (!summaryPath)
|
|
346
|
+
fail('full release DAG summary missing', 'Run `npm run release:check:full` before writing the publish stamp.');
|
|
347
|
+
const reportRoot = path.resolve(root, '.sneakoscope', 'reports', 'release-gates');
|
|
348
|
+
const relative = path.relative(reportRoot, summaryPath);
|
|
349
|
+
if (relative.startsWith('..') || path.isAbsolute(relative))
|
|
350
|
+
fail('release DAG summary is outside the managed report root');
|
|
351
|
+
let summary;
|
|
352
|
+
try {
|
|
353
|
+
summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
fail('unable to read full release DAG summary', error?.message || String(error));
|
|
357
|
+
}
|
|
358
|
+
const validation = validateFullReleaseSummary(summary);
|
|
359
|
+
if (validation.length)
|
|
360
|
+
fail('release DAG summary is not full publish proof', validation.join('\n'));
|
|
361
|
+
const explicitRealSummary = argValue('--real-summary');
|
|
362
|
+
const realSummaryPath = explicitRealSummary
|
|
363
|
+
? path.resolve(root, explicitRealSummary)
|
|
364
|
+
: path.join(root, '.sneakoscope', 'reports', 'release-real-check.json');
|
|
365
|
+
const managedReports = path.resolve(root, '.sneakoscope', 'reports');
|
|
366
|
+
const realRelative = path.relative(managedReports, realSummaryPath);
|
|
367
|
+
if (realRelative.startsWith('..') || path.isAbsolute(realRelative))
|
|
368
|
+
fail('release real-check summary is outside the managed report root');
|
|
369
|
+
let realSummary;
|
|
370
|
+
try {
|
|
371
|
+
realSummary = JSON.parse(fs.readFileSync(realSummaryPath, 'utf8'));
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
fail('unable to read release real-check summary', error?.message || String(error));
|
|
375
|
+
}
|
|
376
|
+
const realValidation = validateReleaseRealSummary(realSummary);
|
|
377
|
+
if (realValidation.length)
|
|
378
|
+
fail('release real-check summary is not publish proof', realValidation.join('\n'));
|
|
379
|
+
if (fs.statSync(realSummaryPath).mtimeMs < fs.statSync(summaryPath).mtimeMs)
|
|
380
|
+
fail('release real-check predates the full release DAG summary');
|
|
381
|
+
return {
|
|
382
|
+
schema: 'sks.release-check-full-proof.v1',
|
|
383
|
+
preset: 'release',
|
|
384
|
+
full: true,
|
|
385
|
+
run_id: summary.run_id,
|
|
386
|
+
summary_path: path.relative(root, summaryPath).split(path.sep).join('/'),
|
|
387
|
+
summary_sha256: sha256(fs.readFileSync(summaryPath)),
|
|
388
|
+
selected_gates: summary.selected_gates,
|
|
389
|
+
completed: summary.completed,
|
|
390
|
+
failed: summary.failed,
|
|
391
|
+
affected_mode: summary.affected_selection?.mode,
|
|
392
|
+
confidence: summary.completion_certificate?.confidence || null,
|
|
393
|
+
real_check_path: path.relative(root, realSummaryPath).split(path.sep).join('/'),
|
|
394
|
+
real_check_sha256: sha256(fs.readFileSync(realSummaryPath)),
|
|
395
|
+
real_check_count: realSummary.all_checks.length
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function latestFullReleaseSummaryPath() {
|
|
399
|
+
const reportRoot = path.join(root, '.sneakoscope', 'reports', 'release-gates');
|
|
400
|
+
if (!fs.existsSync(reportRoot))
|
|
401
|
+
return null;
|
|
402
|
+
return fs.readdirSync(reportRoot, { withFileTypes: true })
|
|
403
|
+
.filter((entry) => entry.isDirectory())
|
|
404
|
+
.map((entry) => path.join(reportRoot, entry.name, 'summary.json'))
|
|
405
|
+
.filter((file) => fs.existsSync(file))
|
|
406
|
+
.map((file) => ({ file, stat: fs.statSync(file) }))
|
|
407
|
+
.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
|
|
408
|
+
.map((row) => row.file)
|
|
409
|
+
.find((file) => {
|
|
410
|
+
try {
|
|
411
|
+
return validateFullReleaseSummary(JSON.parse(fs.readFileSync(file, 'utf8'))).length === 0;
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
}) || null;
|
|
417
|
+
}
|
|
418
|
+
function validateFullReleaseSummary(summary) {
|
|
419
|
+
const blockers = [];
|
|
420
|
+
if (summary?.schema !== 'sks.release-gate-dag-run.v1')
|
|
421
|
+
blockers.push('summary_schema_invalid');
|
|
422
|
+
if (summary?.ok !== true)
|
|
423
|
+
blockers.push('summary_not_ok');
|
|
424
|
+
if (summary?.selected_preset !== 'release')
|
|
425
|
+
blockers.push('preset_not_release');
|
|
426
|
+
if (summary?.affected_selection?.mode !== 'full')
|
|
427
|
+
blockers.push('affected_mode_not_full');
|
|
428
|
+
if (!Number.isInteger(summary?.selected_gates) || summary.selected_gates <= 0)
|
|
429
|
+
blockers.push('selected_gates_empty');
|
|
430
|
+
if (summary?.failed !== 0)
|
|
431
|
+
blockers.push('failed_gates_present');
|
|
432
|
+
if (summary?.completed !== summary?.selected_gates)
|
|
433
|
+
blockers.push('not_all_selected_gates_completed');
|
|
434
|
+
if (!Array.isArray(summary?.selected_gate_ids) || summary.selected_gate_ids.length !== summary.selected_gates)
|
|
435
|
+
blockers.push('selected_gate_ids_incomplete');
|
|
436
|
+
if (summary?.completion_certificate?.confidence !== 'full-release-proof')
|
|
437
|
+
blockers.push('completion_confidence_not_full');
|
|
438
|
+
if (summary?.completion_certificate?.full_release_proof !== 'current_run')
|
|
439
|
+
blockers.push('full_release_proof_not_current_run');
|
|
440
|
+
return blockers;
|
|
441
|
+
}
|
|
442
|
+
function validateReleaseRealSummary(summary) {
|
|
443
|
+
const blockers = [];
|
|
444
|
+
if (summary?.schema !== 'sks.release-real-check.v1')
|
|
445
|
+
blockers.push('real_check_schema_invalid');
|
|
446
|
+
if (summary?.ok !== true)
|
|
447
|
+
blockers.push('real_check_not_ok');
|
|
448
|
+
if (!Array.isArray(summary?.all_checks) || summary.all_checks.length === 0)
|
|
449
|
+
blockers.push('real_check_empty');
|
|
450
|
+
else if (summary.all_checks.some((row) => row?.ok !== true))
|
|
451
|
+
blockers.push('real_check_failures_present');
|
|
452
|
+
return blockers;
|
|
453
|
+
}
|
|
454
|
+
function inspectFullReleaseGateProof(proof) {
|
|
455
|
+
const blockers = [];
|
|
456
|
+
if (proof?.schema !== 'sks.release-check-full-proof.v1')
|
|
457
|
+
blockers.push('proof_schema_invalid');
|
|
458
|
+
if (proof?.preset !== 'release' || proof?.full !== true)
|
|
459
|
+
blockers.push('proof_not_full_release');
|
|
460
|
+
const summaryPath = proof?.summary_path ? path.resolve(root, proof.summary_path) : null;
|
|
461
|
+
const reportRoot = path.resolve(root, '.sneakoscope', 'reports', 'release-gates');
|
|
462
|
+
if (!summaryPath || !fs.existsSync(summaryPath))
|
|
463
|
+
blockers.push('summary_missing');
|
|
464
|
+
else {
|
|
465
|
+
const relative = path.relative(reportRoot, summaryPath);
|
|
466
|
+
if (relative.startsWith('..') || path.isAbsolute(relative))
|
|
467
|
+
blockers.push('summary_outside_managed_reports');
|
|
468
|
+
else {
|
|
469
|
+
const bytes = fs.readFileSync(summaryPath);
|
|
470
|
+
if (sha256(bytes) !== proof.summary_sha256)
|
|
471
|
+
blockers.push('summary_hash_mismatch');
|
|
472
|
+
try {
|
|
473
|
+
const summary = JSON.parse(bytes);
|
|
474
|
+
blockers.push(...validateFullReleaseSummary(summary));
|
|
475
|
+
if (summary.run_id !== proof.run_id || summary.selected_gates !== proof.selected_gates || summary.completed !== proof.completed || summary.failed !== proof.failed)
|
|
476
|
+
blockers.push('summary_identity_mismatch');
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
blockers.push('summary_parse_failed');
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const realCheckPath = proof?.real_check_path ? path.resolve(root, proof.real_check_path) : null;
|
|
484
|
+
if (!realCheckPath || !fs.existsSync(realCheckPath))
|
|
485
|
+
blockers.push('real_check_summary_missing');
|
|
486
|
+
else {
|
|
487
|
+
const relative = path.relative(path.resolve(root, '.sneakoscope', 'reports'), realCheckPath);
|
|
488
|
+
if (relative.startsWith('..') || path.isAbsolute(relative))
|
|
489
|
+
blockers.push('real_check_summary_outside_managed_reports');
|
|
490
|
+
else {
|
|
491
|
+
const bytes = fs.readFileSync(realCheckPath);
|
|
492
|
+
if (sha256(bytes) !== proof.real_check_sha256)
|
|
493
|
+
blockers.push('real_check_summary_hash_mismatch');
|
|
494
|
+
try {
|
|
495
|
+
const realSummary = JSON.parse(bytes);
|
|
496
|
+
blockers.push(...validateReleaseRealSummary(realSummary));
|
|
497
|
+
if (realSummary.all_checks?.length !== proof.real_check_count)
|
|
498
|
+
blockers.push('real_check_identity_mismatch');
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
blockers.push('real_check_summary_parse_failed');
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return { ok: blockers.length === 0, blockers: [...new Set(blockers)] };
|
|
506
|
+
}
|
|
507
|
+
function argValue(name) {
|
|
508
|
+
const index = commandArgs.indexOf(name);
|
|
509
|
+
return index >= 0 ? commandArgs[index + 1] || null : null;
|
|
510
|
+
}
|
|
331
511
|
function verifyStamp() {
|
|
332
512
|
const result = inspectStamp();
|
|
333
513
|
if (!result.ok)
|
|
@@ -359,5 +539,5 @@ else if (command === 'verify')
|
|
|
359
539
|
else if (command === 'ensure')
|
|
360
540
|
ensureStamp();
|
|
361
541
|
else
|
|
362
|
-
fail(`unknown command ${command}`, 'Usage: node ./dist/scripts/release-check-stamp.js <write|verify|ensure>');
|
|
542
|
+
fail(`unknown command ${command}`, 'Usage: node ./dist/scripts/release-check-stamp.js <write --preset release --full [--summary path]|verify|ensure>');
|
|
363
543
|
//# sourceMappingURL=release-check-stamp.js.map
|
|
@@ -14,14 +14,14 @@ const requiredReleasePresetIds = [
|
|
|
14
14
|
'qa-loop:comprehensive-verification',
|
|
15
15
|
'loop-integration-finalizer-check',
|
|
16
16
|
'naruto:canonical-stop-gate',
|
|
17
|
-
'agent:native-cli-session-swarm',
|
|
18
|
-
'agent:
|
|
19
|
-
'
|
|
20
|
-
'release:dag-
|
|
17
|
+
'agent:native-cli-session-swarm-scaling',
|
|
18
|
+
'agent:fast-mode-policy',
|
|
19
|
+
'codex-control:event-stream-ledger',
|
|
20
|
+
'release:dag-runner',
|
|
21
21
|
'release:gate-budget',
|
|
22
|
-
'release:gate-
|
|
22
|
+
'release:gate-selection-comprehensive',
|
|
23
23
|
'policy:gate-audit',
|
|
24
|
-
'runtime:
|
|
24
|
+
'runtime:proof-summary',
|
|
25
25
|
'typecheck'
|
|
26
26
|
];
|
|
27
27
|
const requiredHarnessPresetIds = [
|
|
@@ -42,7 +42,7 @@ const schemaComplete = [...releaseManifest.gates, ...harnessManifest.gates].ever
|
|
|
42
42
|
const report = {
|
|
43
43
|
schema: 'sks.release-dag-full-coverage-check.v2',
|
|
44
44
|
ok: schemaComplete
|
|
45
|
-
&& releasePreset.length <=
|
|
45
|
+
&& releasePreset.length <= 200
|
|
46
46
|
&& missingRequiredReleasePreset.length === 0
|
|
47
47
|
&& missingRequiredHarnessPreset.length === 0
|
|
48
48
|
&& duplicateAcrossManifests.length === 0
|
|
@@ -15,6 +15,14 @@ assertGate(String(scripts['release:check:affected']).includes('build:incremental
|
|
|
15
15
|
assertGate(dag.includes('selectAffectedReleaseGates'), 'release DAG must use affected selector');
|
|
16
16
|
assertGate(dagMod.selectReleaseGatePreset(manifest, 'affected').length === releaseCount, 'affected preset must select the release gate universe before affected filtering');
|
|
17
17
|
assertGate(dagMod.selectReleaseGatePreset(manifest, 'fast').length === releaseCount, 'fast preset must select the release gate universe before affected filtering');
|
|
18
|
+
let unknownPresetBlocked = false;
|
|
19
|
+
try {
|
|
20
|
+
dagMod.selectReleaseGatePreset(manifest, 'unknown-preset');
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
unknownPresetBlocked = /release_gate_preset_empty_or_unknown/.test(String(error?.message || error));
|
|
24
|
+
}
|
|
25
|
+
assertGate(unknownPresetBlocked, 'unknown or empty release presets must fail closed');
|
|
18
26
|
assertGate(runner.includes('--changed-since') && runner.includes('--full') && runner.includes('--sla'), 'release DAG runner must parse changed-since/full/sla');
|
|
19
27
|
emitGate('release:dynamic-presets', { presets: 4 });
|
|
20
28
|
//# sourceMappingURL=release-dynamic-presets-check.js.map
|
|
@@ -17,7 +17,8 @@ const effectiveReleaseCheck = delegatedReleaseCheck ? String(pkg.scripts[delegat
|
|
|
17
17
|
assertGate(effectiveReleaseCheck.includes('release-gate-dag-runner.js'), 'release:check must execute DAG runner', { release_check: releaseCheck, delegated_release_check: delegatedReleaseCheck || null, effective_release_check: effectiveReleaseCheck });
|
|
18
18
|
assertGate(!/&&\s*npm run\s+\w/.test(releaseCheck.replace('npm run build --silent &&', '')), 'release:check must not be a giant npm-run chain', releaseCheck);
|
|
19
19
|
assertGate(!/&&\s*npm run\s+\w/.test(effectiveReleaseCheck.replace('npm run build:incremental --silent &&', '').replace('npm run build --silent &&', '')), 'effective release:check must not be a giant npm-run chain', effectiveReleaseCheck);
|
|
20
|
-
assertGate(pkg.scripts['release:check:legacy'], 'release:check:legacy must
|
|
20
|
+
assertGate(!pkg.scripts['release:check:legacy'], 'retired release:check:legacy alias must stay removed');
|
|
21
|
+
assertGate(pkg.scripts['release:real-check'] === 'node ./dist/scripts/release-real-check.js', 'release:real-check must expose the canonical environment-dependent proof runner');
|
|
21
22
|
assertGate(manifest.schema === 'sks.release-gates.v2' && manifest.gates.length >= 10, 'release-gates.v2 manifest must exist with nodes', manifest);
|
|
22
23
|
for (const gateId of [
|
|
23
24
|
'scheduler:comprehensive',
|
|
@@ -37,7 +38,7 @@ assertGate(runner.includes('detached: process.platform') && runner.includes('kil
|
|
|
37
38
|
assertGate(runner.includes('pruneOldReleaseGateRunDirs') && runner.includes('SKS_RELEASE_GATE_RUN_RETENTION'), 'DAG runner must prune stale release-gate run reports before they exhaust local disk');
|
|
38
39
|
assertGate(runnerCli.includes('ensureCurrentMigrationBeforeCommand') && runnerCli.includes('release-gate-runner-preflight'), 'DAG runner CLI must self-heal project migration before hermetic gates run');
|
|
39
40
|
assertGate(hermeticEnv.includes('SKS_UPDATE_MIGRATION_GATE_DISABLED'), 'hermetic release gates must not rewrite project migration receipts from temporary HOME roots');
|
|
40
|
-
assertGate(hermeticEnv.includes('legacy:update-e2e') && hermeticEnv.includes('
|
|
41
|
+
assertGate(hermeticEnv.includes("input.gate.id === 'legacy:update-e2e'") && hermeticEnv.includes('delete gateEnv.SKS_UPDATE_MIGRATION_GATE_DISABLED'), 'legacy:update-e2e must explicitly remove an inherited disable flag while testing migration retry/repair behavior');
|
|
41
42
|
const dag = await importDist('core/release/release-gate-dag.js');
|
|
42
43
|
const fsx = await importDist('core/fsx.js');
|
|
43
44
|
const fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-release-dag-timeout-'));
|