@xtalpi/agentic-lab-skills 0.0.8 → 0.0.10
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/package.json +1 -1
- package/skills/lab-flow-designer/SKILL.md +46 -34
- package/skills/lab-flow-designer/embedded-template/SKILL.md +16 -1
- package/skills/lab-flow-designer/embedded-template/pools//345/205/245/345/217/243/346/261/240.md +9 -0
- package/skills/lab-flow-designer/embedded-template/pools//345/207/272/345/217/243/346/261/240.md +9 -0
- package/skills/lab-flow-designer/embedded-template/valves//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.md +24 -9
- package/skills/lab-flow-designer/references/agentic-lab-processer.md +56 -36
- package/skills/lab-flow-designer/references/agentic-lab-sdk.md +88 -0
- package/skills/lab-flow-designer/references/skill-package-layout.md +70 -6
- package/skills/lab-flow-designer/references//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/240/207/345/207/206.md +20 -12
- package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/250/241/346/235/277.md +24 -1
- package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/347/244/272/344/276/213.md +10 -0
- package/skills/lab-flow-designer/testing/test-processer.mjs +330 -165
|
@@ -356,6 +356,34 @@ function validateCompleteResult(result) {
|
|
|
356
356
|
return errors;
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
function validateRunResult(result) {
|
|
360
|
+
const errors = [];
|
|
361
|
+
if (typeof result !== 'object' || result === null) {
|
|
362
|
+
errors.push('run() must return an object, got ' + (result === null ? 'null' : typeof result));
|
|
363
|
+
return errors;
|
|
364
|
+
}
|
|
365
|
+
if (result.new_tickets !== undefined) {
|
|
366
|
+
if (!Array.isArray(result.new_tickets)) {
|
|
367
|
+
errors.push(`run().new_tickets must be array, got ${typeof result.new_tickets}`);
|
|
368
|
+
} else {
|
|
369
|
+
for (let i = 0; i < Math.min(result.new_tickets.length, 3); i++) {
|
|
370
|
+
const t = result.new_tickets[i];
|
|
371
|
+
if (typeof t.flow_id !== 'number') errors.push(`new_tickets[${i}].flow_id must be number, got ${typeof t.flow_id}`);
|
|
372
|
+
if (typeof t.pool_id !== 'number') errors.push(`new_tickets[${i}].pool_id must be number, got ${typeof t.pool_id}`);
|
|
373
|
+
if (typeof t.order_id !== 'number') errors.push(`new_tickets[${i}].order_id must be number, got ${typeof t.order_id}`);
|
|
374
|
+
if (typeof t.detail !== 'object' || t.detail === null) errors.push(`new_tickets[${i}].detail must be object`);
|
|
375
|
+
if (typeof t.status !== 'string') errors.push(`new_tickets[${i}].status must be string, got ${typeof t.status}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
for (const key of Object.keys(result)) {
|
|
380
|
+
if (CAMEL_CASE_PATTERN.test(key)) {
|
|
381
|
+
errors.push(`run() return key "${key}" is camelCase — must be snake_case`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return errors;
|
|
385
|
+
}
|
|
386
|
+
|
|
359
387
|
// ─── Script loader via vm ──────────────────────────────────────────
|
|
360
388
|
|
|
361
389
|
function loadProcesserClass(scriptContent, filename) {
|
|
@@ -621,8 +649,10 @@ function formatTicketsTable(tickets, maxDetailKeys) {
|
|
|
621
649
|
|
|
622
650
|
function formatPreviewReport(trace) {
|
|
623
651
|
const { scriptPath, valveName, valveConfig, pools, dataSource,
|
|
624
|
-
inputTickets,
|
|
625
|
-
|
|
652
|
+
inputTickets, isAutoTrigger,
|
|
653
|
+
startResult, startError, startTraceLog, startDetailDiffs,
|
|
654
|
+
completeTickets, completeResult, completeError, completeTraceLog, completeDetailDiffs,
|
|
655
|
+
runResult, runError, runTraceLog, runDetailDiffs } = trace;
|
|
626
656
|
|
|
627
657
|
const lines = [];
|
|
628
658
|
const push = (...args) => lines.push(...args);
|
|
@@ -638,108 +668,176 @@ function formatPreviewReport(trace) {
|
|
|
638
668
|
push(`- 入口池: ${inputPoolNames}`);
|
|
639
669
|
push(`- 出口池: ${outputPoolNames}`);
|
|
640
670
|
push(`- 数据来源: ${dataSource}`);
|
|
641
|
-
push(`- tickets 数: ${inputTickets.length}
|
|
671
|
+
push(`- tickets 数: ${inputTickets.length}`);
|
|
672
|
+
push(`- 触发方式: ${isAutoTrigger ? '自动触发 (run)' : '人工触发 (start/complete)'}`, '');
|
|
673
|
+
|
|
674
|
+
if (isAutoTrigger) {
|
|
675
|
+
// run() trace
|
|
676
|
+
push('## run() 执行追踪', '');
|
|
677
|
+
push(`### 输入数据 (${inputTickets.length} 条 tickets)`, '');
|
|
678
|
+
push(formatTicketsTable(inputTickets));
|
|
679
|
+
|
|
680
|
+
push('### SDK 调用链', '');
|
|
681
|
+
for (const entry of (runTraceLog || [])) {
|
|
682
|
+
const paramStr = JSON.stringify(entry.params);
|
|
683
|
+
push(`${entry.seq + 1}. \`${entry.method}(${paramStr === '{}' ? '' : paramStr})\` → ${entry.returnSummary}`);
|
|
684
|
+
}
|
|
685
|
+
push('');
|
|
642
686
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
687
|
+
if (runDetailDiffs && runDetailDiffs.length > 0) {
|
|
688
|
+
push('### ticket.detail 变更 (run 阶段)', '');
|
|
689
|
+
push('| ticket | 字段 | 变更前 | 变更后 |');
|
|
690
|
+
push('|--------|------|--------|--------|');
|
|
691
|
+
for (const d of runDetailDiffs) {
|
|
692
|
+
for (const c of d.changes) {
|
|
693
|
+
push(`| #${d.ticketId} | ${c.key} | ${c.before} | ${c.after} |`);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
push('');
|
|
697
|
+
}
|
|
647
698
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
push(
|
|
699
|
+
if (runError) {
|
|
700
|
+
push(`### run() 错误`, '');
|
|
701
|
+
push(`\`\`\`\n${runError}\n\`\`\``, '');
|
|
702
|
+
} else if (runResult) {
|
|
703
|
+
push('### 数据路由', '');
|
|
704
|
+
if (runResult.new_tickets && runResult.new_tickets.length > 0) {
|
|
705
|
+
const routeMap = new Map();
|
|
706
|
+
for (const nt of runResult.new_tickets) {
|
|
707
|
+
const poolId = nt.pool_id;
|
|
708
|
+
if (!routeMap.has(poolId)) routeMap.set(poolId, []);
|
|
709
|
+
routeMap.get(poolId).push(nt);
|
|
710
|
+
}
|
|
711
|
+
push('| 目标池 | pool_id | 数量 | status |');
|
|
712
|
+
push('|--------|---------|------|--------|');
|
|
713
|
+
for (const [poolId, nts] of routeMap) {
|
|
714
|
+
const poolName = valveConfig.output_pools.find((_, i) => 100 + i === poolId)?.name || `pool_${poolId}`;
|
|
715
|
+
push(`| ${poolName} | ${poolId} | ${nts.length} | ${nts[0]?.status || '-'} |`);
|
|
716
|
+
}
|
|
717
|
+
push('');
|
|
718
|
+
} else {
|
|
719
|
+
push('(无 new_tickets 返回)', '');
|
|
662
720
|
}
|
|
663
721
|
}
|
|
664
|
-
push('');
|
|
665
|
-
}
|
|
666
722
|
|
|
667
|
-
|
|
668
|
-
push(
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
push(`- ticket_ids: [${(startResult.ticket_ids || []).join(', ')}]`);
|
|
674
|
-
push('');
|
|
675
|
-
}
|
|
723
|
+
// Summary
|
|
724
|
+
push('## 执行摘要', '');
|
|
725
|
+
const usedMethods = new Set((runTraceLog || []).map(c => c.method));
|
|
726
|
+
const allMethods = ['ticket.list', 'ticket.update', 'ticket.append', 'pool.getNext',
|
|
727
|
+
'compound.getStockFromXfcSh', 'process.list', 'process.execute', 'station.list'];
|
|
728
|
+
const unused = allMethods.filter(m => !usedMethods.has(m));
|
|
676
729
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
730
|
+
const runStatus = runError
|
|
731
|
+
? `✗ 失败: ${runError.split('\n')[0]}`
|
|
732
|
+
: `✓ 成功 (${(runTraceLog || []).length} SDK 调用, ${(runResult?.new_tickets || []).length} new_tickets)`;
|
|
680
733
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
push(
|
|
690
|
-
push(
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
}
|
|
734
|
+
push(`- run: ${runStatus}`);
|
|
735
|
+
if (unused.length > 0) {
|
|
736
|
+
push(`- 未调用的 SDK 方法: ${unused.join(', ')}`);
|
|
737
|
+
}
|
|
738
|
+
push('');
|
|
739
|
+
} else {
|
|
740
|
+
// start() trace
|
|
741
|
+
push('## start() 执行追踪', '');
|
|
742
|
+
push(`### 输入数据 (${inputTickets.length} 条 tickets)`, '');
|
|
743
|
+
push(formatTicketsTable(inputTickets));
|
|
744
|
+
|
|
745
|
+
push('### SDK 调用链', '');
|
|
746
|
+
for (const entry of startTraceLog) {
|
|
747
|
+
const paramStr = JSON.stringify(entry.params);
|
|
748
|
+
push(`${entry.seq + 1}. \`${entry.method}(${paramStr === '{}' ? '' : paramStr})\` → ${entry.returnSummary}`);
|
|
696
749
|
}
|
|
697
750
|
push('');
|
|
698
|
-
}
|
|
699
751
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const poolId = nt.pool_id;
|
|
709
|
-
if (!routeMap.has(poolId)) routeMap.set(poolId, []);
|
|
710
|
-
routeMap.get(poolId).push(nt);
|
|
752
|
+
if (startDetailDiffs.length > 0) {
|
|
753
|
+
push('### ticket.detail 变更 (start 阶段)', '');
|
|
754
|
+
push('| ticket | 字段 | 变更前 | 变更后 |');
|
|
755
|
+
push('|--------|------|--------|--------|');
|
|
756
|
+
for (const d of startDetailDiffs) {
|
|
757
|
+
for (const c of d.changes) {
|
|
758
|
+
push(`| #${d.ticketId} | ${c.key} | ${c.before} | ${c.after} |`);
|
|
759
|
+
}
|
|
711
760
|
}
|
|
712
|
-
push('
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
761
|
+
push('');
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (startError) {
|
|
765
|
+
push(`### start() 错误`, '');
|
|
766
|
+
push(`\`\`\`\n${startError}\n\`\`\``, '');
|
|
767
|
+
} else if (startResult) {
|
|
768
|
+
push('### 返回值', '');
|
|
769
|
+
push(`- orbit_link: \`${startResult.orbit_link}\``);
|
|
770
|
+
push(`- ticket_ids: [${(startResult.ticket_ids || []).join(', ')}]`);
|
|
771
|
+
push('');
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// complete() trace
|
|
775
|
+
push('## complete() 执行追踪', '');
|
|
776
|
+
push(`### 输入 tickets (${(completeTickets || []).length} 条)`, '');
|
|
777
|
+
|
|
778
|
+
push('### SDK 调用链', '');
|
|
779
|
+
for (const entry of completeTraceLog) {
|
|
780
|
+
const paramStr = JSON.stringify(entry.params);
|
|
781
|
+
push(`${entry.seq + 1}. \`${entry.method}(${paramStr === '{}' ? '' : paramStr})\` → ${entry.returnSummary}`);
|
|
782
|
+
}
|
|
783
|
+
push('');
|
|
784
|
+
|
|
785
|
+
if (completeDetailDiffs.length > 0) {
|
|
786
|
+
push('### ticket.detail 变更 (complete 阶段)', '');
|
|
787
|
+
push('| ticket | 字段 | 变更前 | 变更后 |');
|
|
788
|
+
push('|--------|------|--------|--------|');
|
|
789
|
+
for (const d of completeDetailDiffs) {
|
|
790
|
+
for (const c of d.changes) {
|
|
791
|
+
push(`| #${d.ticketId} | ${c.key} | ${c.before} | ${c.after} |`);
|
|
792
|
+
}
|
|
717
793
|
}
|
|
718
794
|
push('');
|
|
719
|
-
} else {
|
|
720
|
-
push('(无 new_tickets 返回)', '');
|
|
721
795
|
}
|
|
722
|
-
}
|
|
723
796
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
797
|
+
if (completeError) {
|
|
798
|
+
push(`### complete() 错误`, '');
|
|
799
|
+
push(`\`\`\`\n${completeError}\n\`\`\``, '');
|
|
800
|
+
} else if (completeResult) {
|
|
801
|
+
push('### 数据路由', '');
|
|
802
|
+
if (completeResult.new_tickets && completeResult.new_tickets.length > 0) {
|
|
803
|
+
const routeMap = new Map();
|
|
804
|
+
for (const nt of completeResult.new_tickets) {
|
|
805
|
+
const poolId = nt.pool_id;
|
|
806
|
+
if (!routeMap.has(poolId)) routeMap.set(poolId, []);
|
|
807
|
+
routeMap.get(poolId).push(nt);
|
|
808
|
+
}
|
|
809
|
+
push('| 目标池 | pool_id | 数量 | status |');
|
|
810
|
+
push('|--------|---------|------|--------|');
|
|
811
|
+
for (const [poolId, nts] of routeMap) {
|
|
812
|
+
const poolName = valveConfig.output_pools.find((_, i) => 100 + i === poolId)?.name || `pool_${poolId}`;
|
|
813
|
+
push(`| ${poolName} | ${poolId} | ${nts.length} | ${nts[0]?.status || '-'} |`);
|
|
814
|
+
}
|
|
815
|
+
push('');
|
|
816
|
+
} else {
|
|
817
|
+
push('(无 new_tickets 返回)', '');
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Summary
|
|
822
|
+
push('## 执行摘要', '');
|
|
823
|
+
const allCalls = [...startTraceLog, ...completeTraceLog];
|
|
824
|
+
const usedMethods = new Set(allCalls.map(c => c.method));
|
|
825
|
+
const allMethods = ['ticket.list', 'ticket.update', 'ticket.append', 'pool.getNext',
|
|
826
|
+
'compound.getStockFromXfcSh', 'process.list', 'process.execute', 'station.list'];
|
|
827
|
+
const unused = allMethods.filter(m => !usedMethods.has(m));
|
|
828
|
+
|
|
829
|
+
const startStatus = startError ? `✗ 失败: ${startError.split('\n')[0]}` : `✓ 成功 (${startTraceLog.length} SDK 调用, ${inputTickets.length} tickets)`;
|
|
830
|
+
const completeStatus = completeError
|
|
831
|
+
? `✗ 失败: ${completeError.split('\n')[0]}`
|
|
832
|
+
: `✓ 成功 (${completeTraceLog.length} SDK 调用, ${(completeResult?.new_tickets || []).length} new_tickets)`;
|
|
833
|
+
|
|
834
|
+
push(`- start: ${startStatus}`);
|
|
835
|
+
push(`- complete: ${completeStatus}`);
|
|
836
|
+
if (unused.length > 0) {
|
|
837
|
+
push(`- 未调用的 SDK 方法: ${unused.join(', ')}`);
|
|
838
|
+
}
|
|
839
|
+
push('');
|
|
741
840
|
}
|
|
742
|
-
push('');
|
|
743
841
|
|
|
744
842
|
return lines.join('\n');
|
|
745
843
|
}
|
|
@@ -787,59 +885,85 @@ async function runPreview(args) {
|
|
|
787
885
|
}
|
|
788
886
|
|
|
789
887
|
const inputPoolIds = valveConfig.input_pools.map((_, i) => i + 1);
|
|
888
|
+
const proto = ProcesserClass.prototype;
|
|
889
|
+
const isAutoTrigger = typeof proto.run === 'function' && typeof proto.start !== 'function';
|
|
890
|
+
|
|
790
891
|
const trace = {
|
|
791
|
-
scriptPath, valveName, valveConfig, pools, dataSource, inputTickets,
|
|
892
|
+
scriptPath, valveName, valveConfig, pools, dataSource, inputTickets, isAutoTrigger,
|
|
792
893
|
startResult: null, startError: null, startTraceLog: [], startDetailDiffs: [],
|
|
793
894
|
completeTickets: null, completeResult: null, completeError: null, completeTraceLog: [], completeDetailDiffs: [],
|
|
895
|
+
runResult: null, runError: null, runTraceLog: [], runDetailDiffs: [],
|
|
794
896
|
};
|
|
795
897
|
|
|
796
|
-
|
|
797
|
-
|
|
898
|
+
if (isAutoTrigger) {
|
|
899
|
+
// Auto-trigger: run()
|
|
798
900
|
const tc = createTracingContext(pools, valveConfig, inputTickets);
|
|
799
901
|
let processer;
|
|
800
902
|
try {
|
|
801
903
|
processer = new ProcesserClass(tc.context);
|
|
802
904
|
} catch (e) {
|
|
803
|
-
trace.
|
|
905
|
+
trace.runError = `constructor threw: ${e.message}\n${e.stack}`;
|
|
804
906
|
console.log(formatPreviewReport(trace));
|
|
805
907
|
process.exit(1);
|
|
806
908
|
}
|
|
807
909
|
|
|
808
910
|
try {
|
|
809
|
-
trace.
|
|
911
|
+
trace.runResult = await processer.run({ valve_id: 1, pool_ids: inputPoolIds });
|
|
810
912
|
} catch (e) {
|
|
811
|
-
trace.
|
|
913
|
+
trace.runError = `${e.constructor.name}: ${e.message}\n${e.stack}`;
|
|
812
914
|
}
|
|
813
|
-
trace.
|
|
814
|
-
trace.
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
915
|
+
trace.runTraceLog = tc.traceLog;
|
|
916
|
+
trace.runDetailDiffs = tc.detailDiffs;
|
|
917
|
+
} else {
|
|
918
|
+
// Manual-trigger: start() + complete()
|
|
919
|
+
// Run start()
|
|
920
|
+
{
|
|
921
|
+
const tc = createTracingContext(pools, valveConfig, inputTickets);
|
|
922
|
+
let processer;
|
|
923
|
+
try {
|
|
924
|
+
processer = new ProcesserClass(tc.context);
|
|
925
|
+
} catch (e) {
|
|
926
|
+
trace.startError = `constructor threw: ${e.message}\n${e.stack}`;
|
|
927
|
+
console.log(formatPreviewReport(trace));
|
|
928
|
+
process.exit(1);
|
|
929
|
+
}
|
|
821
930
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
trace.
|
|
828
|
-
|
|
829
|
-
process.exit(1);
|
|
931
|
+
try {
|
|
932
|
+
trace.startResult = await processer.start({ valve_id: 1, pool_ids: inputPoolIds });
|
|
933
|
+
} catch (e) {
|
|
934
|
+
trace.startError = `${e.constructor.name}: ${e.message}\n${e.stack}`;
|
|
935
|
+
}
|
|
936
|
+
trace.startTraceLog = tc.traceLog;
|
|
937
|
+
trace.startDetailDiffs = tc.detailDiffs;
|
|
830
938
|
}
|
|
831
939
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
trace.
|
|
940
|
+
// Run complete()
|
|
941
|
+
{
|
|
942
|
+
const completeTickets = inputTickets.map(t => ({ ...t, status: 'created' }));
|
|
943
|
+
trace.completeTickets = completeTickets;
|
|
944
|
+
|
|
945
|
+
const tc = createTracingContext(pools, valveConfig, completeTickets);
|
|
946
|
+
let processer;
|
|
947
|
+
try {
|
|
948
|
+
processer = new ProcesserClass(tc.context);
|
|
949
|
+
} catch (e) {
|
|
950
|
+
trace.completeError = `constructor threw: ${e.message}\n${e.stack}`;
|
|
951
|
+
console.log(formatPreviewReport(trace));
|
|
952
|
+
process.exit(1);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
try {
|
|
956
|
+
trace.completeResult = await processer.complete({ valve_id: 1, tickets: completeTickets });
|
|
957
|
+
} catch (e) {
|
|
958
|
+
trace.completeError = `${e.constructor.name}: ${e.message}\n${e.stack}`;
|
|
959
|
+
}
|
|
960
|
+
trace.completeTraceLog = tc.traceLog;
|
|
961
|
+
trace.completeDetailDiffs = tc.detailDiffs;
|
|
836
962
|
}
|
|
837
|
-
trace.completeTraceLog = tc.traceLog;
|
|
838
|
-
trace.completeDetailDiffs = tc.detailDiffs;
|
|
839
963
|
}
|
|
840
964
|
|
|
841
965
|
console.log(formatPreviewReport(trace));
|
|
842
|
-
const hasError = trace.startError || trace.completeError;
|
|
966
|
+
const hasError = isAutoTrigger ? trace.runError : (trace.startError || trace.completeError);
|
|
843
967
|
process.exit(hasError ? 1 : 0);
|
|
844
968
|
}
|
|
845
969
|
|
|
@@ -856,7 +980,7 @@ async function runTests(args) {
|
|
|
856
980
|
script: scriptPath,
|
|
857
981
|
phases: {
|
|
858
982
|
syntax: { pass: false },
|
|
859
|
-
structure: { pass: false, hasConstructor: false, hasStart: false, hasComplete: false },
|
|
983
|
+
structure: { pass: false, hasConstructor: false, hasStart: false, hasComplete: false, hasRun: false },
|
|
860
984
|
normalRun: {
|
|
861
985
|
start: { pass: false, returnValid: false, errors: [] },
|
|
862
986
|
complete: { pass: false, returnValid: false, errors: [] },
|
|
@@ -914,18 +1038,23 @@ async function runTests(args) {
|
|
|
914
1038
|
result.phases.structure.hasConstructor = typeof ProcesserClass === 'function';
|
|
915
1039
|
result.phases.structure.hasStart = typeof proto.start === 'function';
|
|
916
1040
|
result.phases.structure.hasComplete = typeof proto.complete === 'function';
|
|
1041
|
+
result.phases.structure.hasRun = typeof proto.run === 'function';
|
|
917
1042
|
result.phases.structure.pass =
|
|
918
1043
|
result.phases.structure.hasConstructor &&
|
|
919
|
-
|
|
920
|
-
|
|
1044
|
+
(
|
|
1045
|
+
(result.phases.structure.hasStart && result.phases.structure.hasComplete) ||
|
|
1046
|
+
result.phases.structure.hasRun
|
|
1047
|
+
);
|
|
921
1048
|
|
|
922
1049
|
if (!result.phases.structure.pass) {
|
|
923
|
-
if (!result.phases.structure.hasStart) result.errors.push('Processer missing start() method');
|
|
924
|
-
if (!result.phases.structure.hasComplete) result.errors.push('Processer missing complete() method');
|
|
1050
|
+
if (!result.phases.structure.hasRun && !result.phases.structure.hasStart) result.errors.push('Processer missing start() method (manual-trigger) or run() method (auto-trigger)');
|
|
1051
|
+
if (!result.phases.structure.hasRun && !result.phases.structure.hasComplete) result.errors.push('Processer missing complete() method (manual-trigger) or run() method (auto-trigger)');
|
|
925
1052
|
result.status = 'fail';
|
|
926
1053
|
return result;
|
|
927
1054
|
}
|
|
928
1055
|
|
|
1056
|
+
const isAutoTrigger = result.phases.structure.hasRun && !result.phases.structure.hasStart;
|
|
1057
|
+
|
|
929
1058
|
// Load pool schemas and valve config
|
|
930
1059
|
const pools = loadAllPools(poolsDir);
|
|
931
1060
|
const valveConfig = loadValveConfig(valvesDir, valveName);
|
|
@@ -938,41 +1067,62 @@ async function runTests(args) {
|
|
|
938
1067
|
try {
|
|
939
1068
|
processer = new ProcesserClass(mock.context);
|
|
940
1069
|
} catch (e) {
|
|
941
|
-
|
|
1070
|
+
const phaseKey = isAutoTrigger ? 'run' : 'start';
|
|
1071
|
+
if (!result.phases.normalRun[phaseKey]) result.phases.normalRun[phaseKey] = { pass: false, returnValid: false, errors: [] };
|
|
1072
|
+
result.phases.normalRun[phaseKey].errors.push(`constructor threw: ${e.message}`);
|
|
942
1073
|
result.errors.push(`Normal run constructor error: ${e.message}`);
|
|
943
1074
|
result.status = 'fail';
|
|
944
1075
|
return result;
|
|
945
1076
|
}
|
|
946
1077
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
result.
|
|
1078
|
+
if (isAutoTrigger) {
|
|
1079
|
+
// Auto-trigger: test run()
|
|
1080
|
+
if (!result.phases.normalRun.run) result.phases.normalRun.run = { pass: false, returnValid: false, errors: [] };
|
|
1081
|
+
try {
|
|
1082
|
+
const runResult = await processer.run({ valve_id: 1, pool_ids: inputPoolIds });
|
|
1083
|
+
const runErrors = validateRunResult(runResult);
|
|
1084
|
+
result.phases.normalRun.run.returnValid = runErrors.length === 0;
|
|
1085
|
+
result.phases.normalRun.run.errors = runErrors;
|
|
1086
|
+
result.phases.normalRun.run.pass = runErrors.length === 0;
|
|
1087
|
+
if (runErrors.length > 0) {
|
|
1088
|
+
result.errors.push(...runErrors.map(e => `[normal run] ${e}`));
|
|
1089
|
+
}
|
|
1090
|
+
} catch (e) {
|
|
1091
|
+
result.phases.normalRun.run.errors.push(e.message);
|
|
1092
|
+
result.errors.push(`[normal run] Runtime error: ${e.message}`);
|
|
1093
|
+
}
|
|
1094
|
+
} else {
|
|
1095
|
+
// Manual-trigger: test start() + complete()
|
|
1096
|
+
// start
|
|
1097
|
+
try {
|
|
1098
|
+
const startResult = await processer.start({ valve_id: 1, pool_ids: inputPoolIds });
|
|
1099
|
+
const startErrors = validateStartResult(startResult);
|
|
1100
|
+
result.phases.normalRun.start.returnValid = startErrors.length === 0;
|
|
1101
|
+
result.phases.normalRun.start.errors = startErrors;
|
|
1102
|
+
result.phases.normalRun.start.pass = startErrors.length === 0;
|
|
1103
|
+
if (startErrors.length > 0) {
|
|
1104
|
+
result.errors.push(...startErrors.map(e => `[normal start] ${e}`));
|
|
1105
|
+
}
|
|
1106
|
+
} catch (e) {
|
|
1107
|
+
result.phases.normalRun.start.errors.push(e.message);
|
|
1108
|
+
result.errors.push(`[normal start] Runtime error: ${e.message}`);
|
|
956
1109
|
}
|
|
957
|
-
} catch (e) {
|
|
958
|
-
result.phases.normalRun.start.errors.push(e.message);
|
|
959
|
-
result.errors.push(`[normal start] Runtime error: ${e.message}`);
|
|
960
|
-
}
|
|
961
1110
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1111
|
+
// complete — use mock tickets from the start phase (with updated details if any)
|
|
1112
|
+
try {
|
|
1113
|
+
const completeTickets = mock.mockTickets.map(t => ({ ...t, status: 'created' }));
|
|
1114
|
+
const completeResult = await processer.complete({ valve_id: 1, tickets: completeTickets });
|
|
1115
|
+
const completeErrors = validateCompleteResult(completeResult);
|
|
1116
|
+
result.phases.normalRun.complete.returnValid = completeErrors.length === 0;
|
|
1117
|
+
result.phases.normalRun.complete.errors = completeErrors;
|
|
1118
|
+
result.phases.normalRun.complete.pass = completeErrors.length === 0;
|
|
1119
|
+
if (completeErrors.length > 0) {
|
|
1120
|
+
result.errors.push(...completeErrors.map(e => `[normal complete] ${e}`));
|
|
1121
|
+
}
|
|
1122
|
+
} catch (e) {
|
|
1123
|
+
result.phases.normalRun.complete.errors.push(e.message);
|
|
1124
|
+
result.errors.push(`[normal complete] Runtime error: ${e.message}`);
|
|
972
1125
|
}
|
|
973
|
-
} catch (e) {
|
|
974
|
-
result.phases.normalRun.complete.errors.push(e.message);
|
|
975
|
-
result.errors.push(`[normal complete] Runtime error: ${e.message}`);
|
|
976
1126
|
}
|
|
977
1127
|
|
|
978
1128
|
result.sdkCalls = [...new Set(mock.callLog.map(c => c.method))];
|
|
@@ -998,7 +1148,9 @@ async function runTests(args) {
|
|
|
998
1148
|
}
|
|
999
1149
|
}
|
|
1000
1150
|
|
|
1001
|
-
const normalPass =
|
|
1151
|
+
const normalPass = isAutoTrigger
|
|
1152
|
+
? result.phases.normalRun.run?.pass
|
|
1153
|
+
: result.phases.normalRun.start.pass && result.phases.normalRun.complete.pass;
|
|
1002
1154
|
if (!normalPass) {
|
|
1003
1155
|
result.status = 'fail';
|
|
1004
1156
|
}
|
|
@@ -1010,26 +1162,39 @@ async function runTests(args) {
|
|
|
1010
1162
|
try {
|
|
1011
1163
|
processer = new ProcesserClass(mock.context);
|
|
1012
1164
|
} catch (e) {
|
|
1013
|
-
|
|
1165
|
+
const phaseKey = isAutoTrigger ? 'run' : 'start';
|
|
1166
|
+
if (!result.phases.adversarialRun[phaseKey]) result.phases.adversarialRun[phaseKey] = { pass: false, errors: [] };
|
|
1167
|
+
result.phases.adversarialRun[phaseKey].errors.push(`constructor threw: ${e.message}`);
|
|
1014
1168
|
result.warnings.push(`[adversarial] constructor error: ${e.message}`);
|
|
1015
1169
|
}
|
|
1016
1170
|
|
|
1017
1171
|
if (processer) {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1172
|
+
if (isAutoTrigger) {
|
|
1173
|
+
if (!result.phases.adversarialRun.run) result.phases.adversarialRun.run = { pass: false, errors: [] };
|
|
1174
|
+
try {
|
|
1175
|
+
await processer.run({ valve_id: 1, pool_ids: inputPoolIds });
|
|
1176
|
+
result.phases.adversarialRun.run.pass = true;
|
|
1177
|
+
} catch (e) {
|
|
1178
|
+
result.phases.adversarialRun.run.errors.push(e.message);
|
|
1179
|
+
result.warnings.push(`[adversarial run] ${e.constructor.name}: ${e.message}`);
|
|
1180
|
+
}
|
|
1181
|
+
} else {
|
|
1182
|
+
try {
|
|
1183
|
+
await processer.start({ valve_id: 1, pool_ids: inputPoolIds });
|
|
1184
|
+
result.phases.adversarialRun.start.pass = true;
|
|
1185
|
+
} catch (e) {
|
|
1186
|
+
result.phases.adversarialRun.start.errors.push(e.message);
|
|
1187
|
+
result.warnings.push(`[adversarial start] ${e.constructor.name}: ${e.message}`);
|
|
1188
|
+
}
|
|
1025
1189
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1190
|
+
try {
|
|
1191
|
+
const completeTickets = mock.mockTickets.map(t => ({ ...t, status: 'created' }));
|
|
1192
|
+
await processer.complete({ valve_id: 1, tickets: completeTickets });
|
|
1193
|
+
result.phases.adversarialRun.complete.pass = true;
|
|
1194
|
+
} catch (e) {
|
|
1195
|
+
result.phases.adversarialRun.complete.errors.push(e.message);
|
|
1196
|
+
result.warnings.push(`[adversarial complete] ${e.constructor.name}: ${e.message}`);
|
|
1197
|
+
}
|
|
1033
1198
|
}
|
|
1034
1199
|
|
|
1035
1200
|
result.phases.compliance.warnings.push(
|