BID · Console
Baseline · Intelligence · Decision
scripts/decision-verify.ts 9,751 bytes · typescript
/**
 * decision-verify — free verification of the Pillar 3 Rule Library,
 * the rule-tool surface, the channel registry, and every Decision
 * agent's contract. No Anthropic spend.
 *
 *   npm run decision:verify
 *
 * Mirrors scripts/intel-verify.ts. Anything green here is a structural
 * green light for the paid pipeline; anything red catches issues
 * deterministically before a paid run.
 */

import {
  loadLibrary,
  findRules,
  getRule,
  resolvePrecedence,
  type Rule,
} from '../src/decision/library.js';
import { executeRuleTool, RULE_TOOLS } from '../src/decision/tools.js';
import {
  listChannels,
  getChannel,
  registerChannel,
  type Channel,
  type DispatchPayload,
} from '../src/decision/channel-registry.js';
import { outputIngestionContract } from '../src/agents/decision/output-ingestion/matrix.js';
import { visualizationContract } from '../src/agents/decision/visualization/matrix.js';
import { deliveryDistributionContract } from '../src/agents/decision/delivery-distribution/matrix.js';

function banner(s: string): void {
  console.log(`\n══════════════════════════════════════════════════════════`);
  console.log(s);
  console.log(`══════════════════════════════════════════════════════════`);
}

function row(label: string, value: unknown): void {
  console.log(`  ${label.padEnd(22)} ${value}`);
}

function summarise(r: Rule): string {
  return `${r.rule_id.padEnd(46)} type=${r.type.padEnd(15)} domain=${r.domain.padEnd(12)} agent=${r.applies_to.agent.padEnd(18)} status=${r.status.padEnd(10)} (${r.sourceFile})`;
}

async function main(): Promise<void> {
  const t0 = Date.now();

  /* ----------------- 1. Library load ----------------- */
  banner('1. Library load (YAML scan + index)');
  const all = await loadLibrary();
  row('entries loaded:', all.length);
  for (const r of all) console.log(`  - ${summarise(r)}`);

  /* ----------------- 2. Full content of one rule ----------------- */
  banner('2. Full content of peer_positioning_recommendation');
  const ppr = await getRule('peer_positioning_recommendation');
  if (!ppr) {
    console.log('  (missing — expected to be present)');
  } else {
    row('name:', ppr.name);
    row('type:', ppr.type);
    row('domain:', ppr.domain);
    row('agent:', ppr.applies_to.agent);
    row('triggers:', ppr.applies_to.triggers.length);
    for (const t of ppr.applies_to.triggers) console.log(`                         • ${t}`);
    row('conditions:', ppr.conditions.length);
    row('action keys:', Object.keys(ppr.action).join(', '));
    row('confidence:', JSON.stringify(ppr.confidence_framework));
    row('disclosure:', JSON.stringify(ppr.disclosure_policy ?? null));
    row('status:', ppr.status);
    row('source file:', ppr.sourceFile);
  }

  /* ----------------- 3. Sample queries ----------------- */
  banner('3. Sample queries (library.ts API)');
  const queries: { label: string; query: Parameters<typeof findRules>[0] }[] = [
    { label: 'type=interpretation',                          query: { type: 'interpretation' } },
    { label: 'type=visualization',                           query: { type: 'visualization' } },
    { label: 'type=delivery',                                query: { type: 'delivery' } },
    { label: 'domain=banking',                               query: { domain: 'banking' } },
    { label: 'domain=insurance (no entries yet)',            query: { domain: 'insurance' } },
    { label: 'agent=output_ingestion',                       query: { agent: 'output_ingestion' } },
    { label: 'agent=visualization',                          query: { agent: 'visualization' } },
    { label: 'agent=delivery',                               query: { agent: 'delivery' } },
    { label: 'triggers=["peer_positioning"]',                query: { triggers: ['peer_positioning'] } },
    { label: 'triggers=["decision_maker"]',                  query: { triggers: ['decision_maker'] } },
    { label: 'triggers=["material"]',                        query: { triggers: ['material'] } },
    { label: 'triggers=["nonsense"] (should be empty)',      query: { triggers: ['nonsense'] } },
  ];
  for (const q of queries) {
    const hits = await findRules(q.query);
    console.log(`  ${q.label.padEnd(48)} → ${hits.length} hit(s): ${hits.map(h => h.rule_id).join(', ') || '—'}`);
  }

  /* ----------------- 4. Precedence resolution ----------------- */
  banner('4. Precedence resolution');
  const allActive = await findRules({});
  const ordered = resolvePrecedence(allActive);
  for (const r of ordered) {
    console.log(`  ${r.rule_id.padEnd(46)} domain=${r.domain.padEnd(8)} triggers=${r.applies_to.triggers.length} declared=${r.declared_date}`);
  }

  /* ----------------- 5. Anthropic tool descriptors ----------------- */
  banner('5. Anthropic tool descriptors (what Decision agents declare to Claude)');
  for (const t of RULE_TOOLS) {
    console.log(`  - ${t.name}`);
    console.log(`      ${t.description.replace(/\s+/g, ' ').slice(0, 110)}…`);
    console.log(`      required: [${t.input_schema.required.join(', ')}]  properties: [${Object.keys(t.input_schema.properties).join(', ')}]`);
  }

  /* ----------------- 6. Execute tools ----------------- */
  banner('6. executeRuleTool — exercises the dispatcher');

  const callA = await executeRuleTool('find_rules', { type: 'interpretation' });
  console.log(`  find_rules({type:"interpretation"}) → ok=${callA.ok}, ${(callA.result as unknown[] | undefined)?.length ?? 0} hit(s)`);

  const callB = await executeRuleTool('find_rules', { triggers: 'decision_maker, peer_positioning' });
  console.log(`  find_rules({triggers:"decision_maker, peer_positioning"}) → ok=${callB.ok}, ${(callB.result as unknown[] | undefined)?.length ?? 0} hit(s)`);

  const callC = await executeRuleTool('get_rule', { rule_id: 'material_decision_maker_alert_delivery' });
  const rule = callC.result as Rule | undefined;
  console.log(`  get_rule("material_decision_maker_alert_delivery") → ok=${callC.ok} name="${rule?.name ?? ''}"`);

  const callD = await executeRuleTool('get_rule', { rule_id: 'does_not_exist' });
  console.log(`  get_rule("does_not_exist") → ok=${callD.ok} error=${callD.error?.category ?? '?'}: ${callD.error?.message ?? ''}`);

  const callE = await executeRuleTool('get_rule', {});
  console.log(`  get_rule({}) → ok=${callE.ok} error=${callE.error?.category ?? '?'}: ${callE.error?.message ?? ''}`);

  const callF = await executeRuleTool('unknown_tool', {});
  console.log(`  unknown_tool({}) → ok=${callF.ok} error=${callF.error?.category ?? '?'}: ${callF.error?.message ?? ''}`);

  /* ----------------- 7. Per-agent reachability ----------------- */
  banner('7. Which rules each Decision agent can reach today');
  const agents = ['output_ingestion', 'visualization', 'delivery'] as const;
  for (const a of agents) {
    const hits = await findRules({ agent: a });
    console.log(`  ${a.padEnd(20)} → ${hits.length} reachable rule(s): ${hits.map(h => h.rule_id).join(', ') || '—'}`);
  }
  console.log(`  (any agent whose row is "—" will escalate every relevant request as a rule-gap)`);

  /* ----------------- 8. Channel registry ----------------- */
  banner('8. Channel registry');
  const channels = listChannels();
  row('registered:', channels.join(', '));
  const auditLog = getChannel('audit-log');
  if (auditLog) {
    row('audit-log desc:', auditLog.description);
    /* Exercise the channel surface — dispatch one payload and read the
     *  outcome. No external side effect (audit-log is no-op). */
    const outcome = await auditLog.dispatch({
      recipient: 'audience-tier:decision_maker',
      audienceTier: 'decision_maker',
      content: { kind: 'verification', data: { hello: 'world' } },
      contentReference: 'decision-verify-smoke',
      severity: 'normal',
      acknowledgmentRequired: true,
      acknowledgmentWindowSec: 60,
    });
    row('test dispatch:', `ok=${outcome.ok} ack=${outcome.acknowledgmentState} channel=${outcome.channel}`);
  }
  /* Register-then-unregister roundtrip (verifies the API surface). */
  const fake: Channel = {
    name: '__verify_probe__',
    description: 'temporary probe for decision-verify',
    async dispatch(_p: DispatchPayload) {
      return {
        ok: true,
        channel: this.name,
        dispatchedAt: new Date().toISOString(),
        acknowledgmentRequired: false,
        acknowledgmentState: 'not-applicable' as const,
      };
    },
  };
  registerChannel(fake);
  const probe = getChannel('__verify_probe__');
  row('register/get probe:', probe ? 'ok' : 'FAILED');
  /* Leave probe registered — harmless and proves the API roundtrip;
   * the audit-log default remains the rule-named channel. */

  /* ----------------- 9. Agent contracts ----------------- */
  banner('9. Pillar 3 agent contracts');
  for (const c of [outputIngestionContract, visualizationContract, deliveryDistributionContract]) {
    console.log(`  ${c.agentName.padEnd(34)} v${c.agentVersion}`);
    console.log(`    pillar:       ${c.pillar}`);
    console.log(`    capabilities: ${c.capabilities.join(', ')}`);
    console.log(`    runbook:      ${c.runbook.length} step(s)`);
    console.log(`    triggers:     ${c.triggers.join(', ')}`);
    console.log(`    forbidden:    ${c.rules.pillarSpecificForbidden.join(', ')}`);
  }

  console.log(`\nDone. Elapsed: ${((Date.now() - t0) / 1000).toFixed(2)}s. $0 spent.`);
}

main().catch(err => {
  console.error('decision-verify failed:', err);
  process.exit(1);
});