ms-ai-architect/scripts/kb-update/run-detection.mjs
Kjell Tore Guttormsen 150b20584e feat(ms-ai-architect): Sesjon 21 — C1 Tier 1 opt-in session-forankret deteksjon (ToS-forankret) [skip-docs]
Spor C fase C1 Tier 1. Ny verifisert ToS-analyse avdekket falsk «enten/eller»:
Consumer Terms §3.7 begrenser automatisert tilgang til Claude/Anthropic, IKKE
kjøring av lokale node-scripts. Deteksjon kontakter aldri Claude → utenfor
ToS-flaten; apply (eneste Claude-steg) forblir manuelt/in-session/gated.

- lib/detection-schedule.mjs (ren): opt-in config (default AV) + shouldRunDetection
  gate + DETECTION_STEPS allow-liste + summarizeSkillLifecycle. Zero-dep parser.
- run-detection.mjs: Claude-FRITT entrypoint — kjører kun `node` på de
  allow-listede deteksjons-scriptene; kan ikke invokere claude (guard-testet).
- session-start-context.mjs: ubetinget bakgrunns-spawn → opt-in (default AV
  spawner ingenting) + surfacer skill-signaler read-only.
- commands/kb-update.md: opt-in-seksjon + ToS-note; ms-ai-architect.local.md.example.

TDD: ny test-detection-schedule.test.mjs (16). kb-update 139→155.
Suiter uendret: validate 239 · kb-eval 100 · kb-integrity 192/192.
0 skills/-mutasjon. Tier 2 (lokal OS-timer, deteksjon-only) = neste økt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:54:18 +02:00

53 lines
2.6 KiB
JavaScript

#!/usr/bin/env node
// run-detection.mjs — Spor C / C1: the Claude-FREE detection entrypoint.
//
// Runs the pure-node detection pipeline (poll → report → discover →
// detect-skill-lifecycle), each a local script that produces a JSON report and
// NEVER contacts Anthropic. This is the structural ToS guarantee: this file
// only ever spawns `node` on the allow-listed DETECTION_STEPS — it can never
// invoke `claude`, and it never touches the apply path (which alone invokes
// Claude and stays manual + in-session + gated).
//
// The SessionStart hook background-spawns this when the opt-in schedule is
// enabled + stale (see detection-schedule.mjs). Standalone use is fine too:
// node scripts/kb-update/run-detection.mjs # run the pipeline
// node scripts/kb-update/run-detection.mjs --dry-run # list steps, run none
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { execFileSync } from 'node:child_process';
import { DETECTION_STEPS, loadScheduleConfig } from './lib/detection-schedule.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
const PLUGIN_ROOT = join(__dirname, '..', '..');
const dryRun = process.argv.includes('--dry-run');
// Honor the opt-in content choice: skill-lifecycle detection runs unless the
// config turns it off. (enabled-gating is the hook's job; invoking this script
// directly is itself the intent to run.)
const config = loadScheduleConfig(PLUGIN_ROOT);
const steps = DETECTION_STEPS.filter((s) => !s.skillLifecycle || config.include_skill_lifecycle);
function run(step) {
const fullPath = join(PLUGIN_ROOT, 'scripts', step.dir, step.script);
console.log(`\n--- detection: ${step.name} (${step.dir}/${step.script} ${step.args.join(' ')}) ---`);
try {
// Only ever `node` on a local detection script — never `claude`.
execFileSync('node', [fullPath, ...step.args], { stdio: 'inherit', timeout: 10 * 60 * 1000 });
} catch (err) {
// A failing step is non-fatal for the others — detection is advisory.
console.error(`detection step ${step.name} failed: ${err.message}`);
}
}
if (dryRun) {
console.log('DRY RUN — Claude-free detection pipeline would execute:');
steps.forEach((s, i) => console.log(` ${i + 1}. ${s.dir}/${s.script} ${s.args.join(' ')}`));
console.log('\n(All steps are pure node; none invoke Claude. Apply stays manual + in-session.)');
process.exit(0);
}
console.log('=== Claude-free detection run ===');
for (const step of steps) run(step);
console.log('\n=== detection complete (reports refreshed; 0 writes to skills/) ===');