flow-projection-generator-performance.test.mjs
112 lines 3.4 KB
Raw
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 10 hours ago
1 /**
2 * Tier 6 — PERFORMANCE: projection generator p95 budget.
3 *
4 * @see docs/FLOW-PROJECTION-GENERATOR-CONTRACT-7A-11.md §9
5 */
6 import { describe, it, beforeEach, afterEach } from 'node:test';
7 import assert from 'node:assert/strict';
8 import fs from 'node:fs';
9 import path from 'node:path';
10 import { fileURLToPath } from 'node:url';
11 import {
12 projectFlow,
13 computeFidelity,
14 renderedContentHash,
15 isProjectionStale,
16 detectDrift,
17 } from '../lib/flow/projection-generator.mjs';
18 import {
19 saveFlowStore,
20 buildFlowStepId,
21 MAX_STEPS_PER_FLOW,
22 } from '../lib/flow/flow-store.mjs';
23
24 const __dirname = path.dirname(fileURLToPath(import.meta.url));
25 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-flow-projection-performance');
26
27 function percentile(values, p) {
28 const sorted = [...values].sort((a, b) => a - b);
29 const idx = Math.ceil((p / 100) * sorted.length) - 1;
30 return sorted[Math.max(0, idx)];
31 }
32
33 describe('Flow projection — performance', () => {
34 const dataDir = path.join(tmpRoot, 'data');
35 const flowId = 'flow_perf_projection';
36
37 beforeEach(() => {
38 fs.rmSync(tmpRoot, { recursive: true, force: true });
39 fs.mkdirSync(dataDir, { recursive: true });
40 const steps = [];
41 const stepIds = [];
42 for (let i = 1; i <= MAX_STEPS_PER_FLOW; i += 1) {
43 const stepId = buildFlowStepId(flowId, i);
44 stepIds.push(stepId);
45 steps.push({
46 schema: 'knowtation.flow_step/v0',
47 step_id: stepId,
48 flow_id: flowId,
49 ordinal: i,
50 owned_job: `job ${i}`,
51 instruction: `instruction ${i}`,
52 trigger: `trigger ${i}`,
53 when_not_to_run: `skip ${i}`,
54 boundaries: [`b${i}`],
55 output_shape: `shape ${i}`,
56 verification: { kind: 'human_review', evidence_required: false, description: `d${i}` },
57 automatable: 'manual',
58 });
59 }
60 saveFlowStore(dataDir, {
61 vaults: {
62 default: {
63 flows: [
64 {
65 schema: 'knowtation.flow/v0',
66 flow_id: flowId,
67 title: 'Perf',
68 version: '1.0.0',
69 scope: 'personal',
70 summary: 'perf',
71 tags: [],
72 steps: stepIds,
73 updated: '2026-06-20T00:00:00Z',
74 truncated: false,
75 },
76 ],
77 steps,
78 runs: [],
79 candidates: [],
80 projections: [],
81 },
82 },
83 });
84 });
85
86 afterEach(() => {
87 fs.rmSync(tmpRoot, { recursive: true, force: true });
88 });
89
90 it('projectFlow + fidelity + hash complete within p95 budget on large fixture', () => {
91 const store = JSON.parse(
92 fs.readFileSync(path.join(dataDir, 'hub_flow_store.json'), 'utf8'),
93 );
94 const flow = store.vaults.default.flows[0];
95 const steps = store.vaults.default.steps;
96 const times = [];
97 let rendered = '';
98 for (let i = 0; i < 50; i += 1) {
99 const t0 = performance.now();
100 const projection = projectFlow(flow, steps, { harness: 'cli_runbook' });
101 computeFidelity('cli_runbook', flow, steps);
102 renderedContentHash(projection.rendered);
103 isProjectionStale('0.1.0', '0.2.0');
104 detectDrift(projection.rendered, projection.rendered);
105 times.push(performance.now() - t0);
106 rendered = projection.rendered;
107 }
108 const p95 = percentile(times, 95);
109 assert.ok(p95 < 250, `p95 ${p95}ms exceeds budget`);
110 assert.ok(rendered.length > 0);
111 });
112 });
File History 1 commit
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 10 hours ago