abelien-harness 0.4.0 → 0.6.0

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.
Files changed (104) hide show
  1. package/.env.example +4 -0
  2. package/README.md +11 -18
  3. package/package.json +2 -3
  4. package/skills/abelien-appearance/SKILL.md +1 -0
  5. package/skills/abelien-clarify/SKILL.md +12 -2
  6. package/skills/abelien-clarify/references/examples.md +22 -0
  7. package/skills/abelien-clarify/scripts/check_result.py +38 -3
  8. package/skills/abelien-clarify/scripts/test_check_result.py +32 -1
  9. package/skills/abelien-guide/SKILL.md +1 -1
  10. package/skills/abelien-hardware/SKILL.md +10 -5
  11. package/skills/abelien-hardware/domain.json +3 -3
  12. package/skills/abelien-hardware/references/search-index-format.md +13 -1
  13. package/skills/abelien-hardware/references/search-index-release.md +4 -2
  14. package/skills/abelien-hardware/references/web-evidence-contract.md +3 -3
  15. package/skills/abelien-hardware/scripts/dimensions.mjs +23 -8
  16. package/skills/abelien-hardware/scripts/electrical.mjs +168 -0
  17. package/skills/abelien-hardware/scripts/finalize-selection.mjs +4 -2
  18. package/skills/abelien-hardware/scripts/provider-chain-contract.mjs +27 -6
  19. package/skills/abelien-hardware/scripts/public-page.mjs +11 -121
  20. package/skills/abelien-hardware/scripts/response-redaction.mjs +1 -130
  21. package/skills/abelien-hardware/scripts/search-index-release.mjs +30 -17
  22. package/skills/abelien-hardware/scripts/selection-request.mjs +6 -5
  23. package/skills/abelien-hardware/scripts/tos-wiki-search.mjs +13 -8
  24. package/skills/abelien-hardware/scripts/volc-web-search.mjs +10 -27
  25. package/skills/abelien-hardware/scripts/web-common.mjs +8 -11
  26. package/skills/abelien-hardware/scripts/web-integrity.mjs +1 -1
  27. package/skills/abelien-hardware/scripts/web-provider-chain.mjs +10 -3
  28. package/skills/abelien-hardware/scripts/web-receipt.mjs +4 -2
  29. package/skills/abelien-hardware/scripts/web-search-plan.mjs +21 -2
  30. package/skills/abelien-hardware/scripts/web-search.mjs +5 -2
  31. package/skills/abelien-hardware-selection/SKILL.md +1 -1
  32. package/skills/abelien-hardware-selection/domain.json +2 -2
  33. package/skills/abelien-hardware-selection/references/selection-service-contract.md +30 -3
  34. package/skills/abelien-hardware-selection/scripts/select.mjs +35 -8
  35. package/skills/abelien-harness/scripts/ab.mjs +24 -2
  36. package/skills/abelien-harness/scripts/web/common.mjs +20 -0
  37. package/skills/abelien-harness/scripts/web/public-read.mjs +168 -0
  38. package/skills/abelien-harness/scripts/web/redact.mjs +136 -0
  39. package/skills/abelien-harness/scripts/web/volc.mjs +49 -0
  40. package/skills/abelien-reference/SKILL.md +51 -0
  41. package/skills/abelien-reference/domain.json +92 -0
  42. package/skills/abelien-reference/fixtures/create.final.json +102 -0
  43. package/skills/abelien-reference/fixtures/restore.collected.json +189 -0
  44. package/skills/abelien-reference/fixtures/restore.final.json +267 -0
  45. package/skills/abelien-reference/references/research-contract.md +74 -0
  46. package/skills/abelien-reference/schemas/reference-evidence.v1.json +140 -0
  47. package/skills/abelien-reference/scripts/evidence.mjs +60 -0
  48. package/skills/abelien-reference/scripts/finalize.mjs +116 -0
  49. package/skills/abelien-reference/scripts/measure.mjs +62 -0
  50. package/skills/abelien-reference/scripts/png.mjs +86 -0
  51. package/skills/abelien-reference/scripts/research.mjs +179 -0
  52. package/skills/abelien-structure/SKILL.md +2 -2
  53. package/skills/abelien-structure/domain.json +64 -0
  54. package/skills/abelien-structure/references/build123d-execution.md +1 -1
  55. package/skills/abelien-structure/references/handoff.md +1 -1
  56. package/skills/abelien-structure/worker/abelien_cad.py +585 -0
  57. package/skills/abelien-structure/worker/assembly-validation.mjs +106 -0
  58. package/skills/abelien-structure/worker/cad.py +1623 -0
  59. package/skills/abelien-structure/worker/cli-adapter.mjs +207 -0
  60. package/skills/abelien-structure/worker/component-fit-glb.mjs +173 -0
  61. package/skills/abelien-structure/worker/component-fit.d.mts +82 -0
  62. package/skills/abelien-structure/worker/component-fit.mjs +167 -0
  63. package/skills/abelien-structure/worker/component-target-image.d.mts +23 -0
  64. package/skills/abelien-structure/worker/component-target-image.mjs +107 -0
  65. package/skills/abelien-structure/worker/component-target-render.py +58 -0
  66. package/skills/abelien-structure/worker/config.mjs +6 -0
  67. package/skills/abelien-structure/worker/engineering-fit.d.mts +48 -0
  68. package/skills/abelien-structure/worker/engineering-fit.mjs +118 -0
  69. package/skills/abelien-structure/worker/engineering-policy.json +16 -0
  70. package/skills/abelien-structure/worker/engineering-repair.py +147 -0
  71. package/skills/abelien-structure/worker/engineering.py +324 -0
  72. package/skills/abelien-structure/worker/identity.mjs +98 -0
  73. package/skills/abelien-structure/worker/inspection-geometry.mjs +71 -0
  74. package/skills/abelien-structure/worker/jobs.mjs +451 -0
  75. package/skills/abelien-structure/worker/library-runtime.mjs +15 -0
  76. package/skills/abelien-structure/worker/main.mjs +102 -0
  77. package/skills/abelien-structure/worker/manifest-digest.mjs +11 -0
  78. package/skills/abelien-structure/worker/materials.mjs +52 -0
  79. package/skills/abelien-structure/worker/native-launcher.py +13 -0
  80. package/skills/abelien-structure/worker/native.mjs +157 -0
  81. package/skills/abelien-structure/worker/representation.mjs +47 -0
  82. package/skills/abelien-structure/worker/sandbox.mjs +41 -0
  83. package/skills/abelien-structure/worker/source-contract.mjs +143 -0
  84. package/skills/abelien-structure/worker/source_call_stats.py +59 -0
  85. package/skills/abelien-structure/worker/supervisor.mjs +38 -0
  86. package/skills/abelien-workspace/SKILL.md +11 -0
  87. package/skills/abelien-workspace/assets/journey-stage-plan.json +155 -0
  88. package/skills/abelien-workspace/domain.json +25 -2
  89. package/skills/abelien-workspace/references/hardware-journey-orchestration.md +1 -1
  90. package/skills/abelien-workspace/references/harness-binding.md +2 -2
  91. package/skills/abelien-workspace/references/user-journey.md +10 -0
  92. package/skills/abelien-workspace/scripts/journey-next.mjs +38 -0
  93. package/skills/abelien-workspace/workflows/definitions/0852e4949f5a6246880c1a26bf5ccaa42f8cda06b960e7c7ce234a0a176878f9.json +229 -0
  94. package/skills/abelien-workspace/workflows/definitions/399ef372790b7330d0ef88ce5096e7f69943d4793de97b2ed6c25165a332612f.json +230 -0
  95. package/skills/abelien-workspace/workflows/definitions/591a7f21e0b0ad586e340163f7533ac910356091ba8d2a26e250c0acbc80cd8b.json +176 -0
  96. package/skills/abelien-workspace/workflows/definitions/7c69ea0d51099b1ae947f08b056ea67f5cad6b40ae8bcb00efb496fa5b9d5ed3.json +149 -0
  97. package/skills/abelien-workspace/workflows/definitions/9b5c65ad03fdecc0148931a7677cfa684a3fb04ab2738d6f7a1086a2c79ac86c.json +149 -0
  98. package/skills/abelien-workspace/workflows/definitions/c1b5c6c653f0c4761d51efee923aafbbeea05512e7f35c60f7f7d36ec49fc1d1.json +229 -0
  99. package/skills/abelien-workspace/workflows/definitions/d9f8c6cdf0b2c41cdeb1c72ac5d1e37d95a7caac3272d434a3c7e8483fc565de.json +150 -0
  100. package/skills/abelien-workspace/workflows/definitions/e391e14c14cb39408a0a9e226ed54ffab7a36badea305ae4a3eafbac8124fc02.json +175 -0
  101. package/skills/abelien-workspace/workflows/definitions/fd8ce7074e2044985778446e93153864940bb9465471c1c71c41328cefff70bf.json +175 -0
  102. package/skills/abelien-workspace/workflows/user-journey-hardware-algorithm.json +5 -4
  103. package/skills/abelien-workspace/workflows/user-journey-hardware-candidates.json +5 -4
  104. package/skills/abelien-workspace/workflows/user-journey-hardware.json +5 -4
@@ -0,0 +1,106 @@
1
+ /** Shared same-STEP assembly evidence checks; provenance is supplied by the CAD or engineering path. */
2
+ import {createHash} from 'node:crypto';
3
+ import {manifestCanonical} from './manifest-digest.mjs';
4
+ import {validMaterial,appearanceIdentity,occurrenceIdentity,validateMaterialBindings} from './materials.mjs';
5
+ const hash=value=>createHash('sha256').update(value).digest('hex');
6
+ const error=code=>Object.assign(new Error(code),{code});
7
+ const HEX=/^[a-f0-9]{64}$/,NODE_ID=/^[a-z0-9][a-z0-9_-]{0,127}$/,STABLE=/^[a-z0-9][a-z0-9._:-]{0,127}$/,TARGET_ID=/^[A-Za-z0-9][A-Za-z0-9:_.-]{0,127}$/;
8
+ const VIEWS=['normal','internal','exploded'],DEFERRED=['voltage','peak_current','port_occupancy','logic_level','concurrent_resources','actuator_load'];
9
+ const canonical=manifestCanonical;
10
+ const finiteVector=value=>Array.isArray(value)&&value.length===3&&value.every(item=>typeof item==='number'&&Number.isFinite(item));
11
+ const exactKeys=(value,keys)=>value&&typeof value==='object'&&!Array.isArray(value)&&canonical(Object.keys(value).sort())===canonical([...keys].sort());
12
+ const nodeList=value=>Array.isArray(value)&&value.length>0&&value.length<=128&&new Set(value).size===value.length&&value.every(item=>typeof item==='string'&&NODE_ID.test(item));
13
+ export function glbJson(bytes) {
14
+ if (bytes.length < 20 || bytes.toString('ascii',0,4) !== 'glTF' || bytes.readUInt32LE(8) !== bytes.length || bytes.readUInt32LE(16) !== 0x4e4f534a) throw error('invalid_artifact');
15
+ const length=bytes.readUInt32LE(12);
16
+ if (length < 2 || 20 + length > bytes.length) throw error('invalid_artifact');
17
+ const value=JSON.parse(bytes.subarray(20,20+length).toString().replace(/\0+$/,''));
18
+ if (!Array.isArray(value.meshes) || !value.meshes.length || !Array.isArray(value.nodes)) throw error('invalid_artifact');
19
+ return value;
20
+ }
21
+ function occurrenceMetadata(occurrence){
22
+ if(Object.hasOwn(occurrence,'material')&&!validMaterial(occurrence.material))return false;
23
+ if(!HEX.test(occurrence.geometrySha256??'')||!HEX.test(occurrence.transformSha256??'')||!HEX.test(occurrence.interfaceSha256??'')||!HEX.test(occurrence.appearanceSha256??'')||
24
+ !Array.isArray(occurrence.mountFrames)||occurrence.mountFrames.length>32||!Array.isArray(occurrence.interfaces)||occurrence.interfaces.length>32||!Array.isArray(occurrence.appearanceBindings)||occurrence.appearanceBindings.length>32)return false;
25
+ const frames=new Set(),interfaces=new Set(),bindings=new Set();
26
+ for(const frame of occurrence.mountFrames){if(!exactKeys(frame,['frameId','translationMm','rotationDeg'])||!NODE_ID.test(frame.frameId??'')||frames.has(frame.frameId)||!finiteVector(frame.translationMm)||!finiteVector(frame.rotationDeg))return false;frames.add(frame.frameId);}
27
+ for(const item of occurrence.interfaces){if(!exactKeys(item,['interfaceId','frameId','compatibilityKey'])||!NODE_ID.test(item.interfaceId??'')||interfaces.has(item.interfaceId)||!frames.has(item.frameId)||!STABLE.test(item.compatibilityKey??''))return false;interfaces.add(item.interfaceId);}
28
+ for(const item of occurrence.appearanceBindings){if(!exactKeys(item,['bindingId','materialSlot'])||!TARGET_ID.test(item.bindingId??'')||bindings.has(item.bindingId)||!STABLE.test(item.materialSlot??''))return false;bindings.add(item.bindingId);}
29
+ return occurrence.transformSha256===hash(manifestCanonical(occurrence.views))&&occurrence.interfaceSha256===hash(manifestCanonical({mountFrames:occurrence.mountFrames,interfaces:occurrence.interfaces}))&&occurrence.appearanceSha256===hash(manifestCanonical(appearanceIdentity(occurrence)));
30
+ }
31
+ export function validateAssemblyEvidence(value, sourceSha256, step, glb, files, validateProvenance) {
32
+ if (!value || typeof value !== 'object' || value.schema !== 'abelien.structure-assembly.v2' || !['occurrence_assembly','legacy_shape'].includes(value.mode) ||
33
+ !['candidate','legacy_unverified'].includes(value.status) || value.units !== 'mm' || value.coordinateSystem !== 'z_up' || value.transformSpace !== 'assembly_root_mm' || value.sourceSha256 !== sourceSha256 ||
34
+ !HEX.test(value.assemblySha256 ?? '') || !HEX.test(value.occurrenceSetSha256 ?? '') || !HEX.test(value.inspectionPlanSha256 ?? '') || !Array.isArray(value.occurrences) ||
35
+ !Number.isSafeInteger(value.occurrenceCount) || value.occurrenceCount !== value.occurrences.length || value.occurrenceCount < 1 || value.occurrenceCount > 128 ||
36
+ !Array.isArray(value.componentCounts) || !value.artifacts || value.artifacts.modelStepSha256 !== hash(step) || value.artifacts.modelGlbSha256 !== hash(glb)) throw error('invalid_artifact');
37
+ const ids=new Set(),counts=new Map();
38
+ for (const occurrence of value.occurrences) {
39
+ if (!occurrence || !NODE_ID.test(occurrence.occurrenceId ?? '') || ids.has(occurrence.occurrenceId) || !STABLE.test(occurrence.componentId ?? '') ||
40
+ !(occurrence.bomItemId === null || STABLE.test(occurrence.bomItemId ?? '')) || typeof occurrence.label !== 'string' || !occurrence.label.trim() || occurrence.label.length > 128 ||
41
+ !occurrence.source || !['generated_custom','generated_envelope','legacy_unverified'].includes(occurrence.source.kind) || !HEX.test(occurrence.source.sha256??'') ||
42
+ !['custom_brep','envelope','legacy_unverified'].includes(occurrence.geometryPrecision) || !occurrence.views || VIEWS.some(view => {
43
+ const transform=occurrence.views[view];return !transform || !finiteVector(transform.translationMm) || !finiteVector(transform.rotationDeg) || typeof transform.visible !== 'boolean';
44
+ }) || occurrence.views.normal.visible !== true||!occurrenceMetadata(occurrence)||!(occurrence.lineage===null||exactKeys(occurrence.lineage,['kind','priorAssemblySha256','previousOccurrenceIds'])&&['preserved','replaced','split','merged'].includes(occurrence.lineage.kind)&&HEX.test(occurrence.lineage.priorAssemblySha256??'')&&nodeList(occurrence.lineage.previousOccurrenceIds))) throw error('invalid_artifact');
45
+ ids.add(occurrence.occurrenceId);counts.set(occurrence.componentId,(counts.get(occurrence.componentId) ?? 0) + 1);
46
+ }
47
+ const expectedCounts=[...counts].sort(([a],[b])=>a.localeCompare(b)).map(([componentId,count])=>({componentId,count}));
48
+ if (canonical(value.componentCounts) !== canonical(expectedCounts)) throw error('invalid_artifact');
49
+ validateProvenance();
50
+ const identity=value.occurrences.map(occurrenceIdentity);
51
+ const transforms=Object.fromEntries(VIEWS.map(view=>[view,value.occurrences.map(occurrence=>({occurrenceId:occurrence.occurrenceId,transform:occurrence.views[view]}))]));
52
+ const reset=hash(manifestCanonical(transforms.normal)),artifacts={normal:'normal.png',internal:'internal.png',exploded:'exploded.png'};
53
+ const expectedLabels=value.occurrences.filter(occurrence=>occurrence.bomItemId!==null).map(({occurrenceId,bomItemId})=>({occurrenceId,bomItemId}));
54
+ if (value.occurrenceSetSha256 !== hash(manifestCanonical(identity)) || !value.views || VIEWS.some(view => {
55
+ const binding=value.views[view],labels=view==='exploded'?expectedLabels:[];
56
+ return binding?.modelGlbSha256 !== hash(glb) || binding?.occurrenceSetSha256 !== value.occurrenceSetSha256 || binding?.artifactName !== artifacts[view] ||
57
+ binding?.imageSha256 !== hash(files.get(artifacts[view])) || binding?.transformSetSha256 !== hash(manifestCanonical(transforms[view])) ||
58
+ binding?.resetTransformSetSha256 !== reset || binding?.scale !== 1 || canonical(binding?.labels) !== canonical(labels);
59
+ }) ||
60
+ !value.deferredChecks || DEFERRED.some(id => value.deferredChecks[id]?.status !== 'unverified' || value.deferredChecks[id]?.gating !== false)) throw error('invalid_artifact');
61
+ const unsigned={...value};delete unsigned.assemblySha256;
62
+ if (value.assemblySha256 !== hash(manifestCanonical(unsigned))) throw error('invalid_artifact');
63
+ if (value.mode === 'legacy_shape') {
64
+ if (value.status !== 'legacy_unverified' || value.occurrenceCount !== 1 || value.occurrences[0].source.kind !== 'legacy_unverified' || value.occurrences[0].geometryPrecision !== 'legacy_unverified') throw error('invalid_artifact');
65
+ return value;
66
+ }
67
+ if (value.status !== 'candidate' || value.occurrenceCount < 2 || value.occurrences.some(occurrence => occurrence.bomItemId === null || occurrence.source.kind === 'legacy_unverified' || occurrence.geometryPrecision === 'legacy_unverified') ||
68
+ value.occurrences.some(occurrence=>occurrence.views.exploded.visible!==true) ||
69
+ !value.occurrences.some(occurrence => canonical(occurrence.views.internal) !== canonical(occurrence.views.normal)) ||
70
+ !value.occurrences.some(occurrence => canonical(occurrence.views.exploded) !== canonical(occurrence.views.normal))) throw error('invalid_artifact');
71
+ const document=glbJson(glb),nodes=document.nodes;
72
+ validateMaterialBindings(document,value.occurrences);
73
+ const parents=new Map(),scene=document.scenes?.[document.scene ?? 0],roots=scene?.nodes;
74
+ if(!Array.isArray(roots)||!roots.length)throw error('invalid_artifact');
75
+ nodes.forEach((node,index)=>{if(Array.isArray(node?.children))node.children.forEach(child=>{if(!Number.isSafeInteger(child)||parents.has(child))throw error('invalid_artifact');parents.set(child,index);});});
76
+ const reachable=new Set();
77
+ const visit=index=>{if(!Number.isSafeInteger(index)||index<0||index>=nodes.length||reachable.has(index))return;reachable.add(index);for(const child of nodes[index]?.children??[])visit(child);};
78
+ roots.forEach(visit);
79
+ const near=(a,b)=>typeof a==='number'&&Number.isFinite(a)&&Math.abs(a-b)<1e-9;
80
+ const quaternion=rotationDeg=>{const [x,y,z]=rotationDeg.map(value=>value*Math.PI/360),sx=Math.sin(x),cx=Math.cos(x),sy=Math.sin(y),cy=Math.cos(y),sz=Math.sin(z),cz=Math.cos(z);return [sx*cy*cz+cx*sy*sz,cx*sy*cz-sx*cy*sz,cx*cy*sz+sx*sy*cz,cx*cy*cz-sx*sy*sz];};
81
+ const sameQuaternion=(actual,expected)=>Array.isArray(actual)&&actual.length===4&&(actual.every((item,index)=>near(item,expected[index]))||actual.every((item,index)=>near(item,-expected[index])));
82
+ const occurrencePose=(node,normal)=>{
83
+ const translation=node.translation??[0,0,0],rotation=node.rotation??[0,0,0,1],scale=node.scale??[1,1,1];
84
+ return node.matrix===undefined&&Array.isArray(translation)&&translation.length===3&&translation.every((item,index)=>near(item,normal.translationMm[index]/1000))&&
85
+ sameQuaternion(rotation,quaternion(normal.rotationDeg))&&Array.isArray(scale)&&scale.length===3&&scale.every(item=>near(item,1));
86
+ };
87
+ const rootQuaternion=[-Math.SQRT1_2,0,0,Math.SQRT1_2];
88
+ const assemblyRoot=node=>node?.name==='assembly-root'&&node.matrix===undefined&&node.translation===undefined&&node.scale===undefined&&sameQuaternion(node.rotation,rootQuaternion)&&!Number.isSafeInteger(node.mesh);
89
+ const meshBelow=(index,seen=new Set())=>{
90
+ if (!Number.isSafeInteger(index) || index < 0 || index >= nodes.length || seen.has(index)) return false;
91
+ seen.add(index);const node=nodes[index];
92
+ return Number.isSafeInteger(node?.mesh) || Array.isArray(node?.children) && node.children.some(child=>meshBelow(child,seen));
93
+ };
94
+ const mapped=[];
95
+ for (const occurrence of value.occurrences) {
96
+ const matching=[];nodes.forEach((node,index)=>{if(node?.name===occurrence.occurrenceId)matching.push(index);});
97
+ const index=matching[0];
98
+ if (matching.length !== 1 || !reachable.has(index) || !meshBelow(index) || !occurrencePose(nodes[index],occurrence.views.normal)) throw error('invalid_artifact');
99
+ mapped.push(index);
100
+ }
101
+ const rootIndices=new Set(mapped.map(index=>parents.get(index)));
102
+ if(rootIndices.size!==1||rootIndices.has(undefined))throw error('invalid_artifact');
103
+ const rootIndex=[...rootIndices][0],root=nodes[rootIndex];
104
+ if(parents.has(rootIndex)||roots.length!==1||roots[0]!==rootIndex||!assemblyRoot(root)||canonical(root.children)!==canonical(mapped))throw error('invalid_artifact');
105
+ return value;
106
+ }