sneakoscope 9.0.3 → 9.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/commands/doctor.js +97 -98
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +16 -4
- package/dist/core/commands/basic-cli.js +5 -0
- package/dist/core/doctor/desktop-bridge-catalog-repair.js +102 -0
- package/dist/core/triwiki/context-graph/extractors/code/index.js +7 -3
- package/dist/core/triwiki/context-graph/extractors/evidence/index.js +13 -3
- package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +1 -1
- package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +3 -13
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +6 -2
- package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +2 -1
- package/dist/core/triwiki/context-graph/extractors/index.js +13 -2
- package/dist/core/triwiki/context-graph/extractors/source-inventory.js +35 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +33 -18
- package/dist/core/triwiki/context-graph/extractors/topology/index.js +8 -2
- package/dist/core/triwiki/context-graph/extractors/topology/shared.js +6 -3
- package/dist/core/triwiki/context-graph/lint/rules.js +1 -1
- package/dist/core/update/update-migration-state/current-public-surface-stage.js +67 -0
- package/dist/core/update/update-migration-state/desktop-bridge-catalog-repair-stage.js +45 -0
- package/dist/core/update/update-migration-state.js +7 -65
- package/dist/core/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Proof-first orchestration for Codex CLI, ChatGPT Desktop, AI coding agents, mult
|
|
|
22
22
|
Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
|
|
23
23
|
<!-- END SKS SEARCH VISIBILITY MARKETING -->
|
|
24
24
|
|
|
25
|
-
This README documents package **SKS 9.0.
|
|
25
|
+
This README documents package **SKS 9.0.6** — its own identity, read from `package.json` and subject to release-gate verification, not advice about what to install.
|
|
26
26
|
|
|
27
27
|
Use the official latest stable SKS and Codex CLI releases. The Codex compatibility SSOT is always the **current latest stable** host; capability probes measure what that host can actually do. Product docs do not crown a fixed `0.x.y` string as SSOT (release pins and schema directories are measured artifacts for the current package, not a permanent product version claim). Menu Bar / Center induce updates to the latest stable build. Run `sks update-check` for what is installed and read the capability report for what is supported. Install SSOT is npm `sneakoscope@latest`; PATH `sks` and Menu Bar stamped generation must match that version or gates fail. It resolves managed SKS skills from the authoritative global install, preserves a runnable Naruto child slot when `max_threads=2`, and keeps Menu Bar repair transactional so stamped generations remain verifiable. Naruto uses stable opt-in multi-agent V2 when the host exposes it (Codex official multi-agent wrap-only; SKS does not reimplement a parallel runtime). Local code search is mode-separated (`sks search files|text|structure|symbol|context`); `context` is answered by the compiled TriWiki Context Graph (`context-graph.json` is exhaustive authority; `context-pack.json` and managed `AGENTS.md` are bounded projections) — see [docs/architecture/context-graph.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/docs/architecture/context-graph.md) and [docs/PRODUCT-CONTRACT.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/docs/PRODUCT-CONTRACT.md). See [CHANGELOG.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/CHANGELOG.md).
|
|
28
28
|
|
package/dist/commands/doctor.js
CHANGED
|
@@ -7,10 +7,8 @@ import { ui as cliUi } from '../cli/cli-theme.js';
|
|
|
7
7
|
import { getCodexInfo } from '../core/codex-adapter.js';
|
|
8
8
|
import { rustInfo } from '../core/rust-accelerator.js';
|
|
9
9
|
import { codexAppIntegrationStatus } from '../core/codex-app.js';
|
|
10
|
-
import { desktopBridgeStatusV3
|
|
11
|
-
import {
|
|
12
|
-
import { desktopBridgeRuntimeVersion, desktopBridgeRuntimeVersionStale } from '../core/codex-lb/desktop-bridge/state.js';
|
|
13
|
-
import { PACKAGE_VERSION } from '../core/version.js';
|
|
10
|
+
import { desktopBridgeStatusV3 } from '../core/codex-lb/desktop-controller-v3.js';
|
|
11
|
+
import { repairDoctorDesktopBridgeCatalog } from '../core/doctor/desktop-bridge-catalog-repair.js';
|
|
14
12
|
import { inspectCodexConfigReadability } from '../core/codex/codex-config-readability.js';
|
|
15
13
|
import { inspectOAuthCallbackPortConflict, oauthCallbackDoctorGuidance } from '../core/codex/oauth-callback-port-diagnostic.js';
|
|
16
14
|
import { inventoryCodexPermissionProfiles } from '../core/codex/codex-permission-profiles.js';
|
|
@@ -37,29 +35,7 @@ export { buildCodexAppUiDiagnosticFailure, buildRuntimeReadiness, formatCodexDoc
|
|
|
37
35
|
export function doctorArgWarnings(args = []) {
|
|
38
36
|
return baseDoctorArgWarnings(args);
|
|
39
37
|
}
|
|
40
|
-
export
|
|
41
|
-
if (process.env.SKS_TEST_ISOLATION === '1' || process.env.SKS_RELEASE_UPGRADE_SMOKE === '1') {
|
|
42
|
-
return { restarted: false, warnings: [], blockers: [] };
|
|
43
|
-
}
|
|
44
|
-
const service = await desktopBridgeServiceStatus({ home: input.home }).catch(() => null);
|
|
45
|
-
if (!service?.running || !desktopBridgeRuntimeVersionStale(service.state)) {
|
|
46
|
-
return { restarted: false, warnings: [], blockers: [] };
|
|
47
|
-
}
|
|
48
|
-
const running = desktopBridgeRuntimeVersion(service.state) || 'pre-8.6.2';
|
|
49
|
-
if (!input.fix) {
|
|
50
|
-
return {
|
|
51
|
-
restarted: false,
|
|
52
|
-
warnings: [],
|
|
53
|
-
blockers: [`desktop_bridge_runtime_version_stale:${running}:${PACKAGE_VERSION}`]
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
const restarted = await bootstrapExistingDesktopBridgeService({ home: input.home }).catch(() => null);
|
|
57
|
-
const nowRunning = restarted?.running === true && !desktopBridgeRuntimeVersionStale(restarted.state);
|
|
58
|
-
return nowRunning
|
|
59
|
-
? { restarted: true, warnings: [`desktop_bridge_runtime_restarted:${running}:${PACKAGE_VERSION}`], blockers: [] }
|
|
60
|
-
: { restarted: false, warnings: [], blockers: [`desktop_bridge_runtime_version_stale:${running}:${PACKAGE_VERSION}`] };
|
|
61
|
-
}
|
|
62
|
-
export const CATALOG_REPAIR_MAX_ATTEMPTS = 2;
|
|
38
|
+
export { CATALOG_REPAIR_MAX_ATTEMPTS, repairDoctorDesktopBridgeCatalog, restartStaleDesktopBridgeRuntime } from '../core/doctor/desktop-bridge-catalog-repair.js';
|
|
63
39
|
export function deferCommandAliasCleanupToMigrationReceipt(result) {
|
|
64
40
|
const observedBlockers = Array.isArray(result?.blockers)
|
|
65
41
|
? result.blockers.map(String).filter(Boolean)
|
|
@@ -214,19 +190,41 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
214
190
|
status: 'failed',
|
|
215
191
|
error: err?.message || String(err)
|
|
216
192
|
}));
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
193
|
+
const doctorProfile = doctorProfileFromArgs(args, true);
|
|
194
|
+
const menuBarPolicy = doctorMenuBarInstallPolicy(args, true, doctorEnv);
|
|
195
|
+
const menuBar = menuBarPolicy.phase_enabled
|
|
196
|
+
? await installMenuBarImpl({
|
|
197
|
+
home,
|
|
198
|
+
root: home,
|
|
199
|
+
apply: true,
|
|
200
|
+
launch: !sksMenuBarRestartDeferred(doctorEnv),
|
|
201
|
+
env: doctorEnv,
|
|
202
|
+
quiet: flag(args, '--json') || flag(args, '--machine-only')
|
|
203
|
+
}).catch((err) => ({
|
|
204
|
+
schema: 'sks.codex-app-sks-menubar.v1',
|
|
205
|
+
ok: false,
|
|
206
|
+
status: 'blocked',
|
|
207
|
+
blockers: [err?.message || String(err)],
|
|
208
|
+
warnings: []
|
|
209
|
+
}))
|
|
210
|
+
: {
|
|
211
|
+
schema: 'sks.codex-app-sks-menubar.v1',
|
|
212
|
+
ok: true,
|
|
213
|
+
status: 'skipped_by_profile',
|
|
214
|
+
skipped: true,
|
|
215
|
+
blockers: [],
|
|
216
|
+
warnings: [`sks_menubar_phase_skipped_for_profile:${doctorProfile}`]
|
|
217
|
+
};
|
|
218
|
+
const desktopBridgeRepairImpl = deps.desktopBridgeRepairImpl || repairDoctorDesktopBridgeCatalog;
|
|
219
|
+
const desktopBridgeRepair = await desktopBridgeRepairImpl({ fix: true }).catch((err) => ({
|
|
220
|
+
id: 'desktop_bridge_catalog_repair',
|
|
226
221
|
ok: false,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
222
|
+
repaired: false,
|
|
223
|
+
required_for_ready: false,
|
|
224
|
+
manual_required: false,
|
|
225
|
+
warnings: [],
|
|
226
|
+
blockers: [`desktop_bridge_catalog_repair_failed:${err?.message || String(err)}`],
|
|
227
|
+
rollback_evidence: 'combined_catalog_previous_generation_preserved'
|
|
230
228
|
}));
|
|
231
229
|
const desktopBridge = await inspectDoctorDesktopBridgeStatus({
|
|
232
230
|
home,
|
|
@@ -238,15 +236,54 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
238
236
|
const globalFastModeReady = globalFastMode?.status !== 'failed'
|
|
239
237
|
&& globalFastMode?.ok !== false;
|
|
240
238
|
const menuBarReady = menuBar?.ok !== false;
|
|
241
|
-
const
|
|
239
|
+
const migrationScopeBlockers = [...new Set([
|
|
242
240
|
...(!globalSkillsReady ? [`global_skills_reconcile_failed:${globalSkills?.error || 'core_skill_integrity'}`] : []),
|
|
243
241
|
...(currentSurface?.ok !== true ? (currentSurface?.blockers || ['global_current_surface_reconcile_failed']) : []),
|
|
244
|
-
...(!globalFastModeReady ? [`global_fast_mode_repair_failed:${globalFastMode?.error || globalFastMode?.status || 'unknown'}`] : [])
|
|
245
|
-
|
|
242
|
+
...(!globalFastModeReady ? [`global_fast_mode_repair_failed:${globalFastMode?.error || globalFastMode?.status || 'unknown'}`] : [])
|
|
243
|
+
].map(String).filter(Boolean))];
|
|
244
|
+
const bridgeBlockers = [...new Set([
|
|
245
|
+
...(desktopBridgeRepair?.ok === false
|
|
246
|
+
? (desktopBridgeRepair?.blockers?.length ? desktopBridgeRepair.blockers : ['desktop_bridge_catalog_repair_failed'])
|
|
247
|
+
: []),
|
|
246
248
|
...(desktopBridge.ok === false
|
|
247
249
|
? (desktopBridge.blockers || ['desktop_bridge_unavailable'])
|
|
248
250
|
: [])
|
|
249
251
|
].map(String).filter(Boolean))];
|
|
252
|
+
const bridgeReadinessRequired = doctorProfileRequiresDesktopBridgeReadiness(doctorProfile);
|
|
253
|
+
let migrationReceipt = null;
|
|
254
|
+
if (doctorProfile === 'migration') {
|
|
255
|
+
const writeReceiptImpl = deps.writeProjectUpdateMigrationReceiptImpl || writeProjectUpdateMigrationReceipt;
|
|
256
|
+
migrationReceipt = await writeReceiptImpl({
|
|
257
|
+
root: home,
|
|
258
|
+
source: `doctor-${doctorProfile}`,
|
|
259
|
+
blockers: migrationScopeBlockers,
|
|
260
|
+
warnings: ['global_only_home_scope_migration_receipt'],
|
|
261
|
+
...(migrationScopeBlockers.length ? { status: 'blocked' } : {})
|
|
262
|
+
}).catch((err) => ({
|
|
263
|
+
schema: 'sks.project-migration-receipt.v2',
|
|
264
|
+
status: 'blocked',
|
|
265
|
+
sks_version: null,
|
|
266
|
+
root: home,
|
|
267
|
+
source: `doctor-${doctorProfile}`,
|
|
268
|
+
generated_at: new Date().toISOString(),
|
|
269
|
+
blockers: [`migration_receipt_failed:${err?.message || String(err)}`],
|
|
270
|
+
warnings: []
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
const migrationReceiptBlockers = migrationReceipt && !isUpdateMigrationReceiptCurrent(migrationReceipt)
|
|
274
|
+
? ((migrationReceipt.blockers || []).length ? migrationReceipt.blockers : ['global_migration_receipt_not_current'])
|
|
275
|
+
: [];
|
|
276
|
+
const blockers = [...new Set([
|
|
277
|
+
...migrationScopeBlockers,
|
|
278
|
+
...(!menuBarReady ? (menuBar?.blockers || ['sks_menubar_repair_failed']) : []),
|
|
279
|
+
...(bridgeReadinessRequired ? bridgeBlockers : []),
|
|
280
|
+
...migrationReceiptBlockers
|
|
281
|
+
].map(String).filter(Boolean))];
|
|
282
|
+
const warnings = [...new Set([
|
|
283
|
+
...(!bridgeReadinessRequired ? bridgeBlockers.map((blocker) => `migration_optional_blocker:${blocker}`) : []),
|
|
284
|
+
...(desktopBridgeRepair?.warnings || []),
|
|
285
|
+
...(desktopBridge?.warnings || [])
|
|
286
|
+
].map(String).filter(Boolean))];
|
|
250
287
|
const ok = blockers.length === 0;
|
|
251
288
|
const projectRootAliasDetected = path.resolve(root) === home;
|
|
252
289
|
return {
|
|
@@ -257,6 +294,7 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
257
294
|
diagnostic_depth: 'global-only',
|
|
258
295
|
global_only: true,
|
|
259
296
|
install_scope: 'global',
|
|
297
|
+
doctor_profile: doctorProfile,
|
|
260
298
|
root,
|
|
261
299
|
home,
|
|
262
300
|
project_root_alias_detected: projectRootAliasDetected,
|
|
@@ -268,20 +306,26 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
268
306
|
'project_supabase_mcp_repair',
|
|
269
307
|
'project_hook_trust_repair',
|
|
270
308
|
'project_command_alias_cleanup',
|
|
271
|
-
'project_migration_receipt'
|
|
309
|
+
...(migrationReceipt ? [] : ['project_migration_receipt'])
|
|
272
310
|
],
|
|
273
311
|
skills: { global: globalSkills, project: { skipped: true, reason: 'global_only_doctor' } },
|
|
274
312
|
current_public_surface: currentSurface,
|
|
275
313
|
codex_app_fast_mode: globalFastMode,
|
|
276
314
|
openrouter_provider: desktopBridge.providers?.openrouter || null,
|
|
277
315
|
sks_menubar: menuBar,
|
|
316
|
+
desktop_bridge_repair: desktopBridgeRepair,
|
|
278
317
|
desktop_bridge: desktopBridge,
|
|
318
|
+
...(migrationReceipt ? { migration_receipt: migrationReceipt } : {}),
|
|
279
319
|
blockers,
|
|
320
|
+
warnings,
|
|
280
321
|
next_actions: [
|
|
281
322
|
...(projectRootAliasDetected
|
|
282
323
|
? ['Project checks were skipped because this directory is your home folder. Run them from your project: cd <your-project> && sks doctor --fix']
|
|
283
324
|
: []),
|
|
284
325
|
...(desktopBridge.ok === false ? desktopBridge.recovery_actions || [] : []),
|
|
326
|
+
...(!bridgeReadinessRequired && bridgeBlockers.length
|
|
327
|
+
? ['Desktop Bridge still reports blockers: run `sks doctor --fix` to repair the bridge catalog.']
|
|
328
|
+
: []),
|
|
285
329
|
'Run `sks doctor --fix --json` from a specific project directory when project-scoped repair is required.'
|
|
286
330
|
]
|
|
287
331
|
};
|
|
@@ -300,10 +344,19 @@ async function runDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
300
344
|
printJson(result);
|
|
301
345
|
}
|
|
302
346
|
else {
|
|
347
|
+
const bridgeRepair = result.desktop_bridge_repair;
|
|
303
348
|
console.log(`SKS Doctor global repair: ${result.ok ? 'ok' : 'blocked'}`);
|
|
304
349
|
console.log(`Global skills: ${result.skills.global?.error ? 'blocked' : 'reconciled'}`);
|
|
305
350
|
console.log(`SKS menu bar: ${result.sks_menubar?.status || (result.sks_menubar?.ok ? 'ok' : 'blocked')}`);
|
|
351
|
+
console.log(`Desktop Bridge repair: ${bridgeRepair?.skipped_reason
|
|
352
|
+
? bridgeRepair.skipped_reason
|
|
353
|
+
: bridgeRepair?.repaired
|
|
354
|
+
? 'repaired'
|
|
355
|
+
: bridgeRepair?.ok === false ? 'failed' : 'nothing_to_repair'}`);
|
|
306
356
|
console.log(`Desktop Bridge: ${result.desktop_bridge.ok ? 'ready' : 'blocked'} (${result.desktop_bridge.status?.readiness?.state || 'unavailable'})`);
|
|
357
|
+
if (result.migration_receipt) {
|
|
358
|
+
console.log(`Migration receipt: ${result.migration_receipt.status} (${result.home})`);
|
|
359
|
+
}
|
|
307
360
|
for (const blocker of result.blockers)
|
|
308
361
|
console.log(`- blocker: ${blocker}`);
|
|
309
362
|
for (const action of result.next_actions)
|
|
@@ -1027,61 +1080,7 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
|
|
|
1027
1080
|
{
|
|
1028
1081
|
id: 'desktop_bridge_catalog_repair',
|
|
1029
1082
|
required_for_ready: false,
|
|
1030
|
-
run: async () => {
|
|
1031
|
-
const base = {
|
|
1032
|
-
id: 'desktop_bridge_catalog_repair',
|
|
1033
|
-
required_for_ready: false,
|
|
1034
|
-
manual_required: false,
|
|
1035
|
-
warnings: [],
|
|
1036
|
-
rollback_evidence: 'combined_catalog_previous_generation_preserved'
|
|
1037
|
-
};
|
|
1038
|
-
try {
|
|
1039
|
-
const bridgeHome = path.resolve(process.env.HOME || os.homedir());
|
|
1040
|
-
const restarted = await restartStaleDesktopBridgeRuntime({
|
|
1041
|
-
home: bridgeHome,
|
|
1042
|
-
fix: doctorFix,
|
|
1043
|
-
});
|
|
1044
|
-
const status = await desktopBridgeStatusV3({ home: bridgeHome, env: process.env });
|
|
1045
|
-
const blockers = (status?.readiness?.blockers || []).map(String);
|
|
1046
|
-
const stale = blockers.filter((blocker) => blocker.endsWith('_catalog_stale'));
|
|
1047
|
-
if (!status?.management?.managed || stale.length === 0) {
|
|
1048
|
-
return {
|
|
1049
|
-
...base,
|
|
1050
|
-
ok: restarted.blockers.length === 0,
|
|
1051
|
-
repaired: restarted.restarted,
|
|
1052
|
-
warnings: restarted.warnings,
|
|
1053
|
-
blockers: restarted.blockers
|
|
1054
|
-
};
|
|
1055
|
-
}
|
|
1056
|
-
const attempts = [];
|
|
1057
|
-
let syncedAndVerified = false;
|
|
1058
|
-
for (let attempt = 1; attempt <= CATALOG_REPAIR_MAX_ATTEMPTS; attempt += 1) {
|
|
1059
|
-
const sync = await executeDesktopBridgeCommandV3({ operation: 'catalog.sync' }, { home: bridgeHome, env: process.env });
|
|
1060
|
-
const commandOk = sync?.ok === true && sync?.execution?.ok === true;
|
|
1061
|
-
const after = await desktopBridgeStatusV3({ home: bridgeHome, env: process.env });
|
|
1062
|
-
const stillStale = (after?.readiness?.blockers || [])
|
|
1063
|
-
.map(String).filter((blocker) => blocker.endsWith('_catalog_stale'));
|
|
1064
|
-
if (commandOk && stillStale.length === 0) {
|
|
1065
|
-
syncedAndVerified = true;
|
|
1066
|
-
break;
|
|
1067
|
-
}
|
|
1068
|
-
attempts.push(`catalog_sync_attempt_${attempt}:${commandOk ? `still_stale:${stillStale.join(',')}` : (sync?.execution?.blockers || ['failed']).map(String).join(',')}`);
|
|
1069
|
-
}
|
|
1070
|
-
return {
|
|
1071
|
-
...base,
|
|
1072
|
-
ok: syncedAndVerified && restarted.blockers.length === 0,
|
|
1073
|
-
repaired: syncedAndVerified || restarted.restarted,
|
|
1074
|
-
warnings: [...restarted.warnings, ...(syncedAndVerified ? attempts : [])],
|
|
1075
|
-
blockers: [
|
|
1076
|
-
...(syncedAndVerified ? [] : ['desktop_bridge_catalog_still_stale_after_repair', ...attempts]),
|
|
1077
|
-
...restarted.blockers
|
|
1078
|
-
]
|
|
1079
|
-
};
|
|
1080
|
-
}
|
|
1081
|
-
catch (error) {
|
|
1082
|
-
return { ...base, ok: false, repaired: false, blockers: [String(error?.message || 'desktop_bridge_catalog_sync_failed')] };
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1083
|
+
run: async () => repairDoctorDesktopBridgeCatalog({ fix: doctorFix })
|
|
1085
1084
|
}
|
|
1086
1085
|
].filter((phase) => doctorPhaseIds.includes(phase.id))
|
|
1087
1086
|
}).catch((err) => ({
|
|
@@ -273,13 +273,23 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
|
|
|
273
273
|
upstream.once('response', (response) => {
|
|
274
274
|
const statusCode = response.statusCode || 502;
|
|
275
275
|
if (statusCode >= 400) {
|
|
276
|
-
void readRedactedUpstreamError(response).then(({ body, upstreamCode }) => {
|
|
276
|
+
void readRedactedUpstreamError(response).then(({ body, upstreamCode, upstreamType }) => {
|
|
277
|
+
const transientMislabel = statusCode === 404
|
|
278
|
+
&& (upstreamType === 'upstream_error' || upstreamCode === 'upstream_error');
|
|
279
|
+
if (transientMislabel && replayable && !useFreshConnection) {
|
|
280
|
+
responseStarted = true;
|
|
281
|
+
finish(new StalePooledSocketFailure(new DesktopBridgeError('bridge_upstream_transient_mislabel')));
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const clientStatus = transientMislabel ? 503 : statusCode;
|
|
277
285
|
logHttpRejection({
|
|
278
|
-
code:
|
|
286
|
+
code: transientMislabel
|
|
287
|
+
? `bridge_upstream_status_${statusCode}_translated_503`
|
|
288
|
+
: upstreamCode ? `bridge_upstream_status_${statusCode}:${upstreamCode}` : `bridge_upstream_status_${statusCode}`,
|
|
279
289
|
transport: 'http',
|
|
280
290
|
...(req.method === undefined ? {} : { method: req.method }),
|
|
281
291
|
...(req.url === undefined ? {} : { url: req.url }),
|
|
282
|
-
status:
|
|
292
|
+
status: clientStatus,
|
|
283
293
|
provider_id: provider.provider_id,
|
|
284
294
|
public_model: request.route.public_model,
|
|
285
295
|
});
|
|
@@ -287,7 +297,9 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
|
|
|
287
297
|
const responseHeaders = rewriteResponseHeaders(response.headers, provider.base_url, authenticatedLocalBaseUrl);
|
|
288
298
|
responseHeaders['content-length'] = String(body.length);
|
|
289
299
|
delete responseHeaders['transfer-encoding'];
|
|
290
|
-
|
|
300
|
+
if (transientMislabel)
|
|
301
|
+
responseHeaders['retry-after'] = '10';
|
|
302
|
+
res.writeHead(clientStatus, responseHeaders);
|
|
291
303
|
res.end(body, () => finish());
|
|
292
304
|
}).catch(finish);
|
|
293
305
|
return;
|
|
@@ -255,6 +255,11 @@ export async function updateCommand(sub = 'now', args = []) {
|
|
|
255
255
|
console.log(`New version: ${result.new_version}`);
|
|
256
256
|
if (result.project_receipt)
|
|
257
257
|
console.log(`Migration receipt: ${result.project_receipt.root} (${result.migration_current ? 'current' : 'not current'})`);
|
|
258
|
+
for (const warning of (result.project_receipt?.optional_warnings || [])
|
|
259
|
+
.filter((entry) => String(entry).includes('sks doctor --fix'))
|
|
260
|
+
.slice(0, 3)) {
|
|
261
|
+
console.log(`Follow-up: ${warning}`);
|
|
262
|
+
}
|
|
258
263
|
if (result.sks_menubar)
|
|
259
264
|
console.log(`SKS menu bar: ${result.sks_menubar.status}${result.sks_menubar.app_path ? ` (${result.sks_menubar.app_path})` : ''}`);
|
|
260
265
|
if (result.operation_receipt_path)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { desktopBridgeStatusV3, executeDesktopBridgeCommandV3 } from '../codex-lb/desktop-controller-v3.js';
|
|
4
|
+
import { bootstrapExistingDesktopBridgeService, desktopBridgeServiceStatus } from '../codex-lb/desktop-service.js';
|
|
5
|
+
import { desktopBridgeRuntimeVersion, desktopBridgeRuntimeVersionStale } from '../codex-lb/desktop-bridge/state.js';
|
|
6
|
+
import { PACKAGE_VERSION } from '../version.js';
|
|
7
|
+
export async function restartStaleDesktopBridgeRuntime(input) {
|
|
8
|
+
if (process.env.SKS_TEST_ISOLATION === '1' || process.env.SKS_RELEASE_UPGRADE_SMOKE === '1') {
|
|
9
|
+
return { restarted: false, warnings: [], blockers: [] };
|
|
10
|
+
}
|
|
11
|
+
const service = await desktopBridgeServiceStatus({ home: input.home }).catch(() => null);
|
|
12
|
+
if (!service?.running || !desktopBridgeRuntimeVersionStale(service.state)) {
|
|
13
|
+
return { restarted: false, warnings: [], blockers: [] };
|
|
14
|
+
}
|
|
15
|
+
const running = desktopBridgeRuntimeVersion(service.state) || 'pre-8.6.2';
|
|
16
|
+
if (!input.fix) {
|
|
17
|
+
return {
|
|
18
|
+
restarted: false,
|
|
19
|
+
warnings: [],
|
|
20
|
+
blockers: [`desktop_bridge_runtime_version_stale:${running}:${PACKAGE_VERSION}`]
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const restarted = await bootstrapExistingDesktopBridgeService({ home: input.home }).catch(() => null);
|
|
24
|
+
const nowRunning = restarted?.running === true && !desktopBridgeRuntimeVersionStale(restarted.state);
|
|
25
|
+
return nowRunning
|
|
26
|
+
? { restarted: true, warnings: [`desktop_bridge_runtime_restarted:${running}:${PACKAGE_VERSION}`], blockers: [] }
|
|
27
|
+
: { restarted: false, warnings: [], blockers: [`desktop_bridge_runtime_version_stale:${running}:${PACKAGE_VERSION}`] };
|
|
28
|
+
}
|
|
29
|
+
export const CATALOG_REPAIR_MAX_ATTEMPTS = 2;
|
|
30
|
+
export async function repairDoctorDesktopBridgeCatalog(input, deps = {}) {
|
|
31
|
+
const base = {
|
|
32
|
+
id: 'desktop_bridge_catalog_repair',
|
|
33
|
+
required_for_ready: false,
|
|
34
|
+
manual_required: false,
|
|
35
|
+
warnings: [],
|
|
36
|
+
rollback_evidence: 'combined_catalog_previous_generation_preserved'
|
|
37
|
+
};
|
|
38
|
+
const usingRealImpls = !deps.restartStaleDesktopBridgeRuntimeImpl
|
|
39
|
+
&& !deps.desktopBridgeStatusImpl
|
|
40
|
+
&& !deps.executeDesktopBridgeCommandImpl;
|
|
41
|
+
if (usingRealImpls
|
|
42
|
+
&& (process.env.NODE_TEST_CONTEXT !== undefined
|
|
43
|
+
|| process.env.SKS_TEST_ISOLATION === '1'
|
|
44
|
+
|| process.env.SKS_RELEASE_UPGRADE_SMOKE === '1')) {
|
|
45
|
+
return {
|
|
46
|
+
...base,
|
|
47
|
+
ok: true,
|
|
48
|
+
repaired: false,
|
|
49
|
+
blockers: [],
|
|
50
|
+
skipped_reason: 'desktop_bridge_catalog_repair_skipped_under_tests'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const restartImpl = deps.restartStaleDesktopBridgeRuntimeImpl || restartStaleDesktopBridgeRuntime;
|
|
54
|
+
const statusImpl = deps.desktopBridgeStatusImpl || desktopBridgeStatusV3;
|
|
55
|
+
const executeImpl = deps.executeDesktopBridgeCommandImpl || executeDesktopBridgeCommandV3;
|
|
56
|
+
try {
|
|
57
|
+
const bridgeHome = path.resolve(process.env.HOME || os.homedir());
|
|
58
|
+
const restarted = await restartImpl({
|
|
59
|
+
home: bridgeHome,
|
|
60
|
+
fix: input.fix
|
|
61
|
+
});
|
|
62
|
+
const status = await statusImpl({ home: bridgeHome, env: process.env });
|
|
63
|
+
const blockers = (status?.readiness?.blockers || []).map(String);
|
|
64
|
+
const stale = blockers.filter((blocker) => blocker.endsWith('_catalog_stale'));
|
|
65
|
+
if (!status?.management?.managed || stale.length === 0) {
|
|
66
|
+
return {
|
|
67
|
+
...base,
|
|
68
|
+
ok: restarted.blockers.length === 0,
|
|
69
|
+
repaired: restarted.restarted,
|
|
70
|
+
warnings: restarted.warnings,
|
|
71
|
+
blockers: restarted.blockers
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const attempts = [];
|
|
75
|
+
let syncedAndVerified = false;
|
|
76
|
+
for (let attempt = 1; attempt <= CATALOG_REPAIR_MAX_ATTEMPTS; attempt += 1) {
|
|
77
|
+
const sync = await executeImpl({ operation: 'catalog.sync' }, { home: bridgeHome, env: process.env });
|
|
78
|
+
const commandOk = sync?.ok === true && sync?.execution?.ok === true;
|
|
79
|
+
const after = await statusImpl({ home: bridgeHome, env: process.env });
|
|
80
|
+
const stillStale = (after?.readiness?.blockers || [])
|
|
81
|
+
.map(String).filter((blocker) => blocker.endsWith('_catalog_stale'));
|
|
82
|
+
if (commandOk && stillStale.length === 0) {
|
|
83
|
+
syncedAndVerified = true;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
attempts.push(`catalog_sync_attempt_${attempt}:${commandOk ? `still_stale:${stillStale.join(',')}` : (sync?.execution?.blockers || ['failed']).map(String).join(',')}`);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
...base,
|
|
90
|
+
ok: syncedAndVerified && restarted.blockers.length === 0,
|
|
91
|
+
repaired: syncedAndVerified || restarted.restarted,
|
|
92
|
+
warnings: [...restarted.warnings, ...(syncedAndVerified ? attempts : [])],
|
|
93
|
+
blockers: [
|
|
94
|
+
...(syncedAndVerified ? [] : ['desktop_bridge_catalog_still_stale_after_repair', ...attempts]),
|
|
95
|
+
...restarted.blockers
|
|
96
|
+
]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return { ...base, ok: false, repaired: false, blockers: [String(error?.message || 'desktop_bridge_catalog_sync_failed')] };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -33,17 +33,21 @@ function sortedHashes(hashes) {
|
|
|
33
33
|
}
|
|
34
34
|
export class CodeGraphExtractor {
|
|
35
35
|
preparedInventory;
|
|
36
|
+
sourceInventory;
|
|
36
37
|
id = CODE_GRAPH_EXTRACTOR_ID;
|
|
37
38
|
revision = CODE_GRAPH_EXTRACTOR_REVISION;
|
|
38
|
-
constructor(preparedInventory = null) {
|
|
39
|
+
constructor(preparedInventory = null, sourceInventory = null) {
|
|
39
40
|
this.preparedInventory = preparedInventory;
|
|
41
|
+
this.sourceInventory = sourceInventory;
|
|
40
42
|
}
|
|
41
43
|
async extract(input) {
|
|
42
44
|
const startedAt = Date.now();
|
|
43
45
|
const deadline = startedAt + Math.max(1, input.limits.timeoutMs);
|
|
44
46
|
const root = realRoot(input.root);
|
|
45
47
|
const sink = new CodeGraphSink(input.limits, input.observedAt);
|
|
46
|
-
const inventory = this.preparedInventory
|
|
48
|
+
const inventory = this.preparedInventory
|
|
49
|
+
?? this.sourceInventory?.inventory(root, input.limits)
|
|
50
|
+
?? walkCodeInventory(root, input.limits);
|
|
47
51
|
for (const skip of inventory.skipped)
|
|
48
52
|
sink.addSkip(skip);
|
|
49
53
|
const hashes = new Map();
|
|
@@ -273,6 +277,6 @@ function collectNodeRels(factsByRel, parsedByRel, inventory, selectedRels) {
|
|
|
273
277
|
return [...rels].sort();
|
|
274
278
|
}
|
|
275
279
|
export function createCodeGraphExtractor(options = {}) {
|
|
276
|
-
return new CodeGraphExtractor(options.preparedInventory ?? null);
|
|
280
|
+
return new CodeGraphExtractor(options.preparedInventory ?? null, options.sourceInventory ?? null);
|
|
277
281
|
}
|
|
278
282
|
export { CODE_GRAPH_EXTRACTOR_ID, CODE_GRAPH_EXTRACTOR_REVISION } from './types.js';
|
|
@@ -2,15 +2,25 @@ import { lintWarning } from '../../contracts.js';
|
|
|
2
2
|
import { extractContextPackEvidence } from './claims.js';
|
|
3
3
|
import { extractProofEvidence } from './proofs.js';
|
|
4
4
|
import { sanitizeEvidenceFragment } from './redaction.js';
|
|
5
|
+
import { createSharedSourceInventory } from '../source-inventory.js';
|
|
5
6
|
import { CONTEXT_PACK_REL, EVIDENCE_EXTRACTOR_ID, EVIDENCE_EXTRACTOR_REVISION, EvidenceFragmentBuilder, PROOF_BANK_REL, RiskDomainRegistry, evidenceContext, finalizeEvidenceFragment } from './shared.js';
|
|
6
7
|
export { CONTEXT_PACK_REL, EVIDENCE_EXTRACTOR_ID, EVIDENCE_EXTRACTOR_REVISION, PROOF_BANK_REL, PROOF_INDEX_REL } from './shared.js';
|
|
7
8
|
export { TRIWIKI_PROOF_INDEX_SCHEMA } from './proof-index.js';
|
|
8
9
|
const MIN_FAN_IN_FOR_VERIFICATION_WARNING = 3;
|
|
9
10
|
export class EvidenceGraphExtractor {
|
|
11
|
+
sourceInventory;
|
|
10
12
|
id = EVIDENCE_EXTRACTOR_ID;
|
|
11
13
|
revision = EVIDENCE_EXTRACTOR_REVISION;
|
|
14
|
+
constructor(sourceInventory = createSharedSourceInventory()) {
|
|
15
|
+
this.sourceInventory = sourceInventory;
|
|
16
|
+
}
|
|
12
17
|
async extract(input) {
|
|
13
|
-
const ctx = evidenceContext({
|
|
18
|
+
const ctx = evidenceContext({
|
|
19
|
+
root: input.root,
|
|
20
|
+
observedAt: input.observedAt,
|
|
21
|
+
limits: input.limits,
|
|
22
|
+
sourcePaths: this.sourceInventory.sourcePaths(input.root, input.limits)
|
|
23
|
+
});
|
|
14
24
|
const builder = new EvidenceFragmentBuilder(input.limits, input.observedAt);
|
|
15
25
|
const risks = new RiskDomainRegistry();
|
|
16
26
|
const pack = extractContextPackEvidence(builder, ctx, risks);
|
|
@@ -26,8 +36,8 @@ export class EvidenceGraphExtractor {
|
|
|
26
36
|
return finalizeEvidenceFragment(sanitizeEvidenceFragment(builder.fragment));
|
|
27
37
|
}
|
|
28
38
|
}
|
|
29
|
-
export function createEvidenceGraphExtractor() {
|
|
30
|
-
return new EvidenceGraphExtractor();
|
|
39
|
+
export function createEvidenceGraphExtractor(options = {}) {
|
|
40
|
+
return new EvidenceGraphExtractor(options.sourceInventory ?? createSharedSourceInventory());
|
|
31
41
|
}
|
|
32
42
|
function noteUnverifiedFanIn(builder) {
|
|
33
43
|
const incoming = new Map();
|
|
@@ -3,7 +3,7 @@ import { sha256 } from '../../../../fsx.js';
|
|
|
3
3
|
import { resolveInsideWorkspace, tryNormalizeGraphPath } from '../../paths.js';
|
|
4
4
|
import { PROOF_BANK_REL, PROOF_INDEX_REL, asArray, asRecord, asString, asStringList, readWorkspaceFile, statWorkspaceEntry } from './shared.js';
|
|
5
5
|
export const TRIWIKI_PROOF_INDEX_SCHEMA = 'sks.triwiki-proof-index.v1';
|
|
6
|
-
const MAX_PROOF_RECORDS =
|
|
6
|
+
const MAX_PROOF_RECORDS = 4096;
|
|
7
7
|
const MAX_SCAN_DEPTH = 4;
|
|
8
8
|
const SCAN_EXCLUDED_DIRS = new Set(['.locks', 'node_modules', '.git']);
|
|
9
9
|
export function discoverProofRecords(ctx) {
|
|
@@ -7,10 +7,6 @@ import { EvidenceFragmentBuilder, PROOF_BANK_REL, RiskDomainRegistry, asRecord,
|
|
|
7
7
|
import { discoverProofRecords } from './proof-index.js';
|
|
8
8
|
const MAX_DERIVED_INPUTS = 12;
|
|
9
9
|
const PLACEHOLDER_HASHES = new Set(['', 'unknown', 'legacy-missing', 'none', 'null']);
|
|
10
|
-
const HASH_PINNED_INPUTS = [
|
|
11
|
-
{ field: 'package_lock_hash', rel: 'package-lock.json' },
|
|
12
|
-
{ field: 'release_gates_hash', rel: 'release-gates.v2.json' }
|
|
13
|
-
];
|
|
14
10
|
export function extractProofEvidence(builder, ctx, risks) {
|
|
15
11
|
const discovery = discoverProofRecords(ctx);
|
|
16
12
|
for (const skip of discovery.skipped)
|
|
@@ -220,15 +216,7 @@ function linkSubject(builder, record, health, proofNodeId, mode) {
|
|
|
220
216
|
}
|
|
221
217
|
function linkDerivedInputs(builder, ctx, record, proofNodeId) {
|
|
222
218
|
const raw = record.card;
|
|
223
|
-
const candidates = [];
|
|
224
|
-
if (raw) {
|
|
225
|
-
for (const pinned of HASH_PINNED_INPUTS) {
|
|
226
|
-
const hash = asString(raw[pinned.field]);
|
|
227
|
-
if (hash && !PLACEHOLDER_HASHES.has(hash))
|
|
228
|
-
candidates.push(pinned.rel);
|
|
229
|
-
}
|
|
230
|
-
candidates.push(...asStringList(raw.input_paths), ...asStringList(raw.source_paths));
|
|
231
|
-
}
|
|
219
|
+
const candidates = raw ? [...asStringList(raw.input_paths), ...asStringList(raw.source_paths)] : [];
|
|
232
220
|
const seen = new Set();
|
|
233
221
|
for (const candidate of candidates) {
|
|
234
222
|
if (seen.size >= MAX_DERIVED_INPUTS)
|
|
@@ -236,6 +224,8 @@ function linkDerivedInputs(builder, ctx, record, proofNodeId) {
|
|
|
236
224
|
const rel = tryNormalizeGraphPath(ctx.root, candidate);
|
|
237
225
|
if (!rel || seen.has(rel))
|
|
238
226
|
continue;
|
|
227
|
+
if (!ctx.sourcePaths.has(rel))
|
|
228
|
+
continue;
|
|
239
229
|
const stat = statWorkspaceEntry(ctx.root, rel);
|
|
240
230
|
if (!stat || !stat.isFile())
|
|
241
231
|
continue;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { REDACTION_MARKER, containsPlaintextSecret, redactString } from '../../../../secret-redaction.js';
|
|
2
2
|
import { lintError } from '../../contracts.js';
|
|
3
|
+
import { isStructuralValue } from '../../lint/rules.js';
|
|
3
4
|
import { isWorkspaceRelativePosixPath } from '../../paths.js';
|
|
4
5
|
export const EVIDENCE_REDACTED_PATH = '[redacted-path]';
|
|
5
6
|
export const EVIDENCE_MAX_META_STRING = 160;
|
|
@@ -134,8 +135,11 @@ function sanitizeMetadataValue(value) {
|
|
|
134
135
|
}
|
|
135
136
|
return { value, changed: false };
|
|
136
137
|
}
|
|
138
|
+
function identityLooksSecret(value) {
|
|
139
|
+
return !isStructuralValue(value) && containsPlaintextSecret(value);
|
|
140
|
+
}
|
|
137
141
|
function guardNode(node, extractor) {
|
|
138
|
-
if (
|
|
142
|
+
if (identityLooksSecret(node.id) || identityLooksSecret(node.label)) {
|
|
139
143
|
return {
|
|
140
144
|
node: null,
|
|
141
145
|
issue: lintError('secret_like_value', 'evidence node identity looks secret-like and was refused', {
|
|
@@ -144,7 +148,7 @@ function guardNode(node, extractor) {
|
|
|
144
148
|
})
|
|
145
149
|
};
|
|
146
150
|
}
|
|
147
|
-
if (node.path !== undefined &&
|
|
151
|
+
if (node.path !== undefined && identityLooksSecret(node.path)) {
|
|
148
152
|
return {
|
|
149
153
|
node: null,
|
|
150
154
|
issue: lintError('secret_like_value', 'evidence node path looks secret-like and was refused', {
|
|
Binary file
|
|
@@ -95,8 +95,9 @@ export class EvidenceSourceGraph {
|
|
|
95
95
|
tokenCost: tokenEstimate(rel.length, 64),
|
|
96
96
|
metadata
|
|
97
97
|
}, CONTEXT_PACK_REL);
|
|
98
|
-
if (!isDirectory && (diskHash || manifestHash))
|
|
98
|
+
if (!isDirectory && (diskHash || manifestHash) && this.ctx.sourcePaths.has(rel)) {
|
|
99
99
|
this.linkBackingFile(id, rel, stat, diskHash, manifestHash);
|
|
100
|
+
}
|
|
100
101
|
const state = { id, freshness };
|
|
101
102
|
this.states.set(rel, state);
|
|
102
103
|
return state;
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import { createCodeGraphExtractor } from './code/index.js';
|
|
2
2
|
import { createEvidenceGraphExtractor } from './evidence/index.js';
|
|
3
|
+
import { createSharedSourceInventory } from './source-inventory.js';
|
|
3
4
|
import { createTopologyGraphExtractor } from './topology/index.js';
|
|
4
5
|
export function contextGraphExtractors() {
|
|
5
|
-
|
|
6
|
+
const sourceInventory = createSharedSourceInventory();
|
|
7
|
+
return [
|
|
8
|
+
createCodeGraphExtractor({ sourceInventory }),
|
|
9
|
+
createTopologyGraphExtractor({ sourceInventory }),
|
|
10
|
+
createEvidenceGraphExtractor({ sourceInventory })
|
|
11
|
+
];
|
|
6
12
|
}
|
|
7
13
|
export function codeNavigationGraphExtractors(options = {}) {
|
|
8
14
|
return [createCodeGraphExtractor(options)];
|
|
9
15
|
}
|
|
10
16
|
export function architectureMapGraphExtractors(options = {}) {
|
|
11
|
-
|
|
17
|
+
const sourceInventory = createSharedSourceInventory(options.preparedInventory ?? null);
|
|
18
|
+
return [
|
|
19
|
+
createCodeGraphExtractor({ sourceInventory }),
|
|
20
|
+
createTopologyGraphExtractor({ sourceInventory }),
|
|
21
|
+
createEvidenceGraphExtractor({ sourceInventory })
|
|
22
|
+
];
|
|
12
23
|
}
|
|
13
24
|
export const ARCHITECTURE_MAP_EXTRACTOR_IDS = Object.freeze(['code', 'topology', 'triwiki-evidence']);
|
|
14
25
|
export { createCodeGraphExtractor, createEvidenceGraphExtractor, createTopologyGraphExtractor };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { walkCodeInventory } from './code/inventory.js';
|
|
4
|
+
function canonicalRoot(root) {
|
|
5
|
+
const absolute = path.resolve(root);
|
|
6
|
+
try {
|
|
7
|
+
return fs.realpathSync(absolute);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return absolute;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function createSharedSourceInventory(prepared = null) {
|
|
14
|
+
let cachedRoot = null;
|
|
15
|
+
let cachedInventory = prepared;
|
|
16
|
+
let cachedSet = null;
|
|
17
|
+
const inventory = (root, limits) => {
|
|
18
|
+
if (prepared)
|
|
19
|
+
return prepared;
|
|
20
|
+
const canonical = canonicalRoot(root);
|
|
21
|
+
if (!cachedInventory || cachedRoot !== canonical) {
|
|
22
|
+
cachedInventory = walkCodeInventory(canonical, limits);
|
|
23
|
+
cachedRoot = canonical;
|
|
24
|
+
}
|
|
25
|
+
return cachedInventory;
|
|
26
|
+
};
|
|
27
|
+
const sourcePaths = (root, limits) => {
|
|
28
|
+
const current = inventory(root, limits);
|
|
29
|
+
if (!cachedSet || cachedSet.inventory !== current) {
|
|
30
|
+
cachedSet = { inventory: current, set: new Set(current.files.map((file) => file.rel)) };
|
|
31
|
+
}
|
|
32
|
+
return cachedSet.set;
|
|
33
|
+
};
|
|
34
|
+
return { inventory, sourcePaths };
|
|
35
|
+
}
|
|
@@ -50,22 +50,26 @@ export function buildGateVerificationEdges(ctx, gates) {
|
|
|
50
50
|
const verified = new Set();
|
|
51
51
|
for (const gate of gates) {
|
|
52
52
|
let emitted = 0;
|
|
53
|
+
let capped = false;
|
|
53
54
|
for (const candidate of gateCheckCandidates(gate.command)) {
|
|
54
|
-
if (emitted >= TOPOLOGY_GATE_VERIFIED_CAP)
|
|
55
|
-
break;
|
|
56
55
|
const expansion = expandGlob(ctx.files, candidate, TOPOLOGY_GLOB_MATCH_CAP);
|
|
57
|
-
if (expansion.
|
|
58
|
-
recordSkip(ctx, gate.manifestPath, '
|
|
56
|
+
if (!expansion.total) {
|
|
57
|
+
recordSkip(ctx, gate.manifestPath, 'excluded', `check implementation ${candidate} is not in this workspace`);
|
|
59
58
|
continue;
|
|
60
59
|
}
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
verified.add(gate.id);
|
|
61
|
+
if (expansion.capped) {
|
|
62
|
+
recordSkip(ctx, gate.manifestPath, 'excluded', `check pattern ${candidate} matched ${expansion.total} files; kept whole as checkScripts metadata on the gate node`);
|
|
63
63
|
continue;
|
|
64
64
|
}
|
|
65
65
|
const confidence = isGlobPattern(candidate) ? 'derived' : 'manifest';
|
|
66
66
|
for (const match of expansion.matches) {
|
|
67
|
-
if (
|
|
67
|
+
if (!ctx.sourcePaths.has(match))
|
|
68
|
+
continue;
|
|
69
|
+
if (emitted >= TOPOLOGY_GATE_VERIFIED_CAP) {
|
|
70
|
+
capped = true;
|
|
68
71
|
break;
|
|
72
|
+
}
|
|
69
73
|
const fileId = ensureFileNode(ctx, match, 'gate_check', gate.manifestPath);
|
|
70
74
|
if (!fileId)
|
|
71
75
|
continue;
|
|
@@ -78,11 +82,14 @@ export function buildGateVerificationEdges(ctx, gates) {
|
|
|
78
82
|
hash: gate.manifestHash,
|
|
79
83
|
line: gate.line
|
|
80
84
|
});
|
|
81
|
-
if (added)
|
|
85
|
+
if (added)
|
|
82
86
|
emitted += 1;
|
|
83
|
-
verified.add(gate.id);
|
|
84
|
-
}
|
|
85
87
|
}
|
|
88
|
+
if (capped)
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
if (capped) {
|
|
92
|
+
recordSkip(ctx, gate.manifestPath, 'cap_reached', `gate ${gate.id} hit the verified_by edge cap (${TOPOLOGY_GATE_VERIFIED_CAP}); check relations are missing`);
|
|
86
93
|
}
|
|
87
94
|
}
|
|
88
95
|
return verified;
|
|
@@ -91,21 +98,26 @@ export function buildGateAffectedEdges(ctx, gates) {
|
|
|
91
98
|
const affected = new Set();
|
|
92
99
|
for (const gate of gates) {
|
|
93
100
|
let emitted = 0;
|
|
101
|
+
let capped = false;
|
|
94
102
|
const seen = new Set();
|
|
95
103
|
for (const input of gate.cacheInputs) {
|
|
96
|
-
if (emitted >= TOPOLOGY_GATE_AFFECTED_CAP)
|
|
97
|
-
break;
|
|
98
104
|
const expansion = expandGlob(ctx.files, input, TOPOLOGY_GLOB_MATCH_CAP);
|
|
105
|
+
if (expansion.total > 0)
|
|
106
|
+
affected.add(gate.id);
|
|
99
107
|
if (expansion.capped) {
|
|
100
|
-
recordSkip(ctx, gate.manifestPath, '
|
|
108
|
+
recordSkip(ctx, gate.manifestPath, 'excluded', `cache input ${input} matched ${expansion.total} files; kept whole as cacheInputs metadata on the gate node`);
|
|
101
109
|
continue;
|
|
102
110
|
}
|
|
103
111
|
for (const match of expansion.matches) {
|
|
104
|
-
if (emitted >= TOPOLOGY_GATE_AFFECTED_CAP)
|
|
105
|
-
break;
|
|
106
112
|
if (seen.has(match))
|
|
107
113
|
continue;
|
|
108
114
|
seen.add(match);
|
|
115
|
+
if (!ctx.sourcePaths.has(match))
|
|
116
|
+
continue;
|
|
117
|
+
if (emitted >= TOPOLOGY_GATE_AFFECTED_CAP) {
|
|
118
|
+
capped = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
109
121
|
const fileId = ensureFileNode(ctx, match, 'gate_cache_input', gate.manifestPath);
|
|
110
122
|
if (!fileId)
|
|
111
123
|
continue;
|
|
@@ -118,11 +130,14 @@ export function buildGateAffectedEdges(ctx, gates) {
|
|
|
118
130
|
hash: gate.manifestHash,
|
|
119
131
|
line: gate.line
|
|
120
132
|
});
|
|
121
|
-
if (added)
|
|
133
|
+
if (added)
|
|
122
134
|
emitted += 1;
|
|
123
|
-
affected.add(gate.id);
|
|
124
|
-
}
|
|
125
135
|
}
|
|
136
|
+
if (capped)
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
if (capped) {
|
|
140
|
+
recordSkip(ctx, gate.manifestPath, 'cap_reached', `gate ${gate.id} hit the affected_by edge cap (${TOPOLOGY_GATE_AFFECTED_CAP}); cache-input relations are missing`);
|
|
126
141
|
}
|
|
127
142
|
}
|
|
128
143
|
return affected;
|
|
@@ -4,6 +4,7 @@ import { buildCommandGraph, collectRuntimeManifest } from './commands.js';
|
|
|
4
4
|
import { buildGateAffectedEdges, buildGateDependencyEdges, buildGatePresetPipelines, buildGateVerificationEdges, reportUnbackedProtectedGates } from './gate-edges.js';
|
|
5
5
|
import { buildGateNodes, collectTopologyGates } from './gates.js';
|
|
6
6
|
import { buildFileInventory } from './globs.js';
|
|
7
|
+
import { createSharedSourceInventory } from '../source-inventory.js';
|
|
7
8
|
import { buildRouteGraph } from './routes.js';
|
|
8
9
|
import { TOPOLOGY_EXTRACTOR_ID, TOPOLOGY_EXTRACTOR_REVISION, createTopologyContext, recordSkip, topologyExpired } from './shared.js';
|
|
9
10
|
const INVENTORY_SKIP_PATH = 'release-gates.v2.json';
|
|
@@ -45,8 +46,12 @@ function finalize(ctx) {
|
|
|
45
46
|
return fragment;
|
|
46
47
|
}
|
|
47
48
|
export class TopologyGraphExtractor {
|
|
49
|
+
sourceInventory;
|
|
48
50
|
id = TOPOLOGY_EXTRACTOR_ID;
|
|
49
51
|
revision = TOPOLOGY_EXTRACTOR_REVISION;
|
|
52
|
+
constructor(sourceInventory = createSharedSourceInventory()) {
|
|
53
|
+
this.sourceInventory = sourceInventory;
|
|
54
|
+
}
|
|
50
55
|
async extract(input) {
|
|
51
56
|
const startedAt = Date.now();
|
|
52
57
|
const files = buildFileInventory(input.root, input.limits.maxFiles);
|
|
@@ -55,6 +60,7 @@ export class TopologyGraphExtractor {
|
|
|
55
60
|
observedAt: input.observedAt,
|
|
56
61
|
limits: input.limits,
|
|
57
62
|
files,
|
|
63
|
+
sourcePaths: this.sourceInventory.sourcePaths(input.root, input.limits),
|
|
58
64
|
startedAt
|
|
59
65
|
});
|
|
60
66
|
if (files.truncated) {
|
|
@@ -83,8 +89,8 @@ export class TopologyGraphExtractor {
|
|
|
83
89
|
return finalize(ctx);
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
|
-
export function createTopologyGraphExtractor() {
|
|
87
|
-
return new TopologyGraphExtractor();
|
|
92
|
+
export function createTopologyGraphExtractor(options = {}) {
|
|
93
|
+
return new TopologyGraphExtractor(options.sourceInventory ?? createSharedSourceInventory());
|
|
88
94
|
}
|
|
89
95
|
export { TOPOLOGY_EXTRACTOR_ID, TOPOLOGY_EXTRACTOR_REVISION } from './shared.js';
|
|
90
96
|
export { TOPOLOGY_GATE_MANIFESTS } from './gates.js';
|
|
@@ -4,10 +4,10 @@ import { lintError } from '../../contracts.js';
|
|
|
4
4
|
import { contextGraphEdgeId, contextGraphNodeId } from '../../ids.js';
|
|
5
5
|
import { ContextGraphPathError, isWorkspaceRelativePosixPath, resolveInsideWorkspace } from '../../paths.js';
|
|
6
6
|
export const TOPOLOGY_EXTRACTOR_ID = 'topology';
|
|
7
|
-
export const TOPOLOGY_EXTRACTOR_REVISION = '1.
|
|
7
|
+
export const TOPOLOGY_EXTRACTOR_REVISION = '1.1.0';
|
|
8
8
|
export const TOPOLOGY_GLOB_MATCH_CAP = 48;
|
|
9
|
-
export const TOPOLOGY_GATE_AFFECTED_CAP =
|
|
10
|
-
export const TOPOLOGY_GATE_VERIFIED_CAP =
|
|
9
|
+
export const TOPOLOGY_GATE_AFFECTED_CAP = 256;
|
|
10
|
+
export const TOPOLOGY_GATE_VERIFIED_CAP = 96;
|
|
11
11
|
export function createTopologyContext(params) {
|
|
12
12
|
const timeout = Number.isFinite(params.limits.timeoutMs) && params.limits.timeoutMs > 0 ? params.limits.timeoutMs : 0;
|
|
13
13
|
return {
|
|
@@ -16,6 +16,7 @@ export function createTopologyContext(params) {
|
|
|
16
16
|
limits: params.limits,
|
|
17
17
|
deadline: params.startedAt + timeout,
|
|
18
18
|
files: params.files,
|
|
19
|
+
sourcePaths: params.sourcePaths,
|
|
19
20
|
nodes: new Map(),
|
|
20
21
|
edges: new Map(),
|
|
21
22
|
issues: [],
|
|
@@ -114,6 +115,8 @@ export function addEdge(ctx, input) {
|
|
|
114
115
|
export function ensureFileNode(ctx, relativePath, role, sourcePath) {
|
|
115
116
|
if (!isWorkspaceRelativePosixPath(relativePath))
|
|
116
117
|
return null;
|
|
118
|
+
if (!ctx.sourcePaths.has(relativePath))
|
|
119
|
+
return null;
|
|
117
120
|
const id = contextGraphNodeId({ kind: 'file', path: relativePath });
|
|
118
121
|
const classification = ctx.fileClassification.get(relativePath);
|
|
119
122
|
const metadata = {
|
|
@@ -82,7 +82,7 @@ function looksHighEntropy(token) {
|
|
|
82
82
|
return true;
|
|
83
83
|
return /[A-Z]/.test(token) && /[a-z]/.test(token) && /[0-9]/.test(token);
|
|
84
84
|
}
|
|
85
|
-
function isStructuralValue(value) {
|
|
85
|
+
export function isStructuralValue(value) {
|
|
86
86
|
if (value.length > 512)
|
|
87
87
|
return false;
|
|
88
88
|
for (const run of value.match(HIGH_ENTROPY_RUN_RE) ?? []) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export async function runCurrentPublicSurfaceReconcileStage(root) {
|
|
4
|
+
const [{ runDoctorCommandAliasCleanup }, { migrateSksProfilesToPerFile }, { cleanupRetiredRemoteBridgeLaunchAgent, quarantineRetiredRemoteBridgeBindings }] = await Promise.all([
|
|
5
|
+
import('../../doctor/command-alias-cleanup.js'),
|
|
6
|
+
import('../../auto-review.js'),
|
|
7
|
+
import('../../codex-app/menubar/migration.js')
|
|
8
|
+
]);
|
|
9
|
+
const home = path.resolve(process.env.HOME || os.homedir());
|
|
10
|
+
const globalRuntimeRoot = path.resolve(process.env.SKS_GLOBAL_ROOT || path.join(home, '.sneakoscope-global'));
|
|
11
|
+
const publicSurface = await runDoctorCommandAliasCleanup({
|
|
12
|
+
root,
|
|
13
|
+
home,
|
|
14
|
+
globalRuntimeRoot,
|
|
15
|
+
fix: true,
|
|
16
|
+
managedGenerationAlreadyConverged: true
|
|
17
|
+
});
|
|
18
|
+
const profileMigration = await migrateSksProfilesToPerFile({ env: process.env }).catch((err) => ({
|
|
19
|
+
error: err?.message || String(err),
|
|
20
|
+
retired_profile_table_count: 0,
|
|
21
|
+
retired_profile_file_removed_count: 0
|
|
22
|
+
}));
|
|
23
|
+
const retiredLaunchAgent = await cleanupRetiredRemoteBridgeLaunchAgent({ home, env: process.env });
|
|
24
|
+
const retiredBindings = await quarantineRetiredRemoteBridgeBindings(root);
|
|
25
|
+
const remainingCount = Number(publicSurface.cleanup?.remaining_count || 0)
|
|
26
|
+
+ Number(publicSurface.cleanup?.managed_runtime?.remaining_managed_artifact_count || 0)
|
|
27
|
+
+ Number(publicSurface.cleanup?.project_guidance?.remaining_count || 0);
|
|
28
|
+
const blockers = [
|
|
29
|
+
...(publicSurface.ok === true ? [] : ['public_surface_reconcile_failed']),
|
|
30
|
+
...(retiredLaunchAgent.ok ? [] : retiredLaunchAgent.blockers),
|
|
31
|
+
...(retiredBindings.ok ? [] : retiredBindings.blockers),
|
|
32
|
+
...(profileMigration.error ? [`retired_profile_migration_failed:${profileMigration.error}`] : []),
|
|
33
|
+
...(remainingCount > 0 ? [`public_surface_remaining:${remainingCount}`] : [])
|
|
34
|
+
];
|
|
35
|
+
return {
|
|
36
|
+
ok: blockers.length === 0,
|
|
37
|
+
status: blockers.length ? 'failed' : 'ok',
|
|
38
|
+
actions: [
|
|
39
|
+
'reconciled_current_public_surface',
|
|
40
|
+
...(retiredLaunchAgent.status === 'removed' ? ['retired_remote_bridge_launch_agent_removed'] : []),
|
|
41
|
+
...(retiredBindings.status === 'quarantined' ? ['retired_remote_bridge_bindings_quarantined'] : [])
|
|
42
|
+
],
|
|
43
|
+
blockers,
|
|
44
|
+
warnings: [
|
|
45
|
+
...(publicSurface.warnings || []),
|
|
46
|
+
...retiredLaunchAgent.warnings,
|
|
47
|
+
...retiredBindings.warnings
|
|
48
|
+
],
|
|
49
|
+
detail: {
|
|
50
|
+
removed_skill_count: Number(publicSurface.cleanup?.removed_count || 0),
|
|
51
|
+
quarantined_skill_collision_count: Number(publicSurface.cleanup?.preserved_user_collision_count || 0),
|
|
52
|
+
removed_runtime_artifact_count: Number(publicSurface.cleanup?.managed_runtime?.removed_managed_artifact_count || 0),
|
|
53
|
+
quarantined_runtime_collision_count: Number(publicSurface.cleanup?.managed_runtime?.preserved_user_file_count || 0),
|
|
54
|
+
reconciled_guidance_count: Number(publicSurface.cleanup?.project_guidance?.reconciled_count || 0),
|
|
55
|
+
quarantined_guidance_collision_count: Number(publicSurface.cleanup?.project_guidance?.preserved_user_file_count || 0),
|
|
56
|
+
removed_retired_role_count: 0,
|
|
57
|
+
quarantined_retired_role_collision_count: 0,
|
|
58
|
+
retired_profile_table_count: Number(profileMigration.retired_profile_table_count || 0),
|
|
59
|
+
retired_profile_file_removed_count: Number(profileMigration.retired_profile_file_removed_count || 0),
|
|
60
|
+
retired_remote_bridge_launch_agent_status: retiredLaunchAgent.status,
|
|
61
|
+
retired_remote_bridge_binding_status: retiredBindings.status,
|
|
62
|
+
retired_remote_bridge_binding_count: retiredBindings.retired_binding_count,
|
|
63
|
+
retired_remote_bridge_binding_quarantine_path: retiredBindings.quarantine_path,
|
|
64
|
+
remaining_count: remainingCount
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { exists } from '../../fsx.js';
|
|
4
|
+
import { desktopBridgeServicePaths } from '../../codex-lb/desktop-service.js';
|
|
5
|
+
import { repairDoctorDesktopBridgeCatalog } from '../../doctor/desktop-bridge-catalog-repair.js';
|
|
6
|
+
export async function runDesktopBridgeCatalogRepairStage() {
|
|
7
|
+
const skip = (reason) => ({ ok: true, status: 'ok', actions: [reason], blockers: [], warnings: [] });
|
|
8
|
+
if (process.platform !== 'darwin')
|
|
9
|
+
return skip('desktop_bridge_catalog_repair_not_macos');
|
|
10
|
+
if (process.env.NODE_TEST_CONTEXT !== undefined)
|
|
11
|
+
return skip('desktop_bridge_catalog_repair_skipped_under_tests');
|
|
12
|
+
if (process.env.SKS_TEST_ISOLATION === '1')
|
|
13
|
+
return skip('desktop_bridge_catalog_repair_skipped_under_tests');
|
|
14
|
+
if (process.env.SKS_SKIP_BRIDGE_CATALOG_REPAIR === '1')
|
|
15
|
+
return skip('desktop_bridge_catalog_repair_disabled');
|
|
16
|
+
const home = path.resolve(process.env.HOME || os.homedir());
|
|
17
|
+
if (!(await exists(desktopBridgeServicePaths(home).settings_path))) {
|
|
18
|
+
return skip('desktop_bridge_catalog_repair_no_managed_bridge');
|
|
19
|
+
}
|
|
20
|
+
const repair = await repairDoctorDesktopBridgeCatalog({ fix: true }).catch((error) => ({
|
|
21
|
+
ok: false,
|
|
22
|
+
repaired: false,
|
|
23
|
+
warnings: [],
|
|
24
|
+
blockers: [`desktop_bridge_catalog_repair_failed:${error?.message || String(error)}`]
|
|
25
|
+
}));
|
|
26
|
+
if (repair.ok) {
|
|
27
|
+
return {
|
|
28
|
+
ok: true,
|
|
29
|
+
status: 'ok',
|
|
30
|
+
actions: [repair.repaired ? 'desktop_bridge_catalog_repaired' : 'desktop_bridge_catalog_clean'],
|
|
31
|
+
blockers: [],
|
|
32
|
+
warnings: repair.warnings || []
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
ok: true,
|
|
37
|
+
status: 'ok',
|
|
38
|
+
actions: [],
|
|
39
|
+
blockers: [],
|
|
40
|
+
warnings: [
|
|
41
|
+
...(repair.blockers || []).map((blocker) => `desktop_bridge_catalog_repair_incomplete:${blocker}`),
|
|
42
|
+
'Desktop Bridge catalog still needs repair: run `sks doctor --fix`'
|
|
43
|
+
]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -10,6 +10,8 @@ import { enforceRetention } from '../retention.js';
|
|
|
10
10
|
import { COMMANDS } from '../../cli/command-registry.js';
|
|
11
11
|
import { reconcileLegacyManagedGeneration } from '../init/legacy-generation-convergence.js';
|
|
12
12
|
import { runConfigFastModeNormalizeStage } from './update-migration-state/fast-mode-config.js';
|
|
13
|
+
import { runCurrentPublicSurfaceReconcileStage } from './update-migration-state/current-public-surface-stage.js';
|
|
14
|
+
import { runDesktopBridgeCatalogRepairStage } from './update-migration-state/desktop-bridge-catalog-repair-stage.js';
|
|
13
15
|
import { runDesktopBridgeRestageStage } from './update-migration-state/desktop-bridge-restage.js';
|
|
14
16
|
import { runSessionStateSplitStage } from './update-migration-state/session-state-split.js';
|
|
15
17
|
import { runHookTrustRefreshStage, runOtherHarnessCleanupStage } from './update-migration-state/simple-stages.js';
|
|
@@ -264,73 +266,13 @@ const UPDATE_MIGRATION_STAGES = [
|
|
|
264
266
|
id: 'desktop-bridge-restage',
|
|
265
267
|
min_from_version: '0.0.0',
|
|
266
268
|
run: runDesktopBridgeRestageStage
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: 'desktop-bridge-catalog-repair',
|
|
272
|
+
min_from_version: '0.0.0',
|
|
273
|
+
run: runDesktopBridgeCatalogRepairStage
|
|
267
274
|
}
|
|
268
275
|
];
|
|
269
|
-
async function runCurrentPublicSurfaceReconcileStage(root) {
|
|
270
|
-
const [{ runDoctorCommandAliasCleanup }, { migrateSksProfilesToPerFile }, { cleanupRetiredRemoteBridgeLaunchAgent, quarantineRetiredRemoteBridgeBindings }] = await Promise.all([
|
|
271
|
-
import('../doctor/command-alias-cleanup.js'),
|
|
272
|
-
import('../auto-review.js'),
|
|
273
|
-
import('../codex-app/menubar/migration.js')
|
|
274
|
-
]);
|
|
275
|
-
const home = path.resolve(process.env.HOME || os.homedir());
|
|
276
|
-
const globalRuntimeRoot = path.resolve(process.env.SKS_GLOBAL_ROOT || path.join(home, '.sneakoscope-global'));
|
|
277
|
-
const publicSurface = await runDoctorCommandAliasCleanup({
|
|
278
|
-
root,
|
|
279
|
-
home,
|
|
280
|
-
globalRuntimeRoot,
|
|
281
|
-
fix: true,
|
|
282
|
-
managedGenerationAlreadyConverged: true
|
|
283
|
-
});
|
|
284
|
-
const profileMigration = await migrateSksProfilesToPerFile({ env: process.env }).catch((err) => ({
|
|
285
|
-
error: err?.message || String(err),
|
|
286
|
-
retired_profile_table_count: 0,
|
|
287
|
-
retired_profile_file_removed_count: 0
|
|
288
|
-
}));
|
|
289
|
-
const retiredLaunchAgent = await cleanupRetiredRemoteBridgeLaunchAgent({ home, env: process.env });
|
|
290
|
-
const retiredBindings = await quarantineRetiredRemoteBridgeBindings(root);
|
|
291
|
-
const remainingCount = Number(publicSurface.cleanup?.remaining_count || 0)
|
|
292
|
-
+ Number(publicSurface.cleanup?.managed_runtime?.remaining_managed_artifact_count || 0)
|
|
293
|
-
+ Number(publicSurface.cleanup?.project_guidance?.remaining_count || 0);
|
|
294
|
-
const blockers = [
|
|
295
|
-
...(publicSurface.ok === true ? [] : ['public_surface_reconcile_failed']),
|
|
296
|
-
...(retiredLaunchAgent.ok ? [] : retiredLaunchAgent.blockers),
|
|
297
|
-
...(retiredBindings.ok ? [] : retiredBindings.blockers),
|
|
298
|
-
...(profileMigration.error ? [`retired_profile_migration_failed:${profileMigration.error}`] : []),
|
|
299
|
-
...(remainingCount > 0 ? [`public_surface_remaining:${remainingCount}`] : [])
|
|
300
|
-
];
|
|
301
|
-
return {
|
|
302
|
-
ok: blockers.length === 0,
|
|
303
|
-
status: blockers.length ? 'failed' : 'ok',
|
|
304
|
-
actions: [
|
|
305
|
-
'reconciled_current_public_surface',
|
|
306
|
-
...(retiredLaunchAgent.status === 'removed' ? ['retired_remote_bridge_launch_agent_removed'] : []),
|
|
307
|
-
...(retiredBindings.status === 'quarantined' ? ['retired_remote_bridge_bindings_quarantined'] : [])
|
|
308
|
-
],
|
|
309
|
-
blockers,
|
|
310
|
-
warnings: [
|
|
311
|
-
...(publicSurface.warnings || []),
|
|
312
|
-
...retiredLaunchAgent.warnings,
|
|
313
|
-
...retiredBindings.warnings
|
|
314
|
-
],
|
|
315
|
-
detail: {
|
|
316
|
-
removed_skill_count: Number(publicSurface.cleanup?.removed_count || 0),
|
|
317
|
-
quarantined_skill_collision_count: Number(publicSurface.cleanup?.preserved_user_collision_count || 0),
|
|
318
|
-
removed_runtime_artifact_count: Number(publicSurface.cleanup?.managed_runtime?.removed_managed_artifact_count || 0),
|
|
319
|
-
quarantined_runtime_collision_count: Number(publicSurface.cleanup?.managed_runtime?.preserved_user_file_count || 0),
|
|
320
|
-
reconciled_guidance_count: Number(publicSurface.cleanup?.project_guidance?.reconciled_count || 0),
|
|
321
|
-
quarantined_guidance_collision_count: Number(publicSurface.cleanup?.project_guidance?.preserved_user_file_count || 0),
|
|
322
|
-
removed_retired_role_count: 0,
|
|
323
|
-
quarantined_retired_role_collision_count: 0,
|
|
324
|
-
retired_profile_table_count: Number(profileMigration.retired_profile_table_count || 0),
|
|
325
|
-
retired_profile_file_removed_count: Number(profileMigration.retired_profile_file_removed_count || 0),
|
|
326
|
-
retired_remote_bridge_launch_agent_status: retiredLaunchAgent.status,
|
|
327
|
-
retired_remote_bridge_binding_status: retiredBindings.status,
|
|
328
|
-
retired_remote_bridge_binding_count: retiredBindings.retired_binding_count,
|
|
329
|
-
retired_remote_bridge_binding_quarantine_path: retiredBindings.quarantine_path,
|
|
330
|
-
remaining_count: remainingCount
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
276
|
export async function runUpdateMigrationStages(root, opts = {}) {
|
|
335
277
|
const fromVersion = opts.fromVersion || null;
|
|
336
278
|
const runs = [];
|
package/dist/core/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '9.0.
|
|
1
|
+
export const PACKAGE_VERSION = '9.0.6';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "9.0.
|
|
4
|
+
"version": "9.0.6",
|
|
5
5
|
"description": "Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|