sneakoscope 8.3.2 → 8.4.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.
Files changed (105) hide show
  1. package/README.md +1 -1
  2. package/config/architecture-map-policy.v1.json +168 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/commands/bridge.js +19 -1
  6. package/dist/commands/doctor-profile.js +1 -1
  7. package/dist/commands/doctor.js +33 -1
  8. package/dist/config/skills-manifest.json +1 -1
  9. package/dist/core/align/align-route.js +8 -2
  10. package/dist/core/align/code-navigation-align.js +88 -32
  11. package/dist/core/architecture-map-pipeline.js +172 -0
  12. package/dist/core/architecture-map-review.js +180 -0
  13. package/dist/core/codex-app/codex-app-fast-ui-repair.js +2 -1
  14. package/dist/core/codex-app/menubar/constants.js +1 -0
  15. package/dist/core/codex-lb/codex-lb-env.js +25 -3
  16. package/dist/core/codex-lb/combined-catalog/builder.js +7 -2
  17. package/dist/core/codex-lb/combined-catalog/model-selection.js +71 -0
  18. package/dist/core/codex-lb/combined-catalog/normalize.js +20 -3
  19. package/dist/core/codex-lb/desktop-controller-v3/catalog.js +75 -4
  20. package/dist/core/codex-lb/desktop-controller-v3.js +5 -1
  21. package/dist/core/commands/triwiki-atlas-command.js +372 -0
  22. package/dist/core/commands/triwiki-command.js +12 -1
  23. package/dist/core/fsx.js +14 -0
  24. package/dist/core/git-hygiene/git-policy.js +4 -0
  25. package/dist/core/git-hygiene/gitignore-writer.js +2 -0
  26. package/dist/core/gx-renderer.js +2 -1
  27. package/dist/core/managed-paths.js +1 -0
  28. package/dist/core/perf/release-latency-slo.js +57 -11
  29. package/dist/core/pipeline-internals/pipeline-stage-builder.js +181 -0
  30. package/dist/core/pipeline-internals/runtime-core.js +13 -172
  31. package/dist/core/pipeline-internals/runtime-gates.js +12 -1
  32. package/dist/core/release/content-digest-cache.js +70 -0
  33. package/dist/core/release/gate-manifest.js +9 -0
  34. package/dist/core/release/main-push-guard.js +4 -3
  35. package/dist/core/release/package-dist-snapshot.js +18 -2
  36. package/dist/core/release/physical-release-gates.js +26 -3
  37. package/dist/core/release/release-authorization-snapshot.js +4 -0
  38. package/dist/core/release/release-gate-cache-v2.js +27 -3
  39. package/dist/core/release/release-gate-contract.js +2 -0
  40. package/dist/core/release/release-gate-dag.js +3 -0
  41. package/dist/core/release/release-inspection-memo.js +69 -0
  42. package/dist/core/release/release-pack-content-scanner.js +5 -0
  43. package/dist/core/release/release-pack-receipt.js +28 -18
  44. package/dist/core/release/release-pack-tarball.js +10 -0
  45. package/dist/core/release/release-upgrade-baseline.js +4 -0
  46. package/dist/core/routes.js +1 -1
  47. package/dist/core/safety/ssot-authority-inventory.js +114 -0
  48. package/dist/core/safety/ssot-guard.js +2 -44
  49. package/dist/core/triwiki/context-graph/architecture/baseline.js +81 -0
  50. package/dist/core/triwiki/context-graph/architecture/capsule.js +34 -0
  51. package/dist/core/triwiki/context-graph/architecture/contracts.js +67 -0
  52. package/dist/core/triwiki/context-graph/architecture/delta.js +149 -0
  53. package/dist/core/triwiki/context-graph/architecture/finding-factory.js +28 -0
  54. package/dist/core/triwiki/context-graph/architecture/findings.js +173 -0
  55. package/dist/core/triwiki/context-graph/architecture/fingerprint.js +75 -0
  56. package/dist/core/triwiki/context-graph/architecture/index.js +15 -0
  57. package/dist/core/triwiki/context-graph/architecture/input-bundle.js +66 -0
  58. package/dist/core/triwiki/context-graph/architecture/metrics.js +137 -0
  59. package/dist/core/triwiki/context-graph/architecture/policy.js +321 -0
  60. package/dist/core/triwiki/context-graph/architecture/slice.js +100 -0
  61. package/dist/core/triwiki/context-graph/architecture/ssot-analysis.js +59 -0
  62. package/dist/core/triwiki/context-graph/architecture/topology-overlay.js +106 -0
  63. package/dist/core/triwiki/context-graph/architecture/validation.js +145 -0
  64. package/dist/core/triwiki/context-graph/architecture/voxel-context.js +39 -0
  65. package/dist/core/triwiki/context-graph/extractors/index.js +4 -0
  66. package/dist/core/triwiki/context-graph/projections/index.js +2 -0
  67. package/dist/core/triwiki/context-graph/projections/mermaid/architecture-delta.js +68 -0
  68. package/dist/core/triwiki/context-graph/projections/mermaid/ast.js +49 -0
  69. package/dist/core/triwiki/context-graph/projections/mermaid/change-impact.js +43 -0
  70. package/dist/core/triwiki/context-graph/projections/mermaid/contracts.js +5 -0
  71. package/dist/core/triwiki/context-graph/projections/mermaid/escape.js +45 -0
  72. package/dist/core/triwiki/context-graph/projections/mermaid/ids.js +73 -0
  73. package/dist/core/triwiki/context-graph/projections/mermaid/index.js +113 -0
  74. package/dist/core/triwiki/context-graph/projections/mermaid/manifest.js +37 -0
  75. package/dist/core/triwiki/context-graph/projections/mermaid/module-dependency.js +21 -0
  76. package/dist/core/triwiki/context-graph/projections/mermaid/ownership-workstream.js +21 -0
  77. package/dist/core/triwiki/context-graph/projections/mermaid/project-topology.js +13 -0
  78. package/dist/core/triwiki/context-graph/projections/mermaid/public-surface.js +17 -0
  79. package/dist/core/triwiki/context-graph/projections/mermaid/reduce.js +44 -0
  80. package/dist/core/triwiki/context-graph/projections/mermaid/risk-domains.js +23 -0
  81. package/dist/core/triwiki/context-graph/projections/mermaid/runtime-control.js +20 -0
  82. package/dist/core/triwiki/context-graph/projections/mermaid/selection-ledger.js +94 -0
  83. package/dist/core/triwiki/context-graph/projections/mermaid/serializer.js +81 -0
  84. package/dist/core/triwiki/context-graph/projections/mermaid/ssot-provenance.js +17 -0
  85. package/dist/core/triwiki/context-graph/projections/mermaid/verification-coverage.js +17 -0
  86. package/dist/core/triwiki/context-graph/projections/mermaid/view-builder-common.js +1 -0
  87. package/dist/core/triwiki/context-graph/projections/mermaid/view-builder.js +173 -0
  88. package/dist/core/triwiki/context-graph/projections/mermaid/view-specs.js +33 -0
  89. package/dist/core/triwiki/context-graph/store/architecture-map-store.js +71 -0
  90. package/dist/core/triwiki/triwiki-proof-bank-index.js +26 -1
  91. package/dist/core/triwiki/triwiki-proof-bank.js +82 -2
  92. package/dist/core/version.js +1 -1
  93. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +2 -2
  94. package/dist/native/sks-menubar/Sources/ProvidersModelExposure.swift +178 -0
  95. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +10 -1
  96. package/dist/scripts/architecture-map-check.js +299 -0
  97. package/dist/scripts/lib/ensure-dist-fresh.js +48 -1
  98. package/dist/scripts/release-metadata-check.js +2 -0
  99. package/package.json +6 -3
  100. package/release-gates.v2.json +204 -0
  101. package/schemas/architecture-baseline.v1.schema.json +47 -0
  102. package/schemas/architecture-map-manifest.v1.schema.json +40 -0
  103. package/schemas/architecture-map-policy.v1.schema.json +147 -0
  104. package/schemas/architecture-review.v1.schema.json +46 -0
  105. package/schemas/mermaid-projection.v1.schema.json +47 -0
@@ -1,4 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
2
3
  import fs from 'node:fs/promises';
3
4
  import os from 'node:os';
4
5
  import path from 'node:path';
@@ -31,7 +32,7 @@ export async function runReleaseLatencySlo(root, budgets, options = {}) {
31
32
  await capture('ssh_worker_hello', budgetMap, () => measureSshHello(tmp, runs(budgetMap, 'ssh_worker_hello', 5)))
32
33
  ];
33
34
  const native = platform === 'darwin'
34
- ? await measureNativeMenuBar(tmp, budgetMap).catch((error) => nativeFailureMeasurements(budgetMap, error))
35
+ ? await measureNativeMenuBar(root, budgetMap).catch((error) => nativeFailureMeasurements(budgetMap, error))
35
36
  : ['menubar_first_state_render', 'control_center_open'].map((id) => ({
36
37
  id,
37
38
  budget_p95_ms: budgetMap.get(id)?.budget_p95_ms ?? RELEASE_LATENCY_LIMITS[id],
@@ -140,10 +141,11 @@ async function measureSshHello(tmp, count) {
140
141
  }
141
142
  return { producer: 'RemoteSshWorkerClient.connect:local_stdio_child', samples: values, evidence: { transport: 'local_process_ssh_stdio_fixture', network_included: false, process_startup_included: true } };
142
143
  }
143
- async function measureNativeMenuBar(tmp, budgets) {
144
+ async function measureNativeMenuBar(root, budgets) {
145
+ const tmp = path.join(root, '.sneakoscope', 'cache', 'menubar-latency');
144
146
  const sourceDir = path.join(tmp, 'native-sources');
145
- const executable = path.join(tmp, 'sks-menubar-latency');
146
147
  const home = path.join(tmp, 'native-home');
148
+ await fs.rm(home, { recursive: true, force: true });
147
149
  await Promise.all([sourceDir, home].map((dir) => fs.mkdir(dir, { recursive: true })));
148
150
  const cache = path.join(home, '.sneakoscope-global', 'cache', 'update-status.json');
149
151
  const cacheSnapshot = emptyUpdateStatus('6.3.0', new Date());
@@ -169,13 +171,9 @@ async function measureNativeMenuBar(tmp, budgets) {
169
171
  await fs.writeFile(path.join(sourceDir, source.name), content, { mode: 0o600 });
170
172
  }
171
173
  const files = sources.map((source) => path.join(sourceDir, source.name));
172
- const compileStarted = performance.now();
173
- const compile = await runProcess('swiftc', ['-framework', 'Cocoa', '-framework', 'UserNotifications', ...files, '-o', executable], {
174
- cwd: tmp, timeoutMs: 90_000, maxOutputBytes: 128 * 1024
175
- });
176
- const compileMs = round(performance.now() - compileStarted);
177
- if (compile.code !== 0)
178
- throw new Error(`menubar_latency_compile_failed:${compile.stderr.slice(-400)}`);
174
+ const build = await buildNativeMenuBarHarness(tmp, files);
175
+ const executable = build.executable;
176
+ const compileMs = build.compile_duration_ms;
179
177
  const nativeRuns = Math.max(runs(budgets, 'menubar_first_state_render', 5), runs(budgets, 'control_center_open', 5));
180
178
  const first = [];
181
179
  const control = [];
@@ -191,7 +189,8 @@ async function measureNativeMenuBar(tmp, budgets) {
191
189
  control.push(Number(parsed.control_center_open_ms));
192
190
  }
193
191
  const evidence = {
194
- source_count: sources.length, compile_exit_code: compile.code, compile_duration_ms: compileMs,
192
+ source_count: sources.length, compile_exit_code: 0, compile_duration_ms: compileMs,
193
+ compile_reused: build.reused, compile_source_digest: build.source_digest,
195
194
  link_frameworks: ['Cocoa', 'UserNotifications'], run_exit_codes: exitCodes,
196
195
  cache_based: true, cache_bytes: Buffer.byteLength(JSON.stringify(cacheSnapshot)), controller_prebuilt: true
197
196
  };
@@ -200,6 +199,53 @@ async function measureNativeMenuBar(tmp, budgets) {
200
199
  evaluateLatencySamples('control_center_open', budgets.get('control_center_open')?.budget_p95_ms ?? 400, 'ControlCenterWindowController.show:overview', control, evidence)
201
200
  ];
202
201
  }
202
+ async function buildNativeMenuBarHarness(buildRoot, files) {
203
+ const toolchain = await runProcess('swiftc', ['--version'], { cwd: buildRoot, timeoutMs: 30_000, maxOutputBytes: 32 * 1024 });
204
+ const digest = createHash('sha256');
205
+ digest.update(toolchain.code === 0 ? toolchain.stdout : 'swiftc_version_unavailable');
206
+ digest.update('\0');
207
+ for (const file of [...files].sort()) {
208
+ digest.update(path.basename(file));
209
+ digest.update('\0');
210
+ digest.update(await fs.readFile(file));
211
+ digest.update('\0');
212
+ }
213
+ const sourceDigest = digest.digest('hex');
214
+ const binDir = path.join(buildRoot, 'bin');
215
+ const executable = path.join(binDir, `sks-menubar-latency-${sourceDigest}`);
216
+ await fs.mkdir(binDir, { recursive: true });
217
+ if (await pathIsFile(executable)) {
218
+ return { executable, compile_duration_ms: 0, source_digest: sourceDigest, reused: true };
219
+ }
220
+ const staged = `${executable}.${process.pid}.tmp`;
221
+ const compileStarted = performance.now();
222
+ const compile = await runProcess('swiftc', ['-framework', 'Cocoa', '-framework', 'UserNotifications', ...files, '-o', staged], {
223
+ cwd: buildRoot, timeoutMs: 90_000, maxOutputBytes: 128 * 1024
224
+ });
225
+ const compileMs = round(performance.now() - compileStarted);
226
+ if (compile.code !== 0) {
227
+ await fs.rm(staged, { force: true });
228
+ throw new Error(`menubar_latency_compile_failed:${compile.stderr.slice(-400)}`);
229
+ }
230
+ await fs.rename(staged, executable);
231
+ await pruneNativeHarnessBinaries(binDir, executable);
232
+ return { executable, compile_duration_ms: compileMs, source_digest: sourceDigest, reused: false };
233
+ }
234
+ async function pruneNativeHarnessBinaries(binDir, keep) {
235
+ const entries = await fs.readdir(binDir).catch(() => []);
236
+ await Promise.all(entries
237
+ .map((name) => path.join(binDir, name))
238
+ .filter((file) => file !== keep)
239
+ .map((file) => fs.rm(file, { force: true, recursive: true }).catch(() => undefined)));
240
+ }
241
+ async function pathIsFile(file) {
242
+ try {
243
+ return (await fs.stat(file)).isFile();
244
+ }
245
+ catch {
246
+ return false;
247
+ }
248
+ }
203
249
  function nativeHarnessSource() {
204
250
  return `let application = NSApplication.shared
205
251
  application.setActivationPolicy(.accessory)
@@ -0,0 +1,181 @@
1
+ import { SPEED_LANE_POLICY } from '../proof-field.js';
2
+ import { OFFICIAL_SUBAGENT_EXECUTION_STAGE_ID } from '../agents/agent-schema.js';
3
+ import { normalizeOfficialSubagentPolicy, officialSubagentPipelineStage } from '../agents/agent-plan.js';
4
+ import { looksLikeCodeChangingWork, reflectionRequiredForRoute, routeRequiresSubagents } from '../routes.js';
5
+ import { classifyTaskProfile } from '../runtime/task-profile.js';
6
+ import {} from '../runtime/verification-budget.js';
7
+ export const LIGHTWEIGHT_ROUTES = new Set(['Answer', 'DFix', 'Help', 'Wiki', 'Goal']);
8
+ export const BLOCKING_GATE_LIMITS = Object.freeze({
9
+ passthrough: 0,
10
+ answer: 0,
11
+ 'tiny-change': 1,
12
+ 'bounded-work': 2,
13
+ 'parallel-read': 2,
14
+ 'parallel-write': 3,
15
+ 'high-risk': 4
16
+ });
17
+ function routeNeedsEngineeringSanityReview(route, task) {
18
+ const id = String(route?.id || '');
19
+ if (['DB', 'MadSKS'].includes(id))
20
+ return true;
21
+ if (['Answer', 'Help', 'Wiki', 'Goal', 'Research', 'AutoResearch', 'PPT', 'ImageUXReview', 'ComputerUse', 'GX'].includes(id))
22
+ return false;
23
+ return looksLikeCodeChangingWork(String(task || ''));
24
+ }
25
+ export const GATE_PROFILE_STAGES = Object.freeze({
26
+ none: [],
27
+ minimal: ['route_classification', 'listed_verification'],
28
+ scoped: ['route_classification', 'ownership', 'listed_verification', 'honest_summary'],
29
+ full: ['route_classification', 'ambiguity_gate', 'safety_gate', 'ownership', 'listed_verification', 'honest_summary']
30
+ });
31
+ export const STAGE_BLOCKING_GATE = Object.freeze({
32
+ ambiguity_gate: 'scope',
33
+ safety_gate: 'safety',
34
+ ssot_guard: 'safety',
35
+ context7_evidence: 'safety',
36
+ mistake_recall: 'safety',
37
+ ownership: 'ownership',
38
+ pipeline_plan: 'ownership',
39
+ focused_implementation: 'ownership',
40
+ triwiki_use_first: 'ownership',
41
+ subagent_plan: 'ownership',
42
+ official_subagent_execution: 'ownership',
43
+ parent_integration: 'ownership',
44
+ route_materialization: 'ownership',
45
+ work_order_coverage: 'ownership',
46
+ architecture_map_baseline: 'ownership',
47
+ engineering_sanity_check: 'verification',
48
+ listed_verification: 'verification',
49
+ triwiki_validate_before_final: 'verification',
50
+ architecture_map_review: 'verification',
51
+ completion_proof: 'verification',
52
+ reflection: 'verification',
53
+ honest_summary: 'verification'
54
+ });
55
+ export function selectPipelineLane(route, task, proof, taskProfile = classifyTaskProfile(task)) {
56
+ if (proof.attached && proof.lane) {
57
+ return {
58
+ lane: proof.lane,
59
+ source: 'proof_field',
60
+ fast_lane_allowed: Boolean(proof.fast_lane_allowed),
61
+ reason: proof.fast_lane_allowed ? 'Proof Field allowed the fast lane.' : `Proof Field selected ${proof.lane}.`,
62
+ blockers: proof.blockers || [],
63
+ skip_when_fast: proof.fast_lane_allowed ? SPEED_LANE_POLICY.skip_when_fast : [],
64
+ keep: proof.keep || SPEED_LANE_POLICY.always_keep
65
+ };
66
+ }
67
+ if (taskProfile === 'passthrough' || taskProfile === 'answer')
68
+ return { lane: 'no_pipeline', source: 'task_profile', fast_lane_allowed: true, reason: 'Light conversation does not create an execution pipeline.', blockers: [], skip_when_fast: [], keep: [] };
69
+ if (route?.id === 'ComputerUse')
70
+ return { lane: 'computer_use_fast_lane', source: 'route_policy', fast_lane_allowed: true, reason: 'Computer Use route is intentionally direct and defers wiki/honest checks to closeout.', blockers: [], skip_when_fast: ['planning_debate'], keep: ['focused_implementation', 'triwiki_validate_before_final', 'honest_mode'] };
71
+ if (taskProfile === 'tiny-change')
72
+ return { lane: 'minimal_change_lane', source: 'task_profile', fast_lane_allowed: true, reason: 'Tiny change uses one blocking gate and one focused check at most.', blockers: [], skip_when_fast: SPEED_LANE_POLICY.skip_when_fast, keep: ['listed_verification'] };
73
+ if (LIGHTWEIGHT_ROUTES.has(route?.id))
74
+ return { lane: `${String(route.id).toLowerCase()}_lightweight_lane`, source: 'route_policy', fast_lane_allowed: true, reason: 'Lightweight route bypasses full mission orchestration by design.', blockers: [], skip_when_fast: SPEED_LANE_POLICY.skip_when_fast, keep: ['focused_implementation', 'listed_verification', 'honest_mode'] };
75
+ if (routeRequiresSubagents(route, task, taskProfile))
76
+ return { lane: 'official_subagent_lane', source: 'task_profile', fast_lane_allowed: false, reason: 'Explicit Naruto or parallel work uses the Codex subagent workflow.', blockers: [], skip_when_fast: [], keep: ['subagent_plan', 'official_subagent_execution', 'parent_integration', 'listed_verification', 'honest_summary'] };
77
+ if (taskProfile === 'high-risk')
78
+ return { lane: SPEED_LANE_POLICY.full_lane, source: 'task_profile', fast_lane_allowed: false, reason: 'High-risk work uses the full risk gate profile.', blockers: [], skip_when_fast: [], keep: SPEED_LANE_POLICY.always_keep };
79
+ return { lane: SPEED_LANE_POLICY.balanced_lane, source: 'route_policy', fast_lane_allowed: false, reason: 'Balanced parent-owned route until Proof Field proves a narrower lane.', blockers: ['proof_field_not_attached'], skip_when_fast: [], keep: SPEED_LANE_POLICY.always_keep };
80
+ }
81
+ export function buildPipelineStages(route, task, taskProfile, gateProfile, ambiguity, lane, context7Required, officialSubagentPolicy = normalizeOfficialSubagentPolicy(route, task, {})) {
82
+ if (gateProfile === 'none')
83
+ return [];
84
+ const ids = [...GATE_PROFILE_STAGES[gateProfile]];
85
+ const specializedRoute = Boolean(route?.id && !LIGHTWEIGHT_ROUTES.has(route.id) && route.id !== 'SKS');
86
+ if (gateProfile === 'scoped' || gateProfile === 'full' || specializedRoute)
87
+ ids.push('pipeline_plan', 'focused_implementation');
88
+ if ((gateProfile === 'full' || specializedRoute) && !ids.includes('ssot_guard'))
89
+ ids.push('ssot_guard');
90
+ if (context7Required)
91
+ ids.push('context7_evidence');
92
+ if ((gateProfile === 'scoped' || gateProfile === 'full') && !LIGHTWEIGHT_ROUTES.has(route?.id)) {
93
+ ids.push('triwiki_use_first', 'triwiki_validate_before_final', 'mistake_recall', 'work_order_coverage');
94
+ }
95
+ if (routeNeedsEngineeringSanityReview(route, task))
96
+ ids.push('engineering_sanity_check');
97
+ if ((gateProfile === 'scoped' || gateProfile === 'full') && route?.id !== 'GX' && route?.id !== 'DB' && !LIGHTWEIGHT_ROUTES.has(route?.id)) {
98
+ ids.push('architecture_map_baseline', 'architecture_map_review');
99
+ }
100
+ if (routeRequiresSubagents(route, task, taskProfile))
101
+ ids.push('subagent_plan', 'official_subagent_execution', 'parent_integration');
102
+ if (specializedRoute)
103
+ ids.push('route_materialization');
104
+ if (specializedRoute)
105
+ ids.push('completion_proof');
106
+ if (reflectionRequiredForRoute(route))
107
+ ids.push('reflection');
108
+ return [...new Set(ids)].map((id) => {
109
+ const configuredGate = STAGE_BLOCKING_GATE[id] || null;
110
+ const blockingGate = configuredGate === 'safety' && gateProfile !== 'full'
111
+ ? 'ownership'
112
+ : configuredGate;
113
+ const blocking = Boolean(blockingGate);
114
+ const metadata = { blocking, blocking_gate: blocking ? blockingGate : null };
115
+ if (id === OFFICIAL_SUBAGENT_EXECUTION_STAGE_ID)
116
+ return { ...officialSubagentPipelineStage(officialSubagentPolicy), status: 'required', reason: officialSubagentPolicy.reason, ...metadata };
117
+ if (id === 'engineering_sanity_check') {
118
+ return {
119
+ id,
120
+ status: 'keep',
121
+ reason: 'required_for_code_and_database_quality',
122
+ checks: [
123
+ 'trace actual callers and preserve basic SOLID responsibility and dependency boundaries',
124
+ 'inspect loops, collections, resolvers, and serializers for N+1 or repeated I/O',
125
+ 'prove render, recursion, event, retry, and polling loops are bounded and cancellable',
126
+ 'reject disabled checks, swallowed errors, placeholder success, and verification bypasses',
127
+ 'for DB work, verify the existing canonical adapter and connection/pool lifecycle before changes',
128
+ 'for sensitive or multi-step DB work, verify transaction rollback, error propagation, idempotency, and post-commit invariants'
129
+ ],
130
+ ...metadata
131
+ };
132
+ }
133
+ if (id === 'ambiguity_gate' && ambiguity?.required === false)
134
+ return { id, status: 'not_applicable', reason: 'ambiguity_gate_not_required_for_entrypoint', ...metadata };
135
+ if (id === 'ambiguity_gate' && ambiguity?.passed)
136
+ return { id, status: 'passed', reason: 'ambiguity_contract_already_sealed', ...metadata };
137
+ return { id, status: 'keep', reason: lane.fast_lane_allowed ? 'task_profile_minimal_lane' : 'required_by_task_and_route_profile', ...metadata };
138
+ });
139
+ }
140
+ export function planVerification(route, task, proof, budget) {
141
+ if (budget === 'none')
142
+ return [];
143
+ const out = new Set(proof.verification || []);
144
+ if (budget === 'single-check')
145
+ out.add('run one focused check for the changed surface');
146
+ if (budget === 'affected')
147
+ out.add('run affected tests or checks for changed files');
148
+ if (budget === 'confidence') {
149
+ out.add('run the focused build or typecheck for the affected package');
150
+ out.add('run affected regression tests and the risk-specific safety check');
151
+ }
152
+ if (budget === 'release') {
153
+ out.add('npm run packcheck');
154
+ out.add('sks selftest --mock --json');
155
+ }
156
+ if (route?.id === 'Naruto')
157
+ out.add('validate official subagent evidence and the parent integration summary');
158
+ if (routeNeedsEngineeringSanityReview(route, task)) {
159
+ out.add('complete the engineering sanity review for SOLID boundaries, N+1/repeated I/O, bounded loops, and verification bypasses');
160
+ }
161
+ if (route?.id === 'DB' || route?.id === 'MadSKS') {
162
+ out.add('verify existing canonical DB access, pool lifecycle, and transaction integrity for sensitive or multi-step mutations');
163
+ }
164
+ if (reflectionRequiredForRoute(route))
165
+ out.add('sks wiki validate .sneakoscope/wiki/context-pack.json');
166
+ return [...out];
167
+ }
168
+ export function pipelineInvariants(input) {
169
+ const out = ['no_unrequested_fallback_code'];
170
+ if (input.stages.some((stage) => stage.id === 'engineering_sanity_check'))
171
+ out.push('engineering_sanity_check');
172
+ if (input.verificationBudget !== 'none')
173
+ out.push('listed_verification');
174
+ if (input.stages.some((stage) => stage.id === 'ssot_guard' && stage.status !== 'not_applicable'))
175
+ out.push('ssot_guard');
176
+ if (input.stages.some((stage) => stage.id === 'triwiki_validate_before_final'))
177
+ out.push('triwiki_validate_before_final');
178
+ if (input.gateProfile !== 'none')
179
+ out.push('honest_summary');
180
+ return out;
181
+ }
@@ -38,7 +38,10 @@ import { NARUTO_GATE_FILENAME, NARUTO_SUMMARY_FILENAME, SUBAGENT_PLAN_FILENAME,
38
38
  import { buildSubagentEvidence, readSubagentEvents, recordSubagentEvent, SUBAGENT_EVIDENCE_FILENAME, SUBAGENT_EVENT_LOG_FILENAME, SUBAGENT_PARENT_SUMMARY_FILENAME } from '../subagents/subagent-evidence.js';
39
39
  import { effectiveSubagentTarget, normalizeLegacySubagentCountFields, subagentCountContractBlockers } from '../subagents/wave-lifecycle.js';
40
40
  import { officialSubagentEvidenceReady } from '../subagents/terminal-subagent-state.js';
41
+ import { createArchitectureMapPlanBinding, maybeSeedArchitectureMapForPlan, planStagesArchitectureMap } from '../architecture-map-pipeline.js';
42
+ import { BLOCKING_GATE_LIMITS, LIGHTWEIGHT_ROUTES, GATE_PROFILE_STAGES, STAGE_BLOCKING_GATE, selectPipelineLane, buildPipelineStages, planVerification, pipelineInvariants } from './pipeline-stage-builder.js';
41
43
  export { routePrompt };
44
+ export { LIGHTWEIGHT_ROUTES, GATE_PROFILE_STAGES, STAGE_BLOCKING_GATE, BLOCKING_GATE_LIMITS };
42
45
  export const PIPELINE_PLAN_ARTIFACT = 'pipeline-plan.json';
43
46
  export const PIPELINE_PLAN_SCHEMA_VERSION = 1;
44
47
  const SKILL_DREAM_HOT_PATH_COUNTERS = new Map();
@@ -60,44 +63,6 @@ const HARD_BLOCKER_ARTIFACT = 'hard-blocker.json';
60
63
  const DEFAULT_COMPLIANCE_LOOP_LIMIT = 3;
61
64
  const CLARIFICATION_BYPASS_ROUTES = new Set(['Answer', 'DFix', 'Help', 'Wiki', 'ComputerUse', 'Goal']);
62
65
  const QUESTION_GATE_ROUTES = new Set(['QALoop', 'PPT']);
63
- const LIGHTWEIGHT_ROUTES = new Set(['Answer', 'DFix', 'Help', 'Wiki', 'Goal']);
64
- const GATE_PROFILE_STAGES = Object.freeze({
65
- none: [],
66
- minimal: ['route_classification', 'listed_verification'],
67
- scoped: ['route_classification', 'ownership', 'listed_verification', 'honest_summary'],
68
- full: ['route_classification', 'ambiguity_gate', 'safety_gate', 'ownership', 'listed_verification', 'honest_summary']
69
- });
70
- const STAGE_BLOCKING_GATE = Object.freeze({
71
- ambiguity_gate: 'scope',
72
- safety_gate: 'safety',
73
- ssot_guard: 'safety',
74
- context7_evidence: 'safety',
75
- mistake_recall: 'safety',
76
- ownership: 'ownership',
77
- pipeline_plan: 'ownership',
78
- focused_implementation: 'ownership',
79
- triwiki_use_first: 'ownership',
80
- subagent_plan: 'ownership',
81
- official_subagent_execution: 'ownership',
82
- parent_integration: 'ownership',
83
- route_materialization: 'ownership',
84
- work_order_coverage: 'ownership',
85
- engineering_sanity_check: 'verification',
86
- listed_verification: 'verification',
87
- triwiki_validate_before_final: 'verification',
88
- completion_proof: 'verification',
89
- reflection: 'verification',
90
- honest_summary: 'verification'
91
- });
92
- const BLOCKING_GATE_LIMITS = Object.freeze({
93
- passthrough: 0,
94
- answer: 0,
95
- 'tiny-change': 1,
96
- 'bounded-work': 2,
97
- 'parallel-read': 2,
98
- 'parallel-write': 3,
99
- 'high-risk': 4
100
- });
101
66
  function reflectionInstructionText(commandPrefix = 'sks') {
102
67
  return `Post-route reflection: full routes load \`reflection\` after work/tests and before final; DFix/Answer/Help/Wiki/SKS discovery are exempt. Write ${REFLECTION_ARTIFACT}; record only real misses/gaps, or no_issue_acknowledged. For lessons, append TriWiki claim rows to ${REFLECTION_MEMORY_PATH}. Run "${commandPrefix} wiki refresh" or pack, validate, then pass ${REFLECTION_GATE}.`;
103
68
  }
@@ -182,6 +147,7 @@ export function buildPipelinePlan(input = {}) {
182
147
  verification_budget: verificationBudget,
183
148
  verification,
184
149
  engineering_sanity_review: null,
150
+ architecture_map: null,
185
151
  invariants,
186
152
  proof_field: proof,
187
153
  ssot_guard: buildSsotGuard({ route: route?.id || 'SKS', mode: route?.mode || 'SKS', task }),
@@ -219,6 +185,9 @@ export async function writePipelinePlan(dir, input = {}) {
219
185
  changed_scope_base: resolveChangedScopeBase(root)
220
186
  };
221
187
  }
188
+ if (planStagesArchitectureMap(plan)) {
189
+ plan.architecture_map = createArchitectureMapPlanBinding();
190
+ }
222
191
  await writeJsonAtomic(path.join(dir, PIPELINE_PLAN_ARTIFACT), plan);
223
192
  if (plan.engineering_sanity_review) {
224
193
  const report = await writeCodeStructureReport(root, dir, {
@@ -232,6 +201,9 @@ export async function writePipelinePlan(dir, input = {}) {
232
201
  await writeJsonAtomic(reviewFile, createEngineeringSanityReviewSeed(plan.route?.command || plan.route?.id || null, report, reportText));
233
202
  }
234
203
  }
204
+ await maybeSeedArchitectureMapForPlan({
205
+ root, dir, plan, missionId: input.missionId, taskProfile
206
+ });
235
207
  return plan;
236
208
  }
237
209
  function planStagesEngineeringSanityReview(plan) {
@@ -244,7 +216,8 @@ export function pipelinePlanState(plan) {
244
216
  pipeline_plan_ready: validatePipelinePlan(plan).ok,
245
217
  pipeline_plan_path: PIPELINE_PLAN_ARTIFACT,
246
218
  engineering_sanity_required: Boolean(sanity),
247
- engineering_sanity_scope_base: sanity?.changed_scope_base || null
219
+ engineering_sanity_scope_base: sanity?.changed_scope_base || null,
220
+ architecture_map_required: Boolean(plan?.architecture_map)
248
221
  };
249
222
  }
250
223
  export async function writeRequestIntakeArtifact(dir, input = {}) {
@@ -488,130 +461,6 @@ function routeEconomyPlan(proof = {}) {
488
461
  deletion_policy: 'do_not_delete_or_skip_pipeline_stages_until_report_only_metrics_are_calibrated'
489
462
  };
490
463
  }
491
- function selectPipelineLane(route, task, proof, taskProfile = classifyTaskProfile(task)) {
492
- if (proof.attached && proof.lane) {
493
- return {
494
- lane: proof.lane,
495
- source: 'proof_field',
496
- fast_lane_allowed: Boolean(proof.fast_lane_allowed),
497
- reason: proof.fast_lane_allowed ? 'Proof Field allowed the fast lane.' : `Proof Field selected ${proof.lane}.`,
498
- blockers: proof.blockers || [],
499
- skip_when_fast: proof.fast_lane_allowed ? SPEED_LANE_POLICY.skip_when_fast : [],
500
- keep: proof.keep || SPEED_LANE_POLICY.always_keep
501
- };
502
- }
503
- if (taskProfile === 'passthrough' || taskProfile === 'answer')
504
- return { lane: 'no_pipeline', source: 'task_profile', fast_lane_allowed: true, reason: 'Light conversation does not create an execution pipeline.', blockers: [], skip_when_fast: [], keep: [] };
505
- if (route?.id === 'ComputerUse')
506
- return { lane: 'computer_use_fast_lane', source: 'route_policy', fast_lane_allowed: true, reason: 'Computer Use route is intentionally direct and defers wiki/honest checks to closeout.', blockers: [], skip_when_fast: ['planning_debate'], keep: ['focused_implementation', 'triwiki_validate_before_final', 'honest_mode'] };
507
- if (taskProfile === 'tiny-change')
508
- return { lane: 'minimal_change_lane', source: 'task_profile', fast_lane_allowed: true, reason: 'Tiny change uses one blocking gate and one focused check at most.', blockers: [], skip_when_fast: SPEED_LANE_POLICY.skip_when_fast, keep: ['listed_verification'] };
509
- if (LIGHTWEIGHT_ROUTES.has(route?.id))
510
- return { lane: `${String(route.id).toLowerCase()}_lightweight_lane`, source: 'route_policy', fast_lane_allowed: true, reason: 'Lightweight route bypasses full mission orchestration by design.', blockers: [], skip_when_fast: SPEED_LANE_POLICY.skip_when_fast, keep: ['focused_implementation', 'listed_verification', 'honest_mode'] };
511
- if (routeRequiresSubagents(route, task, taskProfile))
512
- return { lane: 'official_subagent_lane', source: 'task_profile', fast_lane_allowed: false, reason: 'Explicit Naruto or parallel work uses the Codex subagent workflow.', blockers: [], skip_when_fast: [], keep: ['subagent_plan', 'official_subagent_execution', 'parent_integration', 'listed_verification', 'honest_summary'] };
513
- if (taskProfile === 'high-risk')
514
- return { lane: SPEED_LANE_POLICY.full_lane, source: 'task_profile', fast_lane_allowed: false, reason: 'High-risk work uses the full risk gate profile.', blockers: [], skip_when_fast: [], keep: SPEED_LANE_POLICY.always_keep };
515
- return { lane: SPEED_LANE_POLICY.balanced_lane, source: 'route_policy', fast_lane_allowed: false, reason: 'Balanced parent-owned route until Proof Field proves a narrower lane.', blockers: ['proof_field_not_attached'], skip_when_fast: [], keep: SPEED_LANE_POLICY.always_keep };
516
- }
517
- function buildPipelineStages(route, task, taskProfile, gateProfile, ambiguity, lane, context7Required, officialSubagentPolicy = normalizeOfficialSubagentPolicy(route, task, {})) {
518
- if (gateProfile === 'none')
519
- return [];
520
- const ids = [...GATE_PROFILE_STAGES[gateProfile]];
521
- const specializedRoute = Boolean(route?.id && !LIGHTWEIGHT_ROUTES.has(route.id) && route.id !== 'SKS');
522
- if (gateProfile === 'scoped' || gateProfile === 'full' || specializedRoute)
523
- ids.push('pipeline_plan', 'focused_implementation');
524
- if ((gateProfile === 'full' || specializedRoute) && !ids.includes('ssot_guard'))
525
- ids.push('ssot_guard');
526
- if (context7Required)
527
- ids.push('context7_evidence');
528
- if ((gateProfile === 'scoped' || gateProfile === 'full') && !LIGHTWEIGHT_ROUTES.has(route?.id)) {
529
- ids.push('triwiki_use_first', 'triwiki_validate_before_final', 'mistake_recall', 'work_order_coverage');
530
- }
531
- if (routeNeedsEngineeringSanityReview(route, task))
532
- ids.push('engineering_sanity_check');
533
- if (routeRequiresSubagents(route, task, taskProfile))
534
- ids.push('subagent_plan', 'official_subagent_execution', 'parent_integration');
535
- if (specializedRoute)
536
- ids.push('route_materialization');
537
- if (specializedRoute)
538
- ids.push('completion_proof');
539
- if (reflectionRequiredForRoute(route))
540
- ids.push('reflection');
541
- return [...new Set(ids)].map((id) => {
542
- const configuredGate = STAGE_BLOCKING_GATE[id] || null;
543
- const blockingGate = configuredGate === 'safety' && gateProfile !== 'full'
544
- ? 'ownership'
545
- : configuredGate;
546
- const blocking = Boolean(blockingGate);
547
- const metadata = { blocking, blocking_gate: blocking ? blockingGate : null };
548
- if (id === OFFICIAL_SUBAGENT_EXECUTION_STAGE_ID)
549
- return { ...officialSubagentPipelineStage(officialSubagentPolicy), status: 'required', reason: officialSubagentPolicy.reason, ...metadata };
550
- if (id === 'engineering_sanity_check') {
551
- return {
552
- id,
553
- status: 'keep',
554
- reason: 'required_for_code_and_database_quality',
555
- checks: [
556
- 'trace actual callers and preserve basic SOLID responsibility and dependency boundaries',
557
- 'inspect loops, collections, resolvers, and serializers for N+1 or repeated I/O',
558
- 'prove render, recursion, event, retry, and polling loops are bounded and cancellable',
559
- 'reject disabled checks, swallowed errors, placeholder success, and verification bypasses',
560
- 'for DB work, verify the existing canonical adapter and connection/pool lifecycle before changes',
561
- 'for sensitive or multi-step DB work, verify transaction rollback, error propagation, idempotency, and post-commit invariants'
562
- ],
563
- ...metadata
564
- };
565
- }
566
- if (id === 'ambiguity_gate' && ambiguity?.required === false)
567
- return { id, status: 'not_applicable', reason: 'ambiguity_gate_not_required_for_entrypoint', ...metadata };
568
- if (id === 'ambiguity_gate' && ambiguity?.passed)
569
- return { id, status: 'passed', reason: 'ambiguity_contract_already_sealed', ...metadata };
570
- return { id, status: 'keep', reason: lane.fast_lane_allowed ? 'task_profile_minimal_lane' : 'required_by_task_and_route_profile', ...metadata };
571
- });
572
- }
573
- function planVerification(route, task, proof, budget) {
574
- if (budget === 'none')
575
- return [];
576
- const out = new Set(proof.verification || []);
577
- if (budget === 'single-check')
578
- out.add('run one focused check for the changed surface');
579
- if (budget === 'affected')
580
- out.add('run affected tests or checks for changed files');
581
- if (budget === 'confidence') {
582
- out.add('run the focused build or typecheck for the affected package');
583
- out.add('run affected regression tests and the risk-specific safety check');
584
- }
585
- if (budget === 'release') {
586
- out.add('npm run packcheck');
587
- out.add('sks selftest --mock --json');
588
- }
589
- if (route?.id === 'Naruto')
590
- out.add('validate official subagent evidence and the parent integration summary');
591
- if (routeNeedsEngineeringSanityReview(route, task)) {
592
- out.add('complete the engineering sanity review for SOLID boundaries, N+1/repeated I/O, bounded loops, and verification bypasses');
593
- }
594
- if (route?.id === 'DB' || route?.id === 'MadSKS') {
595
- out.add('verify existing canonical DB access, pool lifecycle, and transaction integrity for sensitive or multi-step mutations');
596
- }
597
- if (reflectionRequiredForRoute(route))
598
- out.add('sks wiki validate .sneakoscope/wiki/context-pack.json');
599
- return [...out];
600
- }
601
- function pipelineInvariants(input) {
602
- const out = ['no_unrequested_fallback_code'];
603
- if (input.stages.some((stage) => stage.id === 'engineering_sanity_check'))
604
- out.push('engineering_sanity_check');
605
- if (input.verificationBudget !== 'none')
606
- out.push('listed_verification');
607
- if (input.stages.some((stage) => stage.id === 'ssot_guard' && stage.status !== 'not_applicable'))
608
- out.push('ssot_guard');
609
- if (input.stages.some((stage) => stage.id === 'triwiki_validate_before_final'))
610
- out.push('triwiki_validate_before_final');
611
- if (input.gateProfile !== 'none')
612
- out.push('honest_summary');
613
- return out;
614
- }
615
464
  function planNextActions(route, task, taskProfile, ambiguity, lane) {
616
465
  if (taskProfile === 'passthrough' || taskProfile === 'answer')
617
466
  return [];
@@ -1593,15 +1442,7 @@ function strictNarutoIntegerOption(text, option) {
1593
1442
  function routeState(id, route, phase, context7Required, extra = {}) {
1594
1443
  const reasoning = routeReasoning(route, extra.prompt || '');
1595
1444
  const subagentsRequired = routeRequiresSubagents(route, extra.prompt || '');
1596
- return { mission_id: id, route: route.id, route_command: route.command, mode: route.mode, phase, context7_required: context7Required, context7_verified: false, subagents_required: subagentsRequired, subagents_verified: !subagentsRequired, native_sessions_required: false, native_sessions_verified: false, reflection_required: reflectionRequiredForRoute(route), engineering_sanity_required: false, engineering_sanity_scope_base: null, visible_progress_required: true, context_tracking: 'triwiki', required_skills: route.requiredSkills, stop_gate: route.stopGate, reasoning_effort: reasoning.effort, reasoning_profile: reasoning.profile, reasoning_temporary: true, goal_continuation: ambientGoalContinuation(), ...extra };
1597
- }
1598
- function routeNeedsEngineeringSanityReview(route, task) {
1599
- const id = String(route?.id || '');
1600
- if (['DB', 'MadSKS'].includes(id))
1601
- return true;
1602
- if (['Answer', 'Help', 'Wiki', 'Goal', 'Research', 'AutoResearch', 'PPT', 'ImageUXReview', 'ComputerUse', 'GX'].includes(id))
1603
- return false;
1604
- return looksLikeCodeChangingWork(String(task || ''));
1445
+ return { mission_id: id, route: route.id, route_command: route.command, mode: route.mode, phase, context7_required: context7Required, context7_verified: false, subagents_required: subagentsRequired, subagents_verified: !subagentsRequired, native_sessions_required: false, native_sessions_verified: false, reflection_required: reflectionRequiredForRoute(route), engineering_sanity_required: false, engineering_sanity_scope_base: null, architecture_map_required: false, visible_progress_required: true, context_tracking: 'triwiki', required_skills: route.requiredSkills, stop_gate: route.stopGate, reasoning_effort: reasoning.effort, reasoning_profile: reasoning.profile, reasoning_temporary: true, goal_continuation: ambientGoalContinuation(), ...extra };
1605
1446
  }
1606
1447
  function routeContext(route, id, task, required, next) {
1607
1448
  const visibleTask = stripVisibleDecisionAnswerBlocks(task);
@@ -17,6 +17,8 @@ import { checkStopGate } from '../stop-gate/stop-gate-check.js';
17
17
  import { readWorkOrderLedger, evaluateWorkOrderCoverage } from '../work-order-ledger.js';
18
18
  import { DB_ACCESS_REVIEW_ARTIFACT, DB_REVIEW_ARTIFACT, validateDbAccessReviewArtifact, validateDbReview, validateMadSksSqlPlaneCompletion } from '../db-review.js';
19
19
  import { ENGINEERING_SANITY_REVIEW_ARTIFACT, validateEngineeringSanityReviewArtifact } from '../engineering-sanity-review.js';
20
+ import { ARCHITECTURE_MAP_REVIEW_ARTIFACT } from '../architecture-map-review.js';
21
+ import { architectureMapGateStatus } from '../architecture-map-pipeline.js';
20
22
  import { clarificationStopReason, context7Evidence, hasContext7DocsEvidence, hasSubagentEvidence, subagentEvidence, PIPELINE_PLAN_ARTIFACT, } from './runtime-core.js';
21
23
  import { projectTriwikiToAgentsMd } from '../triwiki/agents-md-projector.js';
22
24
  import { effectiveSubagentTarget, normalizeLegacySubagentCountFields, subagentCountContractBlockers } from '../subagents/wave-lifecycle.js';
@@ -256,6 +258,7 @@ export async function evaluateStop(root, state, payload, opts = {}) {
256
258
  && !completionProofRequired
257
259
  && !reflectionRequired
258
260
  && state?.engineering_sanity_required !== true
261
+ && state?.architecture_map_required !== true
259
262
  && state?.db_access_review_required !== true) {
260
263
  return null;
261
264
  }
@@ -267,12 +270,14 @@ export async function evaluateStop(root, state, payload, opts = {}) {
267
270
  && activeGatePreview?.covered_gate === 'official-subagent-evidence';
268
271
  const dbAccessPromise = dbAccessReviewGateStatus(root, state, jsonCache);
269
272
  const engineeringSanityPromise = engineeringSanityGateStatus(root, state, jsonCache);
273
+ const architectureMapPromise = architectureMapGateStatus(root, state);
270
274
  const context7Promise = state?.context7_required ? hasContext7DocsEvidence(root, state) : Promise.resolve(true);
271
275
  const subagentPromise = state?.subagents_required && !activeGateNotApplicable ? hasSubagentEvidence(root, state) : Promise.resolve(true);
272
276
  const mistakeRecallPromise = mistakeRecallGateStatus(root, state);
273
- const [dbAccess, engineeringSanity, context7Ok, subagentOk, mistakeRecall] = await Promise.all([
277
+ const [dbAccess, engineeringSanity, architectureMap, context7Ok, subagentOk, mistakeRecall] = await Promise.all([
274
278
  dbAccessPromise,
275
279
  engineeringSanityPromise,
280
+ architectureMapPromise,
276
281
  context7Promise,
277
282
  subagentPromise,
278
283
  mistakeRecallPromise
@@ -280,6 +285,7 @@ export async function evaluateStop(root, state, payload, opts = {}) {
280
285
  await resolveRecoveredComplianceBlocker(root, state, {
281
286
  [DB_ACCESS_REVIEW_ARTIFACT]: dbAccess.ok,
282
287
  [ENGINEERING_SANITY_REVIEW_ARTIFACT]: engineeringSanity.ok,
288
+ [ARCHITECTURE_MAP_REVIEW_ARTIFACT]: architectureMap.ok,
283
289
  'context7-evidence': context7Ok,
284
290
  'official-subagent-evidence': subagentOk
285
291
  });
@@ -289,6 +295,9 @@ export async function evaluateStop(root, state, payload, opts = {}) {
289
295
  if (!engineeringSanity.ok) {
290
296
  return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} must complete the engineering sanity review before completion. Static candidates are advisory only; inspect added hunks and real callers for SOLID boundaries, N+1/repeated I/O, bounded render/recursion/event/retry/polling behavior, and verification bypasses. Missing: ${(engineeringSanity.blockers || []).join(', ')}.`, { gate: ENGINEERING_SANITY_REVIEW_ARTIFACT, missing: engineeringSanity.blockers });
291
297
  }
298
+ if (!architectureMap.ok) {
299
+ return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} must seal Architecture Map baseline before mutation and produce a passing architecture-map-review before completion. Missing: ${(architectureMap.blockers || []).join(', ')}.`, { gate: ARCHITECTURE_MAP_REVIEW_ARTIFACT, missing: architectureMap.blockers });
300
+ }
292
301
  if (state?.context7_required && !context7Ok) {
293
302
  return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} requires Context7 evidence before completion. Use Context7 resolve-library-id, then query-docs, so SKS can record context7-evidence.jsonl.`, { gate: 'context7-evidence' });
294
303
  }
@@ -518,6 +527,8 @@ async function markReflectionInvalidatedForGateFailure(root, state = {}, detail
518
527
  return;
519
528
  if (/^work-order-ledger$/i.test(gate) || /^completion-proof$/i.test(gate))
520
529
  return;
530
+ if (/^atlas:/.test(gate) || /^architecture-map:/.test(gate) || /(^|[\\/])?(architecture-atlas-review|architecture-map-review|atlas-gate)(\.json)?$/i.test(gate))
531
+ return;
521
532
  await setCurrent(root, {
522
533
  mission_id: state.mission_id,
523
534
  reflection_invalidation_required: true,