@tea-agent/loop-agent 0.42.0-next.7 → 0.42.0-next.8
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/CHANGELOG.md +17 -0
- package/dist/build-stamp.json +3 -3
- package/dist/executors/shell-executor.js +3 -1
- package/dist/worker/console/chat/pi-runtime.js +20 -4
- package/dist/worker/console/chat/routes.js +44 -22
- package/dist/worker/console/chat/session-store.js +16 -0
- package/dist/worker/console/server.js +19 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BQBHX31n.js → abnfDiagram-N423BO3Z-C6n9_m0P.js} +1 -1
- package/dist/worker/console/static/assets/{arc-MSJ199eR.js → arc-DQh-IfZ1.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DiRq2qMX.js → architectureDiagram-T3A2C74G-54NnrwUC.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-rlaxFOvi.js → blockDiagram-VBNYF7ZC-pivRALGK.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BSpi9ump.js → c4Diagram-5PPSVZJV-BR7OV2NJ.js} +1 -1
- package/dist/worker/console/static/assets/channel-DsZxrgqe.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BN8VNeh5.js → chunk-2GRJ4B5K-B1Aq6BcK.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CDa1OVRA.js → chunk-2Q5K7J3B-CbdU0rjo.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-zxMQ5b8E.js → chunk-5RXB4S5H-Dqi2GJeD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-XTqNlzH4.js → chunk-5VM5RSS4-BYn1Hu4R.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DpZQfFTq.js → chunk-6Q2QTUOP-DRLjDL0k.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DPv743zu.js → chunk-GF5L2VYU-CY9Xx2jV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CFsCFoF4.js → chunk-JWPE2WC7-BNCZs7_z.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CV__aJTt.js → chunk-KBJHAD2P-DZ8AStLL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-BshKlNir.js → chunk-RYQCIY6F-DsxjYrzz.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-B-A3ErBP.js → chunk-XXDRQBXY-Ddx1KC1l.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-25xj8928.js → cose-bilkent-JH36ORCC-W9TveCnK.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DFmv38cl.js → cynefin-VYW2F7L2-l9j_ztZH.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-hReuWDQP.js → cynefinDiagram-MW4NZA55-BMJMKi4G.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C9X3YPUI.js → dagre-VZM6K2ZE-Bb4mG9pH.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CAVmqjg9.js → diagram-7IWD3JNH-BMgL_Qv0.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DYNlLsIm.js → diagram-B4RE2ZJO-Bvo2T4OQ.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BP5mlbfb.js → diagram-LBJQPF4R-_5kWRN9c.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-KWOHK5ZM.js → diagram-Q27KOJAE-DqdMrltM.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-D25Ro5y-.js → diagram-UB23O5K3-CDDYsNkp.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-kLaahtbh.js → ebnfDiagram-BXEA7PRR-c4nfnZUV.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-1tgKuLez.js → erDiagram-JOGREHBK-DnkUcNMs.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DARr-Cvn.js → flowDiagram-UKHOOZJN-Dz0KGLZE.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BgwjBv_V.js → ganttDiagram-PKOTCBZU-Cj1t1uka.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DneIlYSI.js → gitGraphDiagram-DS77QQ5N-tp2FrBHd.js} +1 -1
- package/dist/worker/console/static/assets/{index-Dkv0ezuE.js → index-D9Sc0f0y.js} +84 -84
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DA7ATqY6.js → infoDiagram-6WML65LV-DQwJS8DH.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DnOS-E1Z.js → ishikawaDiagram-WSZJBQD7-eimCQWD4.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-C8JnwJ_s.js → journeyDiagram-NVQOT4AX-Dd9Gbwgv.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Cw0_OjEC.js → kanban-definition-27J2QSJJ-Qve3cyft.js} +1 -1
- package/dist/worker/console/static/assets/{linear-mrxq3fyn.js → linear-UXKSe36Z.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Bagus6zF.js → mermaid.core-CWhj4JXN.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-Bf95bLuW.js → mindmap-definition-FAOFIHXS-yTcwf4SJ.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BvtUW6w2.js → pegDiagram-VL7TDLO6-_7qToaZP.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CbWtT-Ry.js → pieDiagram-7S7Q4E2Y-CXmrKmDm.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-ctnxxIdD.js → quadrantDiagram-CIZ2JOQS-hMmrJyaS.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BZ7Z3X0-.js → railroadDiagram-AXF67PYL-BXw8tCBe.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BMX1xnIy.js → requirementDiagram-LRYGKXZP-BmjgnLZl.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-MShOQznz.js → sankeyDiagram-W5VNT64P-Bj77SPpN.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CTGOazaB.js → sequenceDiagram-SI44F4Z6-B2FDVysL.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-zGbfM4nV.js → sizeCapture-X5ZJPWSS-UChNY9ZZ.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Brf8Wwzr.js → stateDiagram-OKZ733FA-wbAEkbd7.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CvNw3ZqF.js → swimlanes-SLNWSIFB-DCEkU8HR.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-z2LyZpDt.js → timeline-definition-Z64GVDOM-BGp7H06U.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-Bc_coprH.js → vennDiagram-T6HMQDX7-DsCIQuWR.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-M1NSmWq-.js → wardleyDiagram-T6FBY63Y-cRZLP4Xe.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-Ct0rjGXW.js → xychartDiagram-ELKLHX3M-CP0xLR9Y.js} +1 -1
- package/dist/worker/console/static/index.html +1 -1
- package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +31 -2
- package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -3
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +40 -9
- package/dist/workflows/dag/backend-test-scenario-param.js +126 -19
- package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +7 -3
- package/dist/workflows/dag/init-hybrid.js +23 -3
- package/package.json +1 -1
- package/dist/worker/console/static/assets/channel-BdTaMnYP.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cid7CSRK.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cid7CSRK.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CIkoculx.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-pJ-V5EdT.js +0 -8
|
@@ -307,14 +307,18 @@ export function classifyBackendTestCorrespondenceRepair(facts, layout) {
|
|
|
307
307
|
const markdownReasons = [];
|
|
308
308
|
const pytestReasons = [];
|
|
309
309
|
const counts = bindingCounts(entry);
|
|
310
|
-
if (entry.
|
|
311
|
-
|
|
310
|
+
if (entry.cardinality !== "0:1" &&
|
|
311
|
+
entry.testPoints.some((point) => (counts.get(point) ?? 0) !== 1)) {
|
|
312
|
+
// Only ### 测试点 inventory must be bound exactly once. Extra
|
|
313
|
+
// automation-mapping TPs (typical: 横切 listed only under 跨切证据)
|
|
314
|
+
// are pytest-owned docstring/repair defects, not Markdown BLOCKED.
|
|
312
315
|
markdownReasons.push("markdown-test-point-binding-invalid");
|
|
313
316
|
}
|
|
314
317
|
if (entry.declaredScript && entry.declaredScript !== entry.expectedScript) {
|
|
315
318
|
markdownReasons.push("markdown-script-declaration-invalid");
|
|
316
319
|
}
|
|
317
|
-
|
|
320
|
+
const evidenceOnly = isEvidenceOnlyCorrespondenceEntry(entry);
|
|
321
|
+
if (!evidenceOnly && entry.cardinality === "1:0")
|
|
318
322
|
pytestReasons.push("missing-pytest-symbol");
|
|
319
323
|
if (entry.cardinality === "1:N")
|
|
320
324
|
pytestReasons.push("multiple-pytest-symbols");
|
|
@@ -325,12 +329,15 @@ export function classifyBackendTestCorrespondenceRepair(facts, layout) {
|
|
|
325
329
|
if (entry.declaredPrimarySymbol && entry.pytestSymbols.some((symbol) => symbol !== entry.declaredPrimarySymbol)) {
|
|
326
330
|
pytestReasons.push("pytest-primary-symbol-mismatch");
|
|
327
331
|
}
|
|
328
|
-
if (entry.variantTestPoints.some((point) => !entry.parameterIds.includes(point)))
|
|
332
|
+
if (!evidenceOnly && entry.variantTestPoints.some((point) => !entry.parameterIds.includes(point))) {
|
|
329
333
|
pytestReasons.push("variant-parameter-missing");
|
|
330
|
-
|
|
334
|
+
}
|
|
335
|
+
if (!evidenceOnly && entry.assertionTestPoints.some((point) => !entry.mappedTestPoints.includes(point))) {
|
|
331
336
|
pytestReasons.push("assertion-binding-missing");
|
|
332
|
-
|
|
337
|
+
}
|
|
338
|
+
if (!evidenceOnly && entry.crossCuttingTestPoints.some((point) => !entry.mappedTestPoints.includes(point))) {
|
|
333
339
|
pytestReasons.push("cross-cutting-binding-missing");
|
|
340
|
+
}
|
|
334
341
|
if (entry.parameterIds.some((point) => !entry.testPoints.includes(point)))
|
|
335
342
|
pytestReasons.push("pytest-test-point-binding-extra");
|
|
336
343
|
if (entry.payloadAssessment.status === "UNSAFE")
|
|
@@ -339,7 +346,7 @@ export function classifyBackendTestCorrespondenceRepair(facts, layout) {
|
|
|
339
346
|
!["TEST_POINT_BINDING_DUPLICATE", "PAYLOAD_CONTRACT_UNAVAILABLE"].includes(entry.status)) {
|
|
340
347
|
pytestReasons.push(`correspondence-${entry.status.toLowerCase().replaceAll("_", "-")}`);
|
|
341
348
|
}
|
|
342
|
-
if (entry.status === "PAYLOAD_CONTRACT_UNAVAILABLE") {
|
|
349
|
+
if (entry.status === "PAYLOAD_CONTRACT_UNAVAILABLE" && entry.cardinality !== "0:1") {
|
|
343
350
|
markdownReasons.push("markdown-payload-contract-unavailable");
|
|
344
351
|
}
|
|
345
352
|
const paths = orderedUnique([entry.expectedScript, ...entry.actualScripts].filter(safePytestPath));
|
|
@@ -796,6 +803,16 @@ function isEvidenceOnlyMarkdownCase(testCase) {
|
|
|
796
803
|
!hasVariant &&
|
|
797
804
|
hasNonVariant);
|
|
798
805
|
}
|
|
806
|
+
function isEvidenceOnlyCorrespondenceEntry(entry) {
|
|
807
|
+
return (entry.status === "EXACT_1_TO_1" &&
|
|
808
|
+
entry.cardinality === "1:0" &&
|
|
809
|
+
!entry.declaredScript &&
|
|
810
|
+
!entry.declaredPrimarySymbol &&
|
|
811
|
+
entry.variantTestPoints.length === 0 &&
|
|
812
|
+
(entry.assertionTestPoints.length > 0 || entry.crossCuttingTestPoints.length > 0) &&
|
|
813
|
+
entry.pytestSymbols.length === 0 &&
|
|
814
|
+
entry.actualScripts.length === 0);
|
|
815
|
+
}
|
|
799
816
|
function payloadContract(body) {
|
|
800
817
|
const contractValue = automationLabelBody(body, ["Payload Contract", "载荷合同"]);
|
|
801
818
|
const requiredValue = automationLabelBody(body, ["Payload Required Paths", "载荷必需路径"]);
|
|
@@ -804,7 +821,7 @@ function payloadContract(body) {
|
|
|
804
821
|
const tokens = (value) => orderedUnique(value
|
|
805
822
|
.split(/[,;,;]/)
|
|
806
823
|
.map((item) => item.trim())
|
|
807
|
-
.filter((item) => !/^(?:none|无|not-applicable|n\/a)$/i.test(item))
|
|
824
|
+
.filter((item) => !/^(?:none|无|not-applicable|n\/a|true|false)$/i.test(item))
|
|
808
825
|
.filter((item) => /^[A-Za-z_][A-Za-z0-9_.]*$/.test(item)));
|
|
809
826
|
const enumValues = {};
|
|
810
827
|
for (const assignment of enumValue.split(/[,;,;]/).map((item) => item.trim()).filter(Boolean)) {
|
|
@@ -1705,6 +1722,20 @@ def is_direct_transport_call(node):
|
|
|
1705
1722
|
if isinstance(current,ast.Name): receiver_tokens.append(current.id.lower())
|
|
1706
1723
|
return any(token in {'requests','httpx','client','session','api_client','http_client'} or token.endswith('_client') for token in receiver_tokens)
|
|
1707
1724
|
|
|
1725
|
+
HTTP_METHODS={'GET','POST','PUT','PATCH','DELETE','HEAD','OPTIONS'}
|
|
1726
|
+
HTTP_FIXTURE_NAMES={'http','http_client','client','api_client','session'}
|
|
1727
|
+
|
|
1728
|
+
def is_fixture_http_call(node):
|
|
1729
|
+
# R12: http("POST", path, json_body={"title": title}) is a Name-call fixture, not client.post.
|
|
1730
|
+
# R15: http_client(operation, path, json_body={...}) still transports when operation is a parametrized method.
|
|
1731
|
+
if not isinstance(node,ast.Call) or not isinstance(node.func,ast.Name) or not node.args: return False
|
|
1732
|
+
first=node.args[0]
|
|
1733
|
+
if isinstance(first,ast.Constant) and isinstance(first.value,str) and first.value.upper() in HTTP_METHODS:
|
|
1734
|
+
return True
|
|
1735
|
+
func=node.func.id.lower()
|
|
1736
|
+
httpish=func in HTTP_FIXTURE_NAMES or func.endswith('_client')
|
|
1737
|
+
return httpish and isinstance(first,ast.Name)
|
|
1738
|
+
|
|
1708
1739
|
def payload_nodes(fn):
|
|
1709
1740
|
result=[]
|
|
1710
1741
|
assigned={}
|
|
@@ -1727,7 +1758,7 @@ def payload_nodes(fn):
|
|
|
1727
1758
|
# Observe local payload/body variables only when they flow into a direct
|
|
1728
1759
|
# HTTP transport call. Arbitrary setup variables are not target requests.
|
|
1729
1760
|
for node in ast.walk(fn):
|
|
1730
|
-
if not is_direct_transport_call(node): continue
|
|
1761
|
+
if not (is_direct_transport_call(node) or is_fixture_http_call(node)): continue
|
|
1731
1762
|
for keyword in node.keywords:
|
|
1732
1763
|
if keyword.arg in PAYLOAD_NAMES: result.extend(payload_expr_nodes(keyword.value,assigned,parameters))
|
|
1733
1764
|
return result
|
|
@@ -59,6 +59,7 @@ const CLOSED_SET = new Set([
|
|
|
59
59
|
"null",
|
|
60
60
|
"min-1",
|
|
61
61
|
"min",
|
|
62
|
+
"max-1",
|
|
62
63
|
"max",
|
|
63
64
|
"max+1",
|
|
64
65
|
"pattern-invalid",
|
|
@@ -155,14 +156,19 @@ function parseScenarioBoundToken(raw) {
|
|
|
155
156
|
function parseScenarioIntentLine(line) {
|
|
156
157
|
// Target may contain commas (body.title,body.status). Intent may contain `|` / spaces.
|
|
157
158
|
// Stop field values only at known trailing keys or end-of-line.
|
|
158
|
-
const
|
|
159
|
+
const unfenced = line
|
|
160
|
+
.trim()
|
|
161
|
+
.replace(/^[-*+]\s+/, "")
|
|
162
|
+
.replace(/^`(.+)`$/, "$1")
|
|
163
|
+
.trim();
|
|
164
|
+
const precise = /场景意图\s*[::]\s*(TP-[A-Z0-9-]+)\s*[|;]\s*operation\s*=\s*([^;\n]+?)\s*[|;]\s*target\s*=\s*([^;\n]+?)\s*[|;]\s*intent\s*=\s*([^;\n]+?)(?:\s*[|;]\s*bound\s*=\s*([^\s;`|\n]+))?(?:\s*[|;]\s*example\s*=\s*([^;\n]*))?(?:\s*[|;]\s*expectedCode\s*=\s*[^;\n]+)?\s*$/i.exec(unfenced);
|
|
159
165
|
if (precise) {
|
|
160
166
|
return {
|
|
161
167
|
tpId: precise[1],
|
|
162
168
|
rawIntent: (precise[4] ?? "unknown").trim(),
|
|
163
169
|
target: precise[3]?.trim(),
|
|
164
170
|
bound: parseScenarioBoundToken(precise[5]),
|
|
165
|
-
example: precise[6]
|
|
171
|
+
example: precise[6] === undefined ? undefined : precise[6].trim(),
|
|
166
172
|
legacy: false,
|
|
167
173
|
};
|
|
168
174
|
}
|
|
@@ -192,6 +198,53 @@ function resolveScenarioIntentField(target, field) {
|
|
|
192
198
|
target.split(/\s*,\s*/)[0];
|
|
193
199
|
return preferred?.split(".").at(-1);
|
|
194
200
|
}
|
|
201
|
+
function normalizeLengthIntentAlias(rawIntent) {
|
|
202
|
+
const lower = rawIntent.trim().toLowerCase();
|
|
203
|
+
if (lower === "max-1" || lower === "maxminus1" || lower === "max_minus_1") {
|
|
204
|
+
return "max-1";
|
|
205
|
+
}
|
|
206
|
+
if (lower === "min-1" || lower === "minminus1" || lower === "min_minus_1") {
|
|
207
|
+
return "min-1";
|
|
208
|
+
}
|
|
209
|
+
return lower;
|
|
210
|
+
}
|
|
211
|
+
function proseLengthBound(haystack, field) {
|
|
212
|
+
const fieldAlt = field ? field.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : undefined;
|
|
213
|
+
const scoped = fieldAlt ? new RegExp(`${fieldAlt}[^\\n]{0,160}`, "ig") : undefined;
|
|
214
|
+
const windows = [];
|
|
215
|
+
if (scoped) {
|
|
216
|
+
for (const match of haystack.matchAll(scoped)) {
|
|
217
|
+
windows.push(match[0] ?? "");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
windows.push(haystack);
|
|
221
|
+
let max;
|
|
222
|
+
let min;
|
|
223
|
+
const proseMaxRe = /(\d{1,4})\s*个字符的最大/i;
|
|
224
|
+
for (const window of windows) {
|
|
225
|
+
const proseMax = proseMaxRe.exec(window);
|
|
226
|
+
if (proseMax && max === undefined)
|
|
227
|
+
max = Number(proseMax[1]);
|
|
228
|
+
const nums = [...window.matchAll(/(?<![+\-])\d{1,4}/g)]
|
|
229
|
+
.map((item) => Number(item[0]))
|
|
230
|
+
.filter((item) => Number.isFinite(item));
|
|
231
|
+
for (let i = 0; i + 2 < nums.length; i += 1) {
|
|
232
|
+
const a = nums[i];
|
|
233
|
+
const b = nums[i + 1];
|
|
234
|
+
const c = nums[i + 2];
|
|
235
|
+
// Prefer n-1,n,n+1 around a documented max (100/2000), not 0,1,99 or 99,100,101's first two plus leftover.
|
|
236
|
+
if (b === a + 1 && c === b + 1 && b >= 10) {
|
|
237
|
+
max ??= b;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (nums.includes(0) && nums.includes(1))
|
|
242
|
+
min ??= 1;
|
|
243
|
+
if (max !== undefined && min !== undefined)
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
return { max, min };
|
|
247
|
+
}
|
|
195
248
|
function resolveCustomLiteralIntent(rawIntent, field) {
|
|
196
249
|
const lower = rawIntent.toLowerCase();
|
|
197
250
|
if (!lower.startsWith("custom-literal:")) {
|
|
@@ -250,7 +303,7 @@ export function inferScenarioParamIntent(input) {
|
|
|
250
303
|
const field = resolveScenarioIntentField(machine.target, machine.field);
|
|
251
304
|
const rawIntent = machine.rawIntent.trim();
|
|
252
305
|
const example = machine.example;
|
|
253
|
-
const lower = rawIntent
|
|
306
|
+
const lower = normalizeLengthIntentAlias(rawIntent);
|
|
254
307
|
let normalizedIntent = lower === "nominal-operation" ? "nominal" : lower;
|
|
255
308
|
// Writers sometimes mark a short "typical" length sample as min/max with bound=LEN<nominal>.
|
|
256
309
|
// Treat that as nominal so a short helper/string is not forced to equal maxLength.
|
|
@@ -273,16 +326,16 @@ export function inferScenarioParamIntent(input) {
|
|
|
273
326
|
new RegExp(`(?:minLength|min(?:imum)?|下限|最小长度)\\s*[=::]?\\s*(\\d{1,4})[^\\n]{0,80}?${fieldEsc}`, "i").exec(haystack) ??
|
|
274
327
|
/(?:minLength|min(?:imum)?|下限|最小长度)\s*[=::]?\s*(\d{1,4})/i.exec(haystack)
|
|
275
328
|
: /(?:minLength|min(?:imum)?|下限|最小长度)\s*[=::]?\s*(\d{1,4})/i.exec(haystack);
|
|
329
|
+
const proseBound = proseLengthBound(haystack, field);
|
|
276
330
|
const prefersMinBound = normalizedIntent === "min" ||
|
|
277
331
|
normalizedIntent === "min-1" ||
|
|
278
332
|
/(?:MIN|LEN1|LENGTH-?1|最短|最小)/i.test(tpId);
|
|
279
|
-
const boundFromBody = prefersMinBound
|
|
280
|
-
? minBoundFromBody ?? maxBoundFromBody
|
|
281
|
-
: maxBoundFromBody ?? minBoundFromBody;
|
|
333
|
+
const boundFromBody = prefersMinBound ? minBoundFromBody : maxBoundFromBody ?? minBoundFromBody;
|
|
282
334
|
const boundFromTp = /(?:LEN(?:GTH)?|长度)[_-]?(\d{1,5})(?:[_-]|$)/i.exec(tpId) ??
|
|
283
|
-
/(?:MIN|MAX)[_-]
|
|
335
|
+
/(?:MIN|MAX)[_-](\d{2,5})(?:[_-]|$)/i.exec(tpId);
|
|
284
336
|
const lengthIntent = normalizedIntent === "min" ||
|
|
285
337
|
normalizedIntent === "min-1" ||
|
|
338
|
+
normalizedIntent === "max-1" ||
|
|
286
339
|
normalizedIntent === "max" ||
|
|
287
340
|
normalizedIntent === "max+1";
|
|
288
341
|
// Length intents: explicit machine bound >0, then TP-encoded LEN1/LEN100,
|
|
@@ -294,13 +347,35 @@ export function inferScenarioParamIntent(input) {
|
|
|
294
347
|
: normalizedIntent === "max+1"
|
|
295
348
|
? Math.max(0, boundFromTpNumber - 1)
|
|
296
349
|
: boundFromTpNumber;
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
350
|
+
const boundFromProse = prefersMinBound ? proseBound.min : proseBound.max;
|
|
351
|
+
const offsetIntent = normalizedIntent === "max-1" ||
|
|
352
|
+
normalizedIntent === "max+1" ||
|
|
353
|
+
normalizedIntent === "min-1";
|
|
354
|
+
const contractBound = boundFromBody
|
|
355
|
+
? Number(boundFromBody[1])
|
|
356
|
+
: boundFromProse !== undefined && boundFromProse >= 0
|
|
357
|
+
? boundFromProse
|
|
358
|
+
: undefined;
|
|
359
|
+
const machineBoundPositive = typeof machine.bound === "number" && Number.isFinite(machine.bound) && machine.bound > 0;
|
|
360
|
+
const minLengthFromBody = prefersMinBound && minBoundFromBody ? Number(minBoundFromBody[1]) : undefined;
|
|
361
|
+
const machineBoundZeroMin = prefersMinBound &&
|
|
362
|
+
machine.bound === 0 &&
|
|
363
|
+
minLengthFromBody === undefined &&
|
|
364
|
+
boundFromTpNormalized === undefined;
|
|
365
|
+
const bound = offsetIntent && contractBound !== undefined && contractBound > 0
|
|
366
|
+
? contractBound
|
|
367
|
+
: machineBoundPositive && !offsetIntent
|
|
368
|
+
? machine.bound
|
|
369
|
+
: lengthIntent && boundFromTpNormalized !== undefined
|
|
370
|
+
? boundFromTpNormalized
|
|
371
|
+
: minLengthFromBody !== undefined && minLengthFromBody >= 0
|
|
372
|
+
? minLengthFromBody
|
|
373
|
+
: machineBoundZeroMin
|
|
374
|
+
? 0
|
|
375
|
+
: contractBound !== undefined &&
|
|
376
|
+
(prefersMinBound ? contractBound >= 0 : contractBound > 0)
|
|
377
|
+
? contractBound
|
|
378
|
+
: undefined;
|
|
304
379
|
if (rawIntent.toLowerCase().startsWith("custom-literal:")) {
|
|
305
380
|
const resolved = resolveCustomLiteralIntent(rawIntent, field);
|
|
306
381
|
const rawLiteral = rawIntent.slice(rawIntent.indexOf(":") + 1).trim();
|
|
@@ -387,13 +462,15 @@ export function inferScenarioParamIntent(input) {
|
|
|
387
462
|
const boundMatch = fieldScopedBound ??
|
|
388
463
|
/(?:maxLength|max(?:imum)?|上限|最大长度)\s*[=::]?\s*(\d{1,4})/i.exec(`${caseBody}\n${dataSection}`) ??
|
|
389
464
|
/(?:minLength|min(?:imum)?|下限|最小长度)\s*[=::]?\s*(\d{1,4})/i.exec(`${caseBody}\n${dataSection}`);
|
|
390
|
-
const
|
|
465
|
+
const proseBound = proseLengthBound(`${caseBody}
|
|
466
|
+
${dataSection}`, field);
|
|
467
|
+
const bound = boundMatch ? Number(boundMatch[1]) : proseBound.max ?? proseBound.min;
|
|
391
468
|
const exampleMatch = /(?:反例|invalid example|example)\s*[=::]\s*[`"]?([^`"\n]+)[`"]?/i.exec(caseBody);
|
|
392
469
|
const example = exampleMatch?.[1]?.trim();
|
|
393
470
|
if (/EMPTY|空白|空串|空字符串/.test(upper) || /空字符串|空串|empty string/i.test(caseBody)) {
|
|
394
471
|
return { intent: "empty", field, bound, example, intentSource: "tp-fallback" };
|
|
395
472
|
}
|
|
396
|
-
if (/MISSING|缺省|缺失|省略/.test(upper) || /缺少字段|缺失字段|missing field/i.test(caseBody)) {
|
|
473
|
+
if (/MISSING|OMITTED|\bOMIT\b|缺省|缺失|省略/.test(upper) || /缺少字段|缺失字段|missing field/i.test(caseBody)) {
|
|
397
474
|
return { intent: "missing", field, bound, example, intentSource: "tp-fallback" };
|
|
398
475
|
}
|
|
399
476
|
if (/\bNULL\b|空值/.test(upper) || /\bnull\b/i.test(caseBody)) {
|
|
@@ -402,6 +479,9 @@ export function inferScenarioParamIntent(input) {
|
|
|
402
479
|
if (/MAX[-_]?PLUS[-_]?1|MAX\+1|超长|越界|LENGTH[-_]?OVER|OVER[-_]?LENGTH|OVER/.test(upper)) {
|
|
403
480
|
return { intent: "max+1", field, bound, example, intentSource: "tp-fallback" };
|
|
404
481
|
}
|
|
482
|
+
if (/MAX[-_]1(?:$|[-_])|max-1/i.test(upper)) {
|
|
483
|
+
return { intent: "max-1", field, bound, example, intentSource: "tp-fallback" };
|
|
484
|
+
}
|
|
405
485
|
if (/\bMAX\b|最大/.test(upper)) {
|
|
406
486
|
return { intent: "max", field, bound, example, intentSource: "tp-fallback" };
|
|
407
487
|
}
|
|
@@ -425,7 +505,7 @@ export function inferScenarioParamIntent(input) {
|
|
|
425
505
|
}
|
|
426
506
|
return { intent: "unknown", field, bound, example, intentSource: "tp-fallback" };
|
|
427
507
|
}
|
|
428
|
-
const OMIT_SENTINEL_RE = /^(?:_OMIT|OMIT|MISSING|Ellipsis|\.\.\.)\b/;
|
|
508
|
+
const OMIT_SENTINEL_RE = /^(?:_OMIT|_MISSING|OMIT|MISSING|Ellipsis|\.\.\.)\b/;
|
|
429
509
|
const HTTP_EXPECTED_CODE_RE = /^(?:2xx|3xx|4xx|5xx|[1-5]\d{2}|HTTP_[1-5]\d{2})$/i;
|
|
430
510
|
/** Semantic custom-literal tokens that describe a value class, not a raw string. */
|
|
431
511
|
function isWhitespaceSemanticLiteral(token) {
|
|
@@ -774,6 +854,10 @@ export function observeParamFeatures(block, field, constants, parameterNames) {
|
|
|
774
854
|
(/["'][A-Za-z_][A-Za-z0-9_]*["']\s*:/.test(dictText) ||
|
|
775
855
|
/(?:payload|body|data|json)\s*=/.test(block));
|
|
776
856
|
if (dictLooksLikePayload && !field) {
|
|
857
|
+
const emptyField = /["']([A-Za-z_][A-Za-z0-9_.]*)["']\s*:\s*(?:""|'')/.exec(dictText);
|
|
858
|
+
if (emptyField?.[1]) {
|
|
859
|
+
return { kind: "empty-string", text: '""', length: 0, literal: "" };
|
|
860
|
+
}
|
|
777
861
|
return { kind: "dict", text: dictText.replace(/\s+/g, " ").slice(0, 160) };
|
|
778
862
|
}
|
|
779
863
|
if (dictLooksLikePayload && field) {
|
|
@@ -803,7 +887,7 @@ export function observeParamFeatures(block, field, constants, parameterNames) {
|
|
|
803
887
|
};
|
|
804
888
|
}
|
|
805
889
|
// Bare omit sentinel before id=...
|
|
806
|
-
if (/pytest\.param\s*\(\s*(?:_OMIT|OMIT|MISSING|\.\.\.|Ellipsis)\s*(?:,|\)|$)/.test(block)) {
|
|
890
|
+
if (/pytest\.param\s*\(\s*(?:_OMIT|_MISSING|OMIT|MISSING|\.\.\.|Ellipsis)\s*(?:,|\)|$)/.test(block)) {
|
|
807
891
|
return {
|
|
808
892
|
kind: "missing-key",
|
|
809
893
|
text: field ? `missing:${field}` : "missing-key",
|
|
@@ -865,7 +949,7 @@ export function observeParamFeatures(block, field, constants, parameterNames) {
|
|
|
865
949
|
if (paramOpen) {
|
|
866
950
|
const afterOpen = block.slice((paramOpen.index ?? 0) + paramOpen[0].length);
|
|
867
951
|
const positionalTokens = [];
|
|
868
|
-
const tokenRe = /\s*(_OMIT|OMIT|MISSING|Ellipsis|\.\.\.|None|True|False|-?\d+(?:\.\d+)?|"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|(?:["'][^"'\\]["']\s*\*\s*(?:\d{1,5}|[A-Za-z_][A-Za-z0-9_]*))|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[A-Za-z_][A-Za-z0-9_]*)\s*(?:,|\)|$)/g;
|
|
952
|
+
const tokenRe = /\s*(_OMIT|_MISSING|OMIT|MISSING|Ellipsis|\.\.\.|None|True|False|-?\d+(?:\.\d+)?|"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|(?:["'][^"'\\]["']\s*\*\s*(?:\d{1,5}|[A-Za-z_][A-Za-z0-9_]*))|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[A-Za-z_][A-Za-z0-9_]*)\s*(?:,|\)|$)/g;
|
|
869
953
|
let match;
|
|
870
954
|
while ((match = tokenRe.exec(afterOpen)) !== null) {
|
|
871
955
|
const between = afterOpen.slice(0, match.index);
|
|
@@ -1008,12 +1092,25 @@ function compareIntent(intent, observed, bound, example, field) {
|
|
|
1008
1092
|
return "MATCH";
|
|
1009
1093
|
return len !== undefined ? "MISMATCH" : "UNDETERMINED";
|
|
1010
1094
|
}
|
|
1095
|
+
case "max-1":
|
|
1096
|
+
if (bound === undefined)
|
|
1097
|
+
return "UNDETERMINED";
|
|
1098
|
+
{
|
|
1099
|
+
const expectedMax1 = Math.max(0, bound - 1);
|
|
1100
|
+
const len = observedLengthValue(observed);
|
|
1101
|
+
if (len === expectedMax1)
|
|
1102
|
+
return "MATCH";
|
|
1103
|
+
return len !== undefined ? "MISMATCH" : "UNDETERMINED";
|
|
1104
|
+
}
|
|
1011
1105
|
case "min":
|
|
1012
1106
|
if (bound === undefined)
|
|
1013
1107
|
return "UNDETERMINED";
|
|
1014
1108
|
{
|
|
1015
1109
|
if (isMinLengthHelper(observed))
|
|
1016
1110
|
return "MATCH";
|
|
1111
|
+
// Optional min=0 may omit the key instead of sending "".
|
|
1112
|
+
if (bound === 0 && observed.kind === "missing-key")
|
|
1113
|
+
return "MATCH";
|
|
1017
1114
|
const len = observedLengthValue(observed);
|
|
1018
1115
|
if (len === bound)
|
|
1019
1116
|
return "MATCH";
|
|
@@ -1167,6 +1264,10 @@ function suggestedFixFor(intent, field, bound, example) {
|
|
|
1167
1264
|
return field && bound !== undefined
|
|
1168
1265
|
? `set ${field}="x"*${bound + 1}`
|
|
1169
1266
|
: undefined;
|
|
1267
|
+
case "max-1":
|
|
1268
|
+
return field && bound !== undefined
|
|
1269
|
+
? `set ${field}="x"*${Math.max(0, bound - 1)}`
|
|
1270
|
+
: undefined;
|
|
1170
1271
|
case "min":
|
|
1171
1272
|
return field && bound !== undefined
|
|
1172
1273
|
? `set ${field}="x"*${bound}`
|
|
@@ -1720,6 +1821,12 @@ export function deterministicRewriteScenarioParam(input) {
|
|
|
1720
1821
|
}
|
|
1721
1822
|
valueLiteral = `"x" * ${bound + 1}`;
|
|
1722
1823
|
break;
|
|
1824
|
+
case "max-1":
|
|
1825
|
+
if (bound === undefined) {
|
|
1826
|
+
return { ok: false, source: input.source, detail: "missing bound for max-1" };
|
|
1827
|
+
}
|
|
1828
|
+
valueLiteral = `"x" * ${Math.max(0, bound - 1)}`;
|
|
1829
|
+
break;
|
|
1723
1830
|
case "min-1":
|
|
1724
1831
|
if (bound === undefined) {
|
|
1725
1832
|
return { ok: false, source: input.source, detail: "missing bound for min-1" };
|
|
@@ -43,6 +43,67 @@ function splitCells(raw) {
|
|
|
43
43
|
.map((cell) => cell.trim())
|
|
44
44
|
.filter((cell, index, cells) => !(cell === "" && (index === 0 || index === cells.length - 1)));
|
|
45
45
|
}
|
|
46
|
+
function isExactSlotInventory(extras, partitionId) {
|
|
47
|
+
const tokens = extras
|
|
48
|
+
.join(";")
|
|
49
|
+
.split(/[;,,;]/)
|
|
50
|
+
.map((token) => token.trim().replace(/`/g, ""))
|
|
51
|
+
.filter((token) => token.length > 0);
|
|
52
|
+
if (tokens.length === 0)
|
|
53
|
+
return false;
|
|
54
|
+
const prefix = `TP-${partitionId.trim().toUpperCase()}-`;
|
|
55
|
+
return tokens.every((token) => /^TP-[A-Z0-9-]+$/i.test(token) &&
|
|
56
|
+
(token.toUpperCase().startsWith(prefix) || token.toUpperCase().startsWith("TP-SP-")));
|
|
57
|
+
}
|
|
58
|
+
function trimExactSlotInventoryCells(cells) {
|
|
59
|
+
if (cells.length <= 7)
|
|
60
|
+
return { cells, trimmed: false };
|
|
61
|
+
const partitionId = cells[0] ?? "";
|
|
62
|
+
if (!isExactSlotInventory(cells.slice(7), partitionId)) {
|
|
63
|
+
return { cells, trimmed: false };
|
|
64
|
+
}
|
|
65
|
+
return { cells: cells.slice(0, 7), trimmed: true };
|
|
66
|
+
}
|
|
67
|
+
export function canonicalizeScenarioPartitionTable(markdown) {
|
|
68
|
+
const lines = markdown.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
|
69
|
+
const headingIndex = lines.findIndex((line) => PARTITION_HEADING.test(line.trim()));
|
|
70
|
+
if (headingIndex === -1) {
|
|
71
|
+
return { markdown: lines.join("\n"), changed: false, trimmedRows: 0 };
|
|
72
|
+
}
|
|
73
|
+
let headerIndex = -1;
|
|
74
|
+
let sectionEnd = lines.length;
|
|
75
|
+
for (let i = headingIndex + 1; i < lines.length; i++) {
|
|
76
|
+
if (/^##\s+/.test(lines[i].trim()) && !PARTITION_HEADING.test(lines[i].trim())) {
|
|
77
|
+
sectionEnd = i;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
if (PARTITION_TABLE_HEADER.test(lines[i])) {
|
|
81
|
+
headerIndex = i;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (headerIndex === -1) {
|
|
85
|
+
return { markdown: lines.join("\n"), changed: false, trimmedRows: 0 };
|
|
86
|
+
}
|
|
87
|
+
let trimmedRows = 0;
|
|
88
|
+
for (let i = headerIndex + 2; i < sectionEnd; i++) {
|
|
89
|
+
const line = lines[i];
|
|
90
|
+
if (!TABLE_ROW.test(line)) {
|
|
91
|
+
if (/^##\s+/.test(line) || /^#\s+/.test(line))
|
|
92
|
+
break;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const { cells, trimmed } = trimExactSlotInventoryCells(splitCells(line));
|
|
96
|
+
if (!trimmed)
|
|
97
|
+
continue;
|
|
98
|
+
lines[i] = `| ${cells.join(" | ")} |`;
|
|
99
|
+
trimmedRows += 1;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
markdown: lines.join("\n"),
|
|
103
|
+
changed: trimmedRows > 0,
|
|
104
|
+
trimmedRows,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
46
107
|
/**
|
|
47
108
|
* Parse the `## Scenario Partitions` section of the README into declared
|
|
48
109
|
* partitions. Returns { partitions, issues }; issues are fail-closed parse
|
|
@@ -77,7 +138,7 @@ export function parseScenarioPartitions(readmeMarkdown) {
|
|
|
77
138
|
break;
|
|
78
139
|
continue;
|
|
79
140
|
}
|
|
80
|
-
const cells = splitCells(line);
|
|
141
|
+
const { cells } = trimExactSlotInventoryCells(splitCells(line));
|
|
81
142
|
if (cells.length !== 7) {
|
|
82
143
|
issues.push(`MISSING_PARTITION: row has ${cells.length} cells, expected 7: ${line.trim()}`);
|
|
83
144
|
continue;
|
|
@@ -369,8 +369,6 @@ function pythonParseable(source) {
|
|
|
369
369
|
const normalized = source.replace(/\r\n/g, "\n");
|
|
370
370
|
if (!normalized.trim())
|
|
371
371
|
return false;
|
|
372
|
-
if (/^\s*(?:def|class|import|from)\b/m.test(normalized) === false)
|
|
373
|
-
return false;
|
|
374
372
|
const structural = pythonStructureOutsideStrings(normalized);
|
|
375
373
|
if (!structural.stringsClosed)
|
|
376
374
|
return false;
|
|
@@ -387,7 +385,13 @@ function pythonParseable(source) {
|
|
|
387
385
|
return false;
|
|
388
386
|
if (openBraces !== closeBraces)
|
|
389
387
|
return false;
|
|
390
|
-
|
|
388
|
+
// Business modules still need a statement. Evidence-only mapped scripts may
|
|
389
|
+
// be a closed module docstring + Case-ID with no `def test_` (R10 health).
|
|
390
|
+
if (/^\s*(?:def|class|import|from|__all__\s*=)/m.test(normalized))
|
|
391
|
+
return true;
|
|
392
|
+
return (/^\s*(?:'''|""")/.test(normalized) &&
|
|
393
|
+
/\bCase-ID\s*:/i.test(normalized) &&
|
|
394
|
+
!/^\s*(?:async\s+)?def\s+test_/m.test(normalized));
|
|
391
395
|
}
|
|
392
396
|
/**
|
|
393
397
|
* Forbidden generated shared helper/factory namespaces for the self-contained
|
|
@@ -4375,7 +4375,27 @@ const headingAlias={'\u6a21\u5757\u7d22\u5f15':'Module Index','\u8986\u76d6\u830
|
|
|
4375
4375
|
const allLines=readme.replace(/\\r\\n/g,'\\n').replace(/\\r/g,'\\n').split('\\n');
|
|
4376
4376
|
let headingRepair=false;
|
|
4377
4377
|
for(let i=0;i<allLines.length;i++){const alias=/^##\\s+(模块索引|覆盖范围|覆盖矩阵|场景分区)\\s*$/.exec(allLines[i].trim());if(alias){allLines[i]='## '+headingAlias[alias[1]];headingRepair=true;}}
|
|
4378
|
-
|
|
4378
|
+
let partitionRepair=false;
|
|
4379
|
+
const partHeadTrim=[];for(let i=0;i<allLines.length;i++){if(allLines[i].trim()==='## Scenario Partitions')partHeadTrim.push(i);}
|
|
4380
|
+
if(partHeadTrim.length===1){
|
|
4381
|
+
const pStart=partHeadTrim[0]+1;let pEnd=allLines.length;for(let i=pStart;i<allLines.length;i++){if(/^##\\s+\\S/.test(allLines[i].trim())){pEnd=i;break;}}
|
|
4382
|
+
const pCells=line=>line.split('|').slice(1,-1).map(v=>String(v||'').trim());
|
|
4383
|
+
const pHeader=['Partition ID','Operation','Axis','Domain','Required Slots','Expected by Slot','Bind Rule'];
|
|
4384
|
+
for(let i=pStart;i<pEnd;i++){
|
|
4385
|
+
if(!allLines[i].includes('|')) continue;
|
|
4386
|
+
const row=pCells(allLines[i]);
|
|
4387
|
+
if(row.length<=7) continue;
|
|
4388
|
+
if(pHeader.every((v,idx)=>row[idx]===v)) continue;
|
|
4389
|
+
const pid=String(row[0]||'').trim();
|
|
4390
|
+
const extras=row.slice(7).join(';').split(/[;,,;]/).map(v=>v.trim().split(bt).join('')).filter(Boolean);
|
|
4391
|
+
const prefix='TP-'+pid.toUpperCase()+'-';
|
|
4392
|
+
if(extras.length && extras.every(t=>/^TP-[A-Z0-9-]+$/i.test(t)&&(t.toUpperCase().startsWith(prefix)||t.toUpperCase().startsWith('TP-SP-')))){
|
|
4393
|
+
allLines[i]='| '+row.slice(0,7).join(' | ')+' |';
|
|
4394
|
+
partitionRepair=true;
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
if(headingRepair||partitionRepair)readme=allLines.join('\\n');
|
|
4379
4399
|
const headings=[];for(let i=0;i<allLines.length;i++){if(allLines[i].trim()==='## Module Index')headings.push(i);}
|
|
4380
4400
|
if(headings.length!==1){process.stderr.write((headings.length===0?'missing-module-index':'duplicate-module-index')+'; require exactly one exact ## Module Index section\\n');process.exit(2);}
|
|
4381
4401
|
const start=headings[0]+1;let end=allLines.length;for(let i=start;i<allLines.length;i++){if(/^##\\s+\\S/.test(allLines[i].trim())){end=i;break;}}
|
|
@@ -4388,7 +4408,7 @@ const headerIndex=lines.findIndex(line=>{const row=cells(line);return expectedHe
|
|
|
4388
4408
|
if(headerIndex<0){process.stderr.write('invalid-module-index-header: require exact business ownership and path columns\\n');process.exit(2);}
|
|
4389
4409
|
const dataRows=lines.slice(headerIndex+2).map(cells).filter(row=>row.length>=8&&row[0]&&row[0]!=='Module Stem');
|
|
4390
4410
|
const allowedSplit=new Set(['explicit-user-layout','primary-business-resource','independent-business-resource','output-budget']);
|
|
4391
|
-
const operationOwners=new Map();const canonicalSeen=new Set();const declaredModules=[];let planRepairApplied=headingRepair;
|
|
4411
|
+
const operationOwners=new Map();const canonicalSeen=new Set();const declaredModules=[];let planRepairApplied=headingRepair||partitionRepair;
|
|
4392
4412
|
for(const row of dataRows){
|
|
4393
4413
|
const rawStem=String(row[0]||'').trim(),resource=String(row[1]||'').trim(),operations=String(row[2]||'').split(';').map(value=>value.trim()).filter(Boolean),split=String(row[5]||'').trim();
|
|
4394
4414
|
const mdMatches=[...String(row[6]||'').matchAll(rxMdPath)];
|
|
@@ -4430,7 +4450,7 @@ if(planRepairApplied){
|
|
|
4430
4450
|
const table=['| '+expectedHeader.join(' | ')+' |','|'+expectedHeader.map(()=>'---').join('|')+'|',...declaredModules.map(item=>'| '+[item.stem,item.businessResource,item.ownedOperations.join('; '),item.ownedRuleKeys.join('; '),item.caseIds.join('; '),item.splitReason,'['+item.stem+'](./'+item.stem+'.md) '+item.markdownPath,item.pytestPath].join(' | ')+' |')].join('\\n');
|
|
4431
4451
|
readme=[...allLines.slice(0,headings[0]+1),table,...allLines.slice(end)].join('\\n');
|
|
4432
4452
|
fs.writeFileSync(planPath,readme,'utf8');
|
|
4433
|
-
} else if(headingRepair){
|
|
4453
|
+
} else if(headingRepair||partitionRepair){
|
|
4434
4454
|
fs.writeFileSync(planPath,readme,'utf8');
|
|
4435
4455
|
}
|
|
4436
4456
|
for(const [operation,owners] of operationOwners){if(owners.length>1&&!owners.every(owner=>owner.split==='explicit-user-layout'||owner.split==='output-budget')){process.stderr.write('overlapping-operation-modules: '+operation+' -> '+owners.map(owner=>owner.stem).join(',')+'; merge by business resource or use an authoritative explicit-user-layout\\n');process.exit(2);}}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ag as o,ah as n}from"./mermaid.core-Bagus6zF.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-DPv743zu.js";import{_ as i}from"./mermaid.core-Bagus6zF.js";import"./chunk-5VM5RSS4-XTqNlzH4.js";import"./chunk-XXDRQBXY-B-A3ErBP.js";import"./chunk-KBJHAD2P-CV__aJTt.js";import"./chunk-2GRJ4B5K-BN8VNeh5.js";import"./index-Dkv0ezuE.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-DPv743zu.js";import{_ as i}from"./mermaid.core-Bagus6zF.js";import"./chunk-5VM5RSS4-XTqNlzH4.js";import"./chunk-XXDRQBXY-B-A3ErBP.js";import"./chunk-KBJHAD2P-CV__aJTt.js";import"./chunk-2GRJ4B5K-BN8VNeh5.js";import"./index-Dkv0ezuE.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as r,b as e,a,S as s}from"./chunk-5RXB4S5H-zxMQ5b8E.js";import{_ as i}from"./mermaid.core-Bagus6zF.js";import"./chunk-XXDRQBXY-B-A3ErBP.js";import"./chunk-KBJHAD2P-CV__aJTt.js";import"./chunk-2GRJ4B5K-BN8VNeh5.js";import"./index-Dkv0ezuE.js";var u={parser:a,get db(){return new s(2)},renderer:e,styles:r,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{u as diagram};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import{c as r,s as e}from"./flowDiagram-UKHOOZJN-DARr-Cvn.js";import{_ as a}from"./mermaid.core-Bagus6zF.js";import"./chunk-5VM5RSS4-XTqNlzH4.js";import"./chunk-XXDRQBXY-B-A3ErBP.js";import"./chunk-KBJHAD2P-CV__aJTt.js";import"./chunk-2GRJ4B5K-BN8VNeh5.js";import"./channel-BdTaMnYP.js";import"./index-Dkv0ezuE.js";var o=a(t=>`${e(t)}
|
|
2
|
-
.swimlane.cluster rect {
|
|
3
|
-
stroke: ${t.clusterBorder} !important;
|
|
4
|
-
}
|
|
5
|
-
[data-look="neo"].cluster rect {
|
|
6
|
-
filter: none;
|
|
7
|
-
}
|
|
8
|
-
`,"getStyles"),s=o,n=r({defaultLayout:"swimlane",styles:s});export{n as diagram};
|