#!/usr/bin/env node /** * cli:derive-sensitivity-specs — entry point. * * THE deterministic deriver of the data-sensitivity seam (lib/ba-sensitivity): * resolves each entity's `- **Sensibilité**` level, emits the scaffolder * fragments (scaffold-business / scaffold-component / scaffold-extension- * search) and reconciles the pagespec `"sensitivity"` key (single source of * truth = entité.md — additive AND stale-removing, idempotent; ABSENT when * `interne`, the no-op guarantee). * * Invocation: * npx --prefer-offline tsx skills/ba-develop/cli/derive-sensitivity-specs/index.ts \ * --spec '{"moduleRoot":".smartstack/ba//"}' * * `mode:"check"` reports the parity without writing — drift is DATA (exit 0). * A `blocked` entity (no neutral label / a sensitive label) is BLOCKING in * `derive` (exit 1): the BA fixes entité.md, nothing is improvised here. */ import { parseArgs } from 'node:util' import { validate } from './validate.js' import { deriveSensitivitySpecs } from './derive.js' import { executeEnvelope, failExecute, printEnvelope } from '../../../lib/output.js' import type { DeriveSensitivitySpecsReport } from './types.js' const COMMAND = 'derive-sensitivity-specs' function main(): void { const { values } = parseArgs({ options: { spec: { type: 'string' } }, strict: true }) if (!values.spec) { printEnvelope(failExecute(COMMAND, ['--spec is required'])) process.exit(1) } let raw: unknown try { raw = JSON.parse(values.spec) } catch { printEnvelope(failExecute(COMMAND, ['Invalid JSON in --spec'])) process.exit(1) } const validation = validate(raw) if (!validation.valid || !validation.spec) { printEnvelope(failExecute(COMMAND, validation.errors)) process.exit(1) } const report = deriveSensitivitySpecs(validation.spec) const nextSteps: string[] = [ 'Phase 2: pass each entities[].scaffoldBusiness.sensitivity to scaffold-business (list search restricted to the display field) and each entities[].extensionSearch to scaffold-extension-search build-spec (`.SearchIn(display)`, restreint excluded) — never re-derive by hand.', 'Phase 3a: scaffold-component reads the pagespec `sensitivity` key this CLI stamped (badge + i18n floor); nothing to pass by hand.', 'Gates: DEV-API-035 (search surfaces of a sensitive entity), DEV-XAPI-015 (public API × sensitivity).', ] if (report.totals.blocked > 0) { nextSteps.unshift( `BLOCKING — ${report.totals.blocked} sensitive entit${report.totals.blocked === 1 ? 'y has' : 'ies have'} no neutral label ` + '(no **Affichage**, no display-family attribute, or a label that IS a sensitive attribute): fix entité.md first — ' + 'the lookup and the search have nothing safe to target, and scaffold-business fails closed on the same shape.', ) } const envelope = executeEnvelope(COMMAND, { success: true, data: report.totals, report, warnings: report.warnings, nextSteps, }) if (validation.spec.mode === 'derive' && report.totals.blocked > 0) { // Controlled refusal: the report travels (the BA reads WHICH entity and why), the envelope says no. printEnvelope({ ...envelope, success: false, errors: report.entities.flatMap((e) => e.blocked.map((b) => `${e.entity}: ${b}`)) }) process.exit(1) } printEnvelope(envelope) process.exit(0) } main()