feat: classify chamfer cut pair and expand roadmap
Some checks failed
real-verification / chrome (push) Has been cancelled
real-verification / freecad-oracle (push) Has been cancelled
real-verification / wasm (push) Has been cancelled

This commit is contained in:
2026-08-16 01:30:04 -04:00
parent 03339a6360
commit 27db1a8f93
7 changed files with 360 additions and 22 deletions

View File

@@ -24,12 +24,12 @@
], ],
"followUpProgress": "config/freecad-follow-up-task-progress.json" "followUpProgress": "config/freecad-follow-up-task-progress.json"
}, },
"nextTask": "TSN-PAIR-chamfer-cut", "nextTask": "TSN-PAIR-chamfer-common",
"summary": { "summary": {
"tasks": 1756, "tasks": 1756,
"completed": 337, "completed": 338,
"inProgress": 1, "inProgress": 1,
"pending": 1418 "pending": 1417
}, },
"milestones": [ "milestones": [
{ {
@@ -4474,17 +4474,20 @@
{ {
"id": "TSN-PAIR-chamfer-cut", "id": "TSN-PAIR-chamfer-cut",
"title": "Classify ordered operation pair chamfer->cut", "title": "Classify ordered operation pair chamfer->cut",
"status": "in_progress", "status": "completed",
"dependencies": [ "dependencies": [],
"TSN-PROD-DRIFT-001"
],
"pair": "chamfer->cut", "pair": "chamfer->cut",
"classification": "accepted",
"evidence": [
"config/freecad-ordered-operation-pair-classification.json",
"native-builders-mutation-naming-and-fcstd-resave-pass"
],
"exit": "native acceptance or rejection is recorded; accepted pairs include builder, mutation, naming, and resave evidence" "exit": "native acceptance or rejection is recorded; accepted pairs include builder, mutation, naming, and resave evidence"
}, },
{ {
"id": "TSN-PAIR-chamfer-common", "id": "TSN-PAIR-chamfer-common",
"title": "Classify ordered operation pair chamfer->common", "title": "Classify ordered operation pair chamfer->common",
"status": "pending", "status": "in_progress",
"dependencies": [ "dependencies": [
"TSN-PROD-DRIFT-001" "TSN-PROD-DRIFT-001"
], ],

View File

@@ -10,8 +10,8 @@
"nativeProbe": { "nativeProbe": {
"executor": { "executor": {
"path": "scripts/run-freecad-ordered-operation-pair-classification.mjs", "path": "scripts/run-freecad-ordered-operation-pair-classification.mjs",
"bytes": 44852, "bytes": 45030,
"sha256": "53c2c0bf200a5c8c5edd2a8f6f1c9816ea8784b671a76d9d1df344efcd531280" "sha256": "4a2c54aa5ce03746cb32401ce8ec2c6db008a6e5a805f9fd061c14f9d4ff23ea"
}, },
"matrix": { "matrix": {
"path": "scripts/freecad-naming-production-matrix.mjs", "path": "scripts/freecad-naming-production-matrix.mjs",
@@ -55935,14 +55935,241 @@
"schemaVersion": 1, "schemaVersion": 1,
"status": "pass" "status": "pass"
} }
},
{
"taskId": "TSN-PAIR-chamfer-cut",
"pair": "chamfer->cut",
"classification": "accepted",
"reasonCode": "native-builders-mutation-naming-and-fcstd-resave-pass",
"nativeDecision": "accepted",
"first": {
"operation": "chamfer",
"builder": "BRepFilletAPI_MakeChamfer",
"inputCount": 1,
"historyProvider": "occt-native",
"namingStatus": "ambiguous",
"summary": {
"isValid": true,
"solids": 1,
"faces": 26,
"edges": 48,
"vertices": 24,
"volume": 990.7413333,
"area": 571.4001834,
"bounds": [
-1e-7,
-1e-7,
-1e-7,
10.0000001,
10.0000001,
10.0000001
]
},
"historyRecords": 150,
"selectedRecords": 6,
"ambiguousResults": 20,
"historySha256": "174a5a268c990440fa62f4ec119f35cc7514c7cdb33b8345baf0559b418dac62",
"namingEvidenceSha256": "0bf7f1fb6ffe846efe899fa69af12bbf8d95bfcb9c40ed1ea28b89daa3b96c51",
"namingSemanticSha256": "7dea70fa2fe706e79931dd739ed66b4663a362876f48587e59688fb301fe1c2d"
},
"second": {
"operation": "cut",
"builder": "BRepAlgoAPI_Cut",
"inputCount": 2,
"historyProvider": "occt-native",
"namingStatus": "ambiguous",
"summary": {
"isValid": true,
"solids": 1,
"faces": 31,
"edges": 63,
"vertices": 34,
"volume": 927.0613333,
"area": 604.1774417,
"bounds": [
-2e-7,
-2e-7,
-2e-7,
10.0000002,
10.0000002,
10.0000001
]
},
"historyRecords": 42,
"selectedRecords": 4,
"ambiguousResults": 12,
"historySha256": "065e5143698c6477697ab38dddeb64dedbb2b35f779015c2b94da5bdf39d0b2d",
"namingEvidenceSha256": "fbd3e2b6c707e2dc43c552ceaeb004f09e4faa46210b8af3187b3b51f758bb33",
"namingSemanticSha256": "8119d7f6bed1f84ad3c688149d03816d4956a60036f78546e2dd5f698b074054"
},
"mutation": {
"scope": "second-operation-only",
"parameter": "toolSize",
"trajectory": [
4,
3,
4
],
"beforeValue": 4,
"editedValue": 3,
"restoredValue": 4,
"changed": true,
"restoredExactly": true,
"editedEvidence": {
"operation": "cut",
"builder": "BRepAlgoAPI_Cut",
"inputCount": 2,
"historyProvider": "occt-native",
"namingStatus": "ambiguous",
"summary": {
"isValid": true,
"solids": 1,
"faces": 31,
"edges": 63,
"vertices": 34,
"volume": 963.9813333,
"area": 589.9431272,
"bounds": [
-2e-7,
-2e-7,
-2e-7,
10.0000002,
10.0000002,
10.0000001
]
},
"historyRecords": 42,
"selectedRecords": 4,
"ambiguousResults": 12,
"historySha256": "065e5143698c6477697ab38dddeb64dedbb2b35f779015c2b94da5bdf39d0b2d",
"namingEvidenceSha256": "14374ce9ae9e777e9e3f322f37eab826db4c0491a77d893b7242f41e594ff197",
"namingSemanticSha256": "8119d7f6bed1f84ad3c688149d03816d4956a60036f78546e2dd5f698b074054"
},
"restoredEvidence": {
"operation": "cut",
"builder": "BRepAlgoAPI_Cut",
"inputCount": 2,
"historyProvider": "occt-native",
"namingStatus": "ambiguous",
"summary": {
"isValid": true,
"solids": 1,
"faces": 31,
"edges": 63,
"vertices": 34,
"volume": 927.0613333,
"area": 604.1774417,
"bounds": [
-2e-7,
-2e-7,
-2e-7,
10.0000002,
10.0000002,
10.0000001
]
},
"historyRecords": 42,
"selectedRecords": 4,
"ambiguousResults": 12,
"historySha256": "065e5143698c6477697ab38dddeb64dedbb2b35f779015c2b94da5bdf39d0b2d",
"namingEvidenceSha256": "28dcc861cb88d2ee7c12bbf5a5c11533db8876ddd6a1b9d331606db0cbcb54a4",
"namingSemanticSha256": "8119d7f6bed1f84ad3c688149d03816d4956a60036f78546e2dd5f698b074054"
}
},
"naming": {
"upstreamEvidenceRestored": true,
"downstreamEvidence": true,
"jsonRoundtripStable": true
},
"persistence": {
"baselineId": "freecad-1.1.1-ordered-operation-pair-resave",
"checks": {
"namingEvidenceStable": true,
"shapeStable": true
},
"decision": "accepted",
"freecadVersion": "1.1.1",
"gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
"pair": "chamfer->cut",
"phases": {
"initial": {
"namingEvidenceBytes": 5936,
"namingEvidenceSha256": "fbd3e2b6c707e2dc43c552ceaeb004f09e4faa46210b8af3187b3b51f758bb33",
"pair": "chamfer->cut",
"shape": {
"area": 604.1774417,
"bounds": [
0,
0,
0,
10,
10,
10
],
"edges": 63,
"faces": 31,
"solids": 1,
"valid": true,
"vertices": 34,
"volume": 927.0613333
}
},
"reopened": {
"namingEvidenceBytes": 5936,
"namingEvidenceSha256": "fbd3e2b6c707e2dc43c552ceaeb004f09e4faa46210b8af3187b3b51f758bb33",
"pair": "chamfer->cut",
"shape": {
"area": 604.1774417,
"bounds": [
0,
0,
0,
10,
10,
10
],
"edges": 63,
"faces": 31,
"solids": 1,
"valid": true,
"vertices": 34,
"volume": 927.0613333
}
},
"resaved": {
"namingEvidenceBytes": 5936,
"namingEvidenceSha256": "fbd3e2b6c707e2dc43c552ceaeb004f09e4faa46210b8af3187b3b51f758bb33",
"pair": "chamfer->cut",
"shape": {
"area": 604.1774417,
"bounds": [
0,
0,
0,
10,
10,
10
],
"edges": 63,
"faces": 31,
"solids": 1,
"valid": true,
"vertices": 34,
"volume": 927.0613333
}
}
},
"schemaVersion": 1,
"status": "pass"
}
} }
], ],
"summary": { "summary": {
"registeredOperations": 19, "registeredOperations": 19,
"orderedPairs": 361, "orderedPairs": 361,
"classifiedPairs": 207, "classifiedPairs": 208,
"accepted": 155, "accepted": 156,
"rejected": 52, "rejected": 52,
"unknown": 154 "unknown": 153
} }
} }

View File

@@ -390,7 +390,7 @@
{ {
"path": "sw.js", "path": "sw.js",
"bytes": 1390, "bytes": 1390,
"sha256": "8b0477955514535bb11cef3733135b67422cc8c085b4331c3b9cb1a1adc9d852" "sha256": "98ec667a774f8370ed654e457b86d4943b2f1eb89e228bd4e7e652b31a51b982"
}, },
{ {
"path": "vendor/camotics/camotics-sweep.wasm", "path": "vendor/camotics/camotics-sweep.wasm",
@@ -412,7 +412,7 @@
"status": "signed", "status": "signed",
"algorithm": "Ed25519", "algorithm": "Ed25519",
"keyId": "bitbybit-local-release-2026", "keyId": "bitbybit-local-release-2026",
"payloadSha256": "ad5f25aeb7e1382b3172d9919357a9831ac198841e54304a6789665cd21cda31", "payloadSha256": "5172b35d4d194483ac1934a1653259b1c7f5bb78c5ab4a68f7e5033cf2a22b86",
"signature": "c8xaS8E7BOThrQKOK9WlCLpMazuqlBZ6eMKbCTt+tzeC0b4TyEEaiVvjelUtFDjo5PK0fVYPeYISrS2895LTDA==" "signature": "FoaYFhliT+/njf8hXECKj5IoCeHPj1MnpbTBIw/FxzYpfyrgQ01nuPkwslM4okUk3d97cncjaXnEkDA7lZBtDw=="
} }
} }

View File

@@ -3,9 +3,9 @@
生成日期2026-08-15。基线FreeCAD 1.1.1 / `0108fd4b4850cc46e625b60e53cea7a7bbe69f8d` 生成日期2026-08-15。基线FreeCAD 1.1.1 / `0108fd4b4850cc46e625b60e53cea7a7bbe69f8d`
- 唯一活动任务:`TSN-PAIR-chamfer-cut` - 唯一活动任务:`TSN-PAIR-chamfer-common`
- 微任务1756 total / 337 completed / 1 in_progress / 1418 pending - 微任务1756 total / 338 completed / 1 in_progress / 1417 pending
- 有序操作对211/361 classified / 150 unknown - 有序操作对212/361 classified / 149 unknown
- Property27/85 native types / 58 opaque types - Property27/85 native types / 58 opaque types
- exact 任务5/52 completed - exact 任务5/52 completed
- 模块能力0 exact / 26 compatible / 5 proxy / 3 development - 模块能力0 exact / 26 compatible / 5 proxy / 3 development
@@ -15,7 +15,7 @@
| `ORA-GUI-BASELINE` | 106 | 106 | 0 | 0 | `-` | | `ORA-GUI-BASELINE` | 106 | 106 | 0 | 0 | `-` |
| `ORA-GUI-WORKFLOWS` | 7 | 7 | 0 | 0 | `-` | | `ORA-GUI-WORKFLOWS` | 7 | 7 | 0 | 0 | `-` |
| `TSN-RECOVERY-DRIFT` | 13 | 13 | 0 | 0 | `-` | | `TSN-RECOVERY-DRIFT` | 13 | 13 | 0 | 0 | `-` |
| `TSN-ORDERED-PAIRS` | 361 | 211 | 1 | 149 | `TSN-PAIR-chamfer-cut` | | `TSN-ORDERED-PAIRS` | 361 | 212 | 1 | 148 | `TSN-PAIR-chamfer-common` |
| `PROPERTY-CODECS` | 522 | 0 | 0 | 522 | `-` | | `PROPERTY-CODECS` | 522 | 0 | 0 | 522 | `-` |
| `DOCUMENT-SEMANTICS` | 90 | 0 | 0 | 90 | `-` | | `DOCUMENT-SEMANTICS` | 90 | 0 | 0 | 90 | `-` |
| `CORE-MODELING` | 171 | 0 | 0 | 171 | `-` | | `CORE-MODELING` | 171 | 0 | 0 | 171 | `-` |

View File

@@ -225,6 +225,7 @@ const expectedPairs = [
{ pair: 'fillet->polar-pattern', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse', secondInputCount: 1, mutationParameter: 'angle', mutationTrajectory: [30, 45, 30] }, { pair: 'fillet->polar-pattern', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse', secondInputCount: 1, mutationParameter: 'angle', mutationTrajectory: [30, 45, 30] },
{ pair: 'fillet->multi-transform', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse(ordered)', secondInputCount: 1, mutationParameter: 'multiTranslationX', mutationTrajectory: [8, 9, 8] }, { pair: 'fillet->multi-transform', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse(ordered)', secondInputCount: 1, mutationParameter: 'multiTranslationX', mutationTrajectory: [8, 9, 8] },
{ pair: 'chamfer->fuse', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeChamfer', firstInputCount: 1, secondBuilder: 'BRepAlgoAPI_Fuse', secondInputCount: 2, mutationParameter: 'toolOffsetX', mutationTrajectory: [8, 7, 8] }, { pair: 'chamfer->fuse', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeChamfer', firstInputCount: 1, secondBuilder: 'BRepAlgoAPI_Fuse', secondInputCount: 2, mutationParameter: 'toolOffsetX', mutationTrajectory: [8, 7, 8] },
{ pair: 'chamfer->cut', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeChamfer', firstInputCount: 1, secondBuilder: 'BRepAlgoAPI_Cut', secondInputCount: 2, mutationParameter: 'toolSize', mutationTrajectory: [4, 3, 4] },
] ]
if (report.nativeProbe?.artifacts?.length !== 3 || report.classifications?.length !== expectedPairs.length) fail('native artifacts or classification prefix is incomplete.') if (report.nativeProbe?.artifacts?.length !== 3 || report.classifications?.length !== expectedPairs.length) fail('native artifacts or classification prefix is incomplete.')
for (const [index, entry] of report.classifications.entries()) { for (const [index, entry] of report.classifications.entries()) {
@@ -286,6 +287,6 @@ for (const [index, entry] of report.classifications.entries()) {
const phases = entry.persistence.phases const phases = entry.persistence.phases
if (JSON.stringify(phases?.initial) !== JSON.stringify(phases?.reopened) || JSON.stringify(phases?.initial) !== JSON.stringify(phases?.resaved) || phases.initial.namingEvidenceSha256 !== entry.second.namingEvidenceSha256) fail(`${expected.pair} FCStd phases changed Shape or naming evidence.`) if (JSON.stringify(phases?.initial) !== JSON.stringify(phases?.reopened) || JSON.stringify(phases?.initial) !== JSON.stringify(phases?.resaved) || phases.initial.namingEvidenceSha256 !== entry.second.namingEvidenceSha256) fail(`${expected.pair} FCStd phases changed Shape or naming evidence.`)
} }
const expectedSummary = { registeredOperations: 19, orderedPairs: 361, classifiedPairs: 207, accepted: 155, rejected: 52, unknown: 154 } const expectedSummary = { registeredOperations: 19, orderedPairs: 361, classifiedPairs: 208, accepted: 156, rejected: 52, unknown: 153 }
if (JSON.stringify(report.summary) !== JSON.stringify(expectedSummary)) fail('summary is inconsistent.') if (JSON.stringify(report.summary) !== JSON.stringify(expectedSummary)) fail('summary is inconsistent.')
console.log(JSON.stringify({ status: 'freecad-ordered-operation-pair-classification-pass', completedTasks: report.classifications.map(({ taskId }) => taskId), pairs: report.classifications.map(({ pair, classification }) => ({ pair, classification })), nativeBuilderRuns: report.classifications.length * 4, fcstdPhases: report.classifications.length * 3, remainingPairs: report.summary.unknown }, null, 2)) console.log(JSON.stringify({ status: 'freecad-ordered-operation-pair-classification-pass', completedTasks: report.classifications.map(({ taskId }) => taskId), pairs: report.classifications.map(({ pair, classification }) => ({ pair, classification })), nativeBuilderRuns: report.classifications.length * 4, fcstdPhases: report.classifications.length * 3, remainingPairs: report.summary.unknown }, null, 2))

View File

@@ -126,6 +126,7 @@ const pairSpecOverrides = {
'fillet->draft': { ...operationSpecs.draft, decision: 'rejected', rejectionAuthority: 'occt-builder' }, 'fillet->draft': { ...operationSpecs.draft, decision: 'rejected', rejectionAuthority: 'occt-builder' },
'fillet->thickness': { ...operationSpecs.thickness, decision: 'rejected', rejectionAuthority: 'occt-no-op', kernelOutcome: 'no-op' }, 'fillet->thickness': { ...operationSpecs.thickness, decision: 'rejected', rejectionAuthority: 'occt-no-op', kernelOutcome: 'no-op' },
'chamfer->fuse': { ...operationSpecs.fuse, nominal: { toolSize: 5, toolOffset: 8 }, edited: { toolSize: 5, toolOffset: 7 }, beforeValue: 8, editedValue: 7 }, 'chamfer->fuse': { ...operationSpecs.fuse, nominal: { toolSize: 5, toolOffset: 8 }, edited: { toolSize: 5, toolOffset: 7 }, beforeValue: 8, editedValue: 7 },
'chamfer->cut': { ...operationSpecs.cut, nominal: { toolSize: 4, toolOffset: 2 }, edited: { toolSize: 3, toolOffset: 2 }, beforeValue: 4, editedValue: 3 },
} }
const createOperation = (module, { operation, objectStep, toolSize, toolOffset, angle, length, profileWidth = 2, profileHeight = 3, profileX = 0.5, profileY = 2, profileZ = 0, ruled, spineLength, radius, distance, depth, position, direction, faceIndex, axisOrigin, neutralPlaneDirection, reversed, offset, intersectionJoin, translationX, planeOriginX, multiTranslationX, multiMirrorPlaneX = 5, stage }) => { const createOperation = (module, { operation, objectStep, toolSize, toolOffset, angle, length, profileWidth = 2, profileHeight = 3, profileX = 0.5, profileY = 2, profileZ = 0, ruled, spineLength, radius, distance, depth, position, direction, faceIndex, axisOrigin, neutralPlaneDirection, reversed, offset, intersectionJoin, translationX, planeOriginX, multiTranslationX, multiMirrorPlaneX = 5, stage }) => {
@@ -467,7 +468,7 @@ const classifyPair = async (fromOperation, toOperation, pairIndex) => {
const operations = ['fuse', 'cut', 'common', 'rotate', 'pad', 'pocket', 'loft', 'pipe', 'revolution', 'groove', 'fillet', 'chamfer', 'hole', 'draft', 'thickness', 'linear-pattern', 'polar-pattern', 'mirrored', 'multi-transform'] const operations = ['fuse', 'cut', 'common', 'rotate', 'pad', 'pocket', 'loft', 'pipe', 'revolution', 'groove', 'fillet', 'chamfer', 'hole', 'draft', 'thickness', 'linear-pattern', 'polar-pattern', 'mirrored', 'multi-transform']
const orderedPairPrefix = [...operations.map((toOperation) => ['fuse', toOperation]), ['cut', 'fuse'], ['cut', 'cut'], ['cut', 'common'], ['cut', 'pad'], ['cut', 'pocket'], ['cut', 'loft'], ['cut', 'pipe'], ['cut', 'revolution'], ['cut', 'groove'], ['cut', 'fillet'], ['cut', 'chamfer'], ['cut', 'hole'], ['cut', 'draft'], ['cut', 'thickness'], ['cut', 'linear-pattern'], ['cut', 'polar-pattern'], ['cut', 'mirrored'], ['cut', 'multi-transform'], ['common', 'fuse'], ['common', 'cut'], ['common', 'common'], ['common', 'rotate'], ['common', 'pad'], ['common', 'pocket'], ['common', 'loft'], ['common', 'pipe'], ['common', 'revolution'], ['common', 'groove'], ['common', 'fillet'], ['common', 'chamfer'], ['common', 'hole'], ['common', 'draft'], ['common', 'thickness'], ['common', 'linear-pattern'], ['common', 'polar-pattern'], ['common', 'mirrored'], ['common', 'multi-transform'], ['rotate', 'fuse'], ['rotate', 'cut'], ['rotate', 'common'], ['rotate', 'rotate'], ['rotate', 'pad'], ['rotate', 'pocket'], ['rotate', 'loft'], ['rotate', 'pipe'], ['rotate', 'revolution'], ['rotate', 'groove'], ['rotate', 'chamfer'], ['rotate', 'hole'], ['rotate', 'draft'], ['rotate', 'thickness'], ['rotate', 'linear-pattern'], ['rotate', 'polar-pattern'], ['rotate', 'mirrored'], ['rotate', 'multi-transform'], ['pad', 'fuse'], ['pad', 'cut'], ['pad', 'common'], ['pad', 'rotate'], ['pad', 'pad'], ['pad', 'pocket'], ['pad', 'loft'], ['pad', 'pipe'], ['pad', 'revolution'], ['pad', 'groove'], ['pad', 'fillet'], ['pad', 'chamfer'], ['pad', 'hole'], ['pad', 'draft'], ['pad', 'thickness'], ['pad', 'linear-pattern'], ['pad', 'polar-pattern'], ['pad', 'mirrored'], ['pad', 'multi-transform'], ['pocket', 'fuse'], ['pocket', 'cut'], ['pocket', 'common'], ['pocket', 'rotate'], ['pocket', 'pad'], ['pocket', 'pocket'], ['pocket', 'loft'], ['pocket', 'pipe'], ['pocket', 'revolution'], ['pocket', 'groove'], ['pocket', 'fillet'], ['pocket', 'chamfer'], ['pocket', 'hole'], ['pocket', 'draft'], ['pocket', 'thickness'], ['pocket', 'linear-pattern'], ['pocket', 'polar-pattern'], ['pocket', 'mirrored'], ['pocket', 'multi-transform'], ['loft', 'fuse'], ['loft', 'cut'], ['loft', 'common'], ['loft', 'rotate'], ['loft', 'pad'], ['loft', 'pocket'], ['loft', 'loft'], ['loft', 'pipe'], ['loft', 'revolution'], ['loft', 'groove'], ['loft', 'fillet'], ['loft', 'chamfer'], ['loft', 'hole'], ['loft', 'draft'], ['loft', 'thickness'], ['loft', 'linear-pattern'], ['loft', 'polar-pattern'], ['loft', 'mirrored'], ['loft', 'multi-transform'], ['pipe', 'fuse'], ['pipe', 'cut'], ['pipe', 'common'], ['pipe', 'rotate'], ['pipe', 'pad'], ['pipe', 'pocket'], ['pipe', 'loft'], ['pipe', 'pipe'], ['pipe', 'revolution'], ['pipe', 'groove'], ['pipe', 'fillet'], ['pipe', 'chamfer'], ['pipe', 'hole'], ['pipe', 'draft'], ['pipe', 'thickness'], ['pipe', 'linear-pattern'], ['pipe', 'polar-pattern'], ['pipe', 'mirrored'], ['pipe', 'multi-transform'], ['revolution', 'fuse'], ['revolution', 'cut'], ['revolution', 'common'], ['revolution', 'rotate'], ['revolution', 'pad'], ['revolution', 'pocket'], ['revolution', 'loft'], ['revolution', 'pipe'], ['revolution', 'revolution'], ['revolution', 'groove'], ['revolution', 'fillet'], ['revolution', 'chamfer'], ['revolution', 'hole'], ['revolution', 'draft'], ['revolution', 'thickness'], ['revolution', 'linear-pattern'], ['revolution', 'polar-pattern'], ['revolution', 'mirrored'], ['revolution', 'multi-transform'], ['groove', 'fuse'], ['groove', 'cut'], ['groove', 'common'], ['groove', 'rotate'], ['groove', 'pad'], ['groove', 'pocket'], ['groove', 'loft'], ['groove', 'pipe'], ['groove', 'revolution'], ['groove', 'groove'], ['groove', 'fillet'], ['groove', 'chamfer'], ['groove', 'hole'], ['groove', 'draft'], ['groove', 'thickness'], ['groove', 'linear-pattern'], ['groove', 'polar-pattern'], ['groove', 'mirrored']] const orderedPairPrefix = [...operations.map((toOperation) => ['fuse', toOperation]), ['cut', 'fuse'], ['cut', 'cut'], ['cut', 'common'], ['cut', 'pad'], ['cut', 'pocket'], ['cut', 'loft'], ['cut', 'pipe'], ['cut', 'revolution'], ['cut', 'groove'], ['cut', 'fillet'], ['cut', 'chamfer'], ['cut', 'hole'], ['cut', 'draft'], ['cut', 'thickness'], ['cut', 'linear-pattern'], ['cut', 'polar-pattern'], ['cut', 'mirrored'], ['cut', 'multi-transform'], ['common', 'fuse'], ['common', 'cut'], ['common', 'common'], ['common', 'rotate'], ['common', 'pad'], ['common', 'pocket'], ['common', 'loft'], ['common', 'pipe'], ['common', 'revolution'], ['common', 'groove'], ['common', 'fillet'], ['common', 'chamfer'], ['common', 'hole'], ['common', 'draft'], ['common', 'thickness'], ['common', 'linear-pattern'], ['common', 'polar-pattern'], ['common', 'mirrored'], ['common', 'multi-transform'], ['rotate', 'fuse'], ['rotate', 'cut'], ['rotate', 'common'], ['rotate', 'rotate'], ['rotate', 'pad'], ['rotate', 'pocket'], ['rotate', 'loft'], ['rotate', 'pipe'], ['rotate', 'revolution'], ['rotate', 'groove'], ['rotate', 'chamfer'], ['rotate', 'hole'], ['rotate', 'draft'], ['rotate', 'thickness'], ['rotate', 'linear-pattern'], ['rotate', 'polar-pattern'], ['rotate', 'mirrored'], ['rotate', 'multi-transform'], ['pad', 'fuse'], ['pad', 'cut'], ['pad', 'common'], ['pad', 'rotate'], ['pad', 'pad'], ['pad', 'pocket'], ['pad', 'loft'], ['pad', 'pipe'], ['pad', 'revolution'], ['pad', 'groove'], ['pad', 'fillet'], ['pad', 'chamfer'], ['pad', 'hole'], ['pad', 'draft'], ['pad', 'thickness'], ['pad', 'linear-pattern'], ['pad', 'polar-pattern'], ['pad', 'mirrored'], ['pad', 'multi-transform'], ['pocket', 'fuse'], ['pocket', 'cut'], ['pocket', 'common'], ['pocket', 'rotate'], ['pocket', 'pad'], ['pocket', 'pocket'], ['pocket', 'loft'], ['pocket', 'pipe'], ['pocket', 'revolution'], ['pocket', 'groove'], ['pocket', 'fillet'], ['pocket', 'chamfer'], ['pocket', 'hole'], ['pocket', 'draft'], ['pocket', 'thickness'], ['pocket', 'linear-pattern'], ['pocket', 'polar-pattern'], ['pocket', 'mirrored'], ['pocket', 'multi-transform'], ['loft', 'fuse'], ['loft', 'cut'], ['loft', 'common'], ['loft', 'rotate'], ['loft', 'pad'], ['loft', 'pocket'], ['loft', 'loft'], ['loft', 'pipe'], ['loft', 'revolution'], ['loft', 'groove'], ['loft', 'fillet'], ['loft', 'chamfer'], ['loft', 'hole'], ['loft', 'draft'], ['loft', 'thickness'], ['loft', 'linear-pattern'], ['loft', 'polar-pattern'], ['loft', 'mirrored'], ['loft', 'multi-transform'], ['pipe', 'fuse'], ['pipe', 'cut'], ['pipe', 'common'], ['pipe', 'rotate'], ['pipe', 'pad'], ['pipe', 'pocket'], ['pipe', 'loft'], ['pipe', 'pipe'], ['pipe', 'revolution'], ['pipe', 'groove'], ['pipe', 'fillet'], ['pipe', 'chamfer'], ['pipe', 'hole'], ['pipe', 'draft'], ['pipe', 'thickness'], ['pipe', 'linear-pattern'], ['pipe', 'polar-pattern'], ['pipe', 'mirrored'], ['pipe', 'multi-transform'], ['revolution', 'fuse'], ['revolution', 'cut'], ['revolution', 'common'], ['revolution', 'rotate'], ['revolution', 'pad'], ['revolution', 'pocket'], ['revolution', 'loft'], ['revolution', 'pipe'], ['revolution', 'revolution'], ['revolution', 'groove'], ['revolution', 'fillet'], ['revolution', 'chamfer'], ['revolution', 'hole'], ['revolution', 'draft'], ['revolution', 'thickness'], ['revolution', 'linear-pattern'], ['revolution', 'polar-pattern'], ['revolution', 'mirrored'], ['revolution', 'multi-transform'], ['groove', 'fuse'], ['groove', 'cut'], ['groove', 'common'], ['groove', 'rotate'], ['groove', 'pad'], ['groove', 'pocket'], ['groove', 'loft'], ['groove', 'pipe'], ['groove', 'revolution'], ['groove', 'groove'], ['groove', 'fillet'], ['groove', 'chamfer'], ['groove', 'hole'], ['groove', 'draft'], ['groove', 'thickness'], ['groove', 'linear-pattern'], ['groove', 'polar-pattern'], ['groove', 'mirrored']]
orderedPairPrefix.push(['groove', 'multi-transform'], ['fillet', 'fuse'], ['fillet', 'cut'], ['fillet', 'common'], ['fillet', 'rotate'], ['fillet', 'pad'], ['fillet', 'pocket'], ['fillet', 'loft'], ['fillet', 'pipe'], ['fillet', 'revolution'], ['fillet', 'groove'], ['fillet', 'fillet'], ['fillet', 'chamfer'], ['fillet', 'hole'], ['fillet', 'draft'], ['fillet', 'thickness'], ['fillet', 'linear-pattern'], ['fillet', 'polar-pattern'], ['fillet', 'multi-transform'], ['chamfer', 'fuse']) orderedPairPrefix.push(['groove', 'multi-transform'], ['fillet', 'fuse'], ['fillet', 'cut'], ['fillet', 'common'], ['fillet', 'rotate'], ['fillet', 'pad'], ['fillet', 'pocket'], ['fillet', 'loft'], ['fillet', 'pipe'], ['fillet', 'revolution'], ['fillet', 'groove'], ['fillet', 'fillet'], ['fillet', 'chamfer'], ['fillet', 'hole'], ['fillet', 'draft'], ['fillet', 'thickness'], ['fillet', 'linear-pattern'], ['fillet', 'polar-pattern'], ['fillet', 'multi-transform'], ['chamfer', 'fuse'], ['chamfer', 'cut'])
const classifications = [] const classifications = []
const selectedPairs = process.env.FREECAD_ONLY_PAIR ? orderedPairPrefix.filter(([fromOperation, toOperation]) => `${fromOperation}->${toOperation}` === process.env.FREECAD_ONLY_PAIR) : orderedPairPrefix const selectedPairs = process.env.FREECAD_ONLY_PAIR ? orderedPairPrefix.filter(([fromOperation, toOperation]) => `${fromOperation}->${toOperation}` === process.env.FREECAD_ONLY_PAIR) : orderedPairPrefix
if (process.env.FREECAD_ONLY_PAIR && selectedPairs.length !== 1) fail(`focused pair ${process.env.FREECAD_ONLY_PAIR} is not in the classified prefix.`) if (process.env.FREECAD_ONLY_PAIR && selectedPairs.length !== 1) fail(`focused pair ${process.env.FREECAD_ONLY_PAIR} is not in the classified prefix.`)

View File

@@ -44,6 +44,101 @@ node -e "const q=require('./config/freecad-active-work-queue.json'); console.log
详细拆分和依赖关系只在机器队列与执行计划中维护。 详细拆分和依赖关系只在机器队列与执行计划中维护。
## 3A. 全功能对标拆分总表(静态路线图)
本节只定义长期范围、阶段边界和可重复的验收粒度,不记录实时完成数、活动任务名或下一任务名。每个阶段由机器队列展开为“一个原生 artifact、一个聚焦检查器、一个明确退出条件”的微任务队列推进时不得跨越阶段依赖。
### 3A.1 统一工作单元
每一个功能、对象、属性、命令、格式和 UI 状态都按下列最小单元拆解:
| 编号 | 最小任务 | 必须产出 | 单项退出条件 |
| --- | --- | --- | --- |
| U1 | 原生清册 | TypeId/命令名、输入、默认值、适用性、失败前置 | 清册可机器读取,未知项显式登记 |
| U2 | 名义成功 | FreeCAD/OCCT Shape、对象集、属性、诊断、拓扑摘要 | 名义输入重复运行结果稳定 |
| U3 | 边界成功 | 最小值、最大值、空集合、单元素、多元素和单位边界 | 每个边界都有接受或拒绝证据 |
| U4 | 原生拒绝 | 失败阶段、异常/诊断、空或无效 Shape、对象集 | 不以 Web fallback 替代原生拒绝 |
| U5 | 取消与中断 | cancel、abort、超时、Worker 终止时的资源和状态 | 文档、事务、Worker、OPFS 无污染 |
| U6 | 参数变异 | 一次只改一个参数的 before/edit/restore 轨迹 | 编辑改变的字段可解释,恢复逐字段一致 |
| U7 | 历史与命名 | Generated/Modified/Deleted、ElementMap2、MappedNameRef、hash | 无私有 token、无错误绑定、歧义保持歧义 |
| U8 | 持久化 | save、reopen、resave、版本迁移和资源引用 | Shape、属性、命名、对象图无未解释漂移 |
| U9 | Facade | 生产 API、类型校验、错误映射、能力级别 | UI 不直连内核,禁止隐藏 fallback |
| U10 | 浏览器回放 | Worker、OPFS、UI、选择、进度、释放证据 | 真实 Chrome 流程与原生合同一致 |
| U11 | 回归封存 | focused check、变更摘要、哈希、证据索引 | 证据可独立复跑,队列只推进一项 |
### 3A.2 阶段与微里程碑
| 阶段 | 目标 | 细分里程碑(每项可再拆为 U1-U11 | 阶段退出门槛 |
| --- | --- | --- | --- |
| P0 基线与治理 | 固定 FreeCAD/OCCT、证据格式和能力声明 | P0.1 版本/提交锁定P0.2 TypeId/命令/格式清册P0.3 artifact 哈希P0.4 依赖图P0.5 known-gap 账本P0.6 单活动任务门禁 | 基线、生成器、检查器和 exact 声明一致,禁止未登记能力 |
| P1 原生观察层 | 建立所有差异的可重复 oracle | P1.1 空文档状态P1.2 单对象状态P1.3 选择/子形状态P1.4 属性 editor 状态P1.5 事务状态P1.6 失败/取消状态P1.7 GUI 命令状态P1.8 资源与版本信息 | 原生 probe 覆盖清册;未知状态只能进入 pending |
| P2 内核几何 | 对齐 OCCT builder 和拓扑历史 | P2.1 基本几何P2.2 布尔P2.3 变换/阵列P2.4 草图与约束几何P2.5 修复/偏置/抽壳P2.6 曲面/网格P2.7 有效性和容差P2.8 资源释放 | 每个 builder 有成功、边界、失败、恢复和 history 证据 |
| P3 文档与历史 | 对齐 Document/DAG/事务/扩展语义 | P3.1 对象生命周期P3.2 属性类型和 editorP3.3 Link/LinkSubP3.4 recompute 顺序P3.5 dependency cycleP3.6 partial failureP3.7 observer 顺序P3.8 undo/redo/abortP3.9 Extension/FeaturePythonP3.10 多文档/MDI | 对象图、事务事件和失败恢复在 save/reopen 后仍可解释 |
| P4 核心建模工作台 | 对齐 Sketcher、Part、PartDesign | P4.1 草图几何P4.2 约束求解P4.3 草图编辑器P4.4 Part 原语P4.5 布尔/修复/检查P4.6 PartDesign Body/TipP4.7 加料/减料P4.8 旋转/扫掠/放样P4.9 圆角/倒角/孔/拔模/厚度P4.10 阵列/镜像/多变换P4.11 跨特征 mutationP4.12 全有序操作对 | 每项都有 A-I 证据,命名和 FCStd 证据达到 exact gate |
| P5 文件与数据交换 | 对齐原生格式、导入导出和代理边界 | P5.1 FCStd 对象/属性/资源P5.2 BREPP5.3 STEPP5.4 IGESP5.5 STL/OBJ/PLYP5.6 DXF/SVG/PDFP5.7 IFCP5.8 CSV/G-codeP5.9 JT/IDF/OpenSCADP5.10 未知对象保留P5.11 安全解析/大小限制 | 双向 round-trip、损坏/未知输入、编码和资源引用均有明确 exact/compatible/excluded 结果 |
| P6 GUI 与交互 | 对齐 FreeCAD 桌面状态和浏览器交互 | P6.1 Shell/菜单/工具栏P6.2 Workbench 切换P6.3 Tree/Dock/MDIP6.4 Task panelP6.5 Property editorP6.6 对话框验证P6.7 3D 选择/预选P6.8 快捷键/焦点P6.9 报告/状态/进度P6.10 cancel/long taskP6.11 截图/可访问性树P6.12 mobile/responsive | 每个工作台的空文档、成功、失败、取消、恢复流程可真实浏览器回放 |
| P7 工程与专业工作台 | 对齐 Draft、TechDraw、Spreadsheet、Plot、Assembly、BIM、Surface、Mesh、Material | P7.1 Draft snaps/working planeP7.2 TechDraw 页面/视图/标注P7.3 Spreadsheet 公式/别名/样式P7.4 Plot 数据/坐标轴/导出P7.5 Assembly joints/solverP7.6 BIM/IFC/数量P7.7 Surface trim/sew/repairP7.8 Mesh 修复/转换P7.9 Material 继承/外观P7.10 跨工作台引用 | 每个专业对象具有对象图、交互、持久化、导出和失败证据 |
| P8 分析、制造与扩展 | 对齐 FEM、CAM、Robot、Inspection、Points、ReverseEngineering、脚本和 addon | P8.1 FEM 对象/网格/求解/结果P8.2 CAM Path/任务/后处理P8.3 仿真和机床 profileP8.4 Robot 运动学/碰撞/导出P8.5 点云/重建P8.6 测量/偏差/报告P8.7 Python API/macro 沙箱P8.8 addon 签名/权限/回滚P8.9 代理模块显式排除 | 生产授权与浏览器能力分离;不支持项可审计、可解释、不会伪装支持 |
| P9 平台质量 | 完成跨环境和长稳验证 | P9.1 Chrome/Firefox/SafariP9.2 Windows/Linux/macOSP9.3 GPU/软件渲染P9.4 desktop/tablet/mobileP9.5 locale/单位/时区P9.6 屏幕阅读器/键盘P9.7 性能/内存/WorkerP9.8 fuzz/stress/长会话P9.9 崩溃恢复/迁移P9.10 供应链/安全 | 所有支持矩阵无未知差异,预算、漏洞、泄漏、错误绑定均为零 |
| P10 发布与 exact promotion | 形成可签名、可复核的发布物 | P10.1 证据索引P10.2 生成物重建P10.3 SBOM/依赖审计P10.4 签名/验签P10.5 release closureP10.6 回滚演练P10.7 exact capability matrixP10.8 版本公告 | 所有 exact 任务闭合,必要零指标满足,签名产物和回滚包可复现 |
### 3A.3 每个阶段内的执行顺序
阶段内固定按以下细粒度顺序执行,任何一步失败都只登记当前微任务,不跳到下一步:
1. 清册:确认 TypeId、命令入口、默认参数、输入数量、依赖和适用性。
2. 名义态:用真实 FreeCAD/OCCT builder 生成结果,记录 Shape、拓扑、属性、诊断和对象集。
3. 边界态:逐个执行最小值、最大值、空、重复、越界和单位转换;原生拒绝也要记录。
4. 失败态:记录失败阶段、异常类型、精确文本、状态标志、空/无效 Shape 和对象污染。
5. 变异态:只修改一个参数,执行 before/edit/restore检查几何、历史、命名和文档版本。
6. 事务态:覆盖 commit、abort、undo、redo、stale、cycle、cancel、timeout 和 Worker 终止。
7. 持久化态:执行 save/reopen/resave校验对象图、属性、资源、Shape、命名和 hash。
8. Facade 态:通过生产 Facade 重放同一输入,确认错误映射、能力级别和权限边界。
9. 浏览器态:真实 Worker/OPFS/Chrome 回放,检查 UI、选择、进度、释放和离线行为。
10. 封存态:运行 focused check、相关模块 gate、`git diff --check`,生成队列和状态页,确认只推进一个任务。
### 3A.4 里程碑完成判定
- `完成`U1-U11 全部有机器证据,检查器通过,依赖项已完成,队列生成器将该项标记为 completed。
- `兼容`:核心工作流可用,但至少一个 exact 证据类缺失;必须保留缺口、范围和下一步,不得提升 exact 声明。
- `代理`:仅保留数据/显示/导入等有限语义;必须标明不支持的原生行为和安全边界。
- `拒绝`:原生明确拒绝或 no-op记录拒绝权威、诊断、回滚和持久化不视为实现缺陷。
- `阻断`:证据缺失、结果不稳定、存在漂移或安全问题;任务保持 in_progress/pending禁止用文档结论强行关闭。
### 3A.5 依赖与并行规则
- P0/P1 是所有阶段的前置;没有基线和 oracle不接受实现“已完成”。
- P2 的几何/历史证据先于 P3 文档语义P3 先于 P4-P8 的生产工作台。
- P5 格式往返依赖 P3 对象图和 P4/P7/P8 的真实对象;未知对象只能进入显式保留或排除路径。
- P6 浏览器 UI 依赖 Facade 合同,但 UI 回归不能反向修改原生合同。
- P9 只在 P4-P8 的工作流有稳定基线后扩大矩阵;发现漂移时回退到对应功能微任务。
- P10 是最终门禁,不能以“主要功能可用”替代任一 exact 依赖。
- 机器队列维持 `maxInProgress=1`;需要并行调查时也必须先落成独立 artifact不能并行修改同一证据账本。
### 3A.6 每项任务的最小记录模板
```text
范围:一个 TypeId/命令/格式/交互状态或一个有序操作对
输入:原生版本、提交、参数、单位、对象图、资源哈希
原生证据:名义、边界、失败、取消、变异、历史、命名
浏览器证据Facade、Worker、OPFS、UI、选择、进度、释放
持久化证据save/reopen/resave、迁移、未知属性/资源
判定accepted / rejected / no-op / compatible / proxy / blocked
退出条件:可执行检查命令、预期摘要、零指标和回滚要求
变更边界只允许修改该任务的实现、oracle、检查器和报告
```
## 3B. 当前阶段的持续推进规则
当前项目仍处于 TSN 命名/历史与有序操作对闭合阶段,之后才按依赖进入 Property、Document、格式、GUI、专业工作台和平台质量阶段。每次完成一个微任务后必须
1. 从 `config/freecad-active-work-queue.json` 读取新的 `nextTask`,不得从本文、聊天记录或手工列表猜测。
2. 为刚完成任务追加原生、浏览器、持久化和回滚证据;缺一项就保持当前任务未完成。
3. 重新生成队列和状态页,检查只有一个 `in_progress`,并核对连续前缀没有跳号。
4. 按任务风险运行 focused gate达到发布点才运行完整 `./npmw run verify`。
5. 提交前检查 `git diff --check`、JSON/脚本语法、未跟踪文件和生成物哈希;提交后推送云仓库并记录提交。
6. 若原生拒绝、no-op 或代理边界是正确结果,完整保留该分类,不为了增加完成数而改变判定。
## 4. 单轮执行流程 ## 4. 单轮执行流程
1. 读取唯一 `nextTask`,确认依赖、输入 artifact 和退出条件。 1. 读取唯一 `nextTask`,确认依赖、输入 artifact 和退出条件。
@@ -2101,4 +2196,15 @@ git diff --check
- `8 -> 7 -> 8` 工具 X 偏移轨迹使体积在 `1066.6586667 -> 1041.7386667 -> 1066.6586667` 间变化X 上界从 13 变为 12 后恢复并精确恢复名义摘要、history 与语义 naming。 - `8 -> 7 -> 8` 工具 X 偏移轨迹使体积在 `1066.6586667 -> 1041.7386667 -> 1066.6586667` 间变化X 上界从 13 变为 12 后恢复并精确恢复名义摘要、history 与语义 naming。
- 上游 Chamfer naming 证据向下游传播FreeCAD FCStd save/reopen/resave 三阶段 Shape 与 naming evidence 稳定focused gate 已锁定相交融合、变异、恢复和持久化证据。 - 上游 Chamfer naming 证据向下游传播FreeCAD FCStd save/reopen/resave 三阶段 Shape 与 naming evidence 稳定focused gate 已锁定相交融合、变异、恢复和持久化证据。
### `TSN-PAIR-chamfer-cut`
`chamfer->cut` 已完成原生接受分类:
- 上游使用真实 `BRepFilletAPI_MakeChamfer` 单输入 builder对 10x10x10 Box 的全部边执行倒角,生成有效 1 Solid、26 Face、体积 `990.7413333`、面积 `571.4001834` 和 150 条原生 history。
- 下游使用真实 `BRepAlgoAPI_Cut` 双输入 builder名义结果为有效 1 Solid、31 Face、体积 `927.0613333`、面积 `604.1774417` 和 42 条原生 history。
- `4 -> 3 -> 4` 工具尺寸轨迹使体积在 `927.0613333 -> 963.9813333 -> 927.0613333` 间变化编辑态面积同步变化恢复态的几何摘要、history hash 和语义 naming 精确回到名义态。
- 上游 Chamfer naming evidence 向下游传播;下游结果因对称拓扑保持 `ambiguous`,未伪造唯一私有 tokenJSON round-trip 稳定。
- FreeCAD 1.1.1 FCStd save/reopen/resave 三阶段均保持有效 1 Solid、31 Face、63 Edge、34 Vertex、体积 `927.0613333`、面积 `604.1774417` 及 naming evidence hash `fbd3e2b6...` 一致focused ordered-pair gate 已通过。
- 机器队列已自动前移一项;新的活动任务仍以 `config/freecad-active-work-queue.json.nextTask` 为唯一来源。
后续任务必须从机器队列的最新 `nextTask` 继续,不在本文手工维护任务名称。 后续任务必须从机器队列的最新 `nextTask` 继续,不在本文手工维护任务名称。