@oscharko-dev/keiko-quality-intelligence 0.2.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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/_fixtureLoader.d.ts +9 -0
- package/dist/__tests__/_fixtureLoader.d.ts.map +1 -0
- package/dist/__tests__/_fixtureLoader.js +75 -0
- package/dist/domain/assertions.d.ts +61 -0
- package/dist/domain/assertions.d.ts.map +1 -0
- package/dist/domain/assertions.js +134 -0
- package/dist/domain/coverageRelevance.d.ts +73 -0
- package/dist/domain/coverageRelevance.d.ts.map +1 -0
- package/dist/domain/coverageRelevance.js +155 -0
- package/dist/domain/deduplication.d.ts +17 -0
- package/dist/domain/deduplication.d.ts.map +1 -0
- package/dist/domain/deduplication.js +95 -0
- package/dist/domain/figma/a11yBaseline.d.ts +17 -0
- package/dist/domain/figma/a11yBaseline.d.ts.map +1 -0
- package/dist/domain/figma/a11yBaseline.js +218 -0
- package/dist/domain/figma/cleanToScreenIr.d.ts +3 -0
- package/dist/domain/figma/cleanToScreenIr.d.ts.map +1 -0
- package/dist/domain/figma/cleanToScreenIr.js +62 -0
- package/dist/domain/figma/codeTargetAdapter.d.ts +30 -0
- package/dist/domain/figma/codeTargetAdapter.d.ts.map +1 -0
- package/dist/domain/figma/codeTargetAdapter.js +27 -0
- package/dist/domain/figma/color.d.ts +31 -0
- package/dist/domain/figma/color.d.ts.map +1 -0
- package/dist/domain/figma/color.js +99 -0
- package/dist/domain/figma/emissionPlan.d.ts +56 -0
- package/dist/domain/figma/emissionPlan.d.ts.map +1 -0
- package/dist/domain/figma/emissionPlan.js +87 -0
- package/dist/domain/figma/htmlCssAdapter.d.ts +13 -0
- package/dist/domain/figma/htmlCssAdapter.d.ts.map +1 -0
- package/dist/domain/figma/htmlCssAdapter.js +452 -0
- package/dist/domain/figma/index.d.ts +19 -0
- package/dist/domain/figma/index.d.ts.map +1 -0
- package/dist/domain/figma/index.js +31 -0
- package/dist/domain/figma/irTypes.d.ts +156 -0
- package/dist/domain/figma/irTypes.d.ts.map +1 -0
- package/dist/domain/figma/irTypes.js +8 -0
- package/dist/domain/figma/links.d.ts +6 -0
- package/dist/domain/figma/links.d.ts.map +1 -0
- package/dist/domain/figma/links.js +102 -0
- package/dist/domain/figma/navGraph.d.ts +74 -0
- package/dist/domain/figma/navGraph.d.ts.map +1 -0
- package/dist/domain/figma/navGraph.js +315 -0
- package/dist/domain/figma/normalize.d.ts +7 -0
- package/dist/domain/figma/normalize.d.ts.map +1 -0
- package/dist/domain/figma/normalize.js +252 -0
- package/dist/domain/figma/prune.d.ts +15 -0
- package/dist/domain/figma/prune.d.ts.map +1 -0
- package/dist/domain/figma/prune.js +65 -0
- package/dist/domain/figma/screenDetect.d.ts +8 -0
- package/dist/domain/figma/screenDetect.d.ts.map +1 -0
- package/dist/domain/figma/screenDetect.js +35 -0
- package/dist/domain/figma/screenIrTestBaseline.d.ts +52 -0
- package/dist/domain/figma/screenIrTestBaseline.d.ts.map +1 -0
- package/dist/domain/figma/screenIrTestBaseline.js +326 -0
- package/dist/domain/figma/semanticNaming.d.ts +24 -0
- package/dist/domain/figma/semanticNaming.d.ts.map +1 -0
- package/dist/domain/figma/semanticNaming.js +67 -0
- package/dist/domain/figma/sourceNode.d.ts +24 -0
- package/dist/domain/figma/sourceNode.d.ts.map +1 -0
- package/dist/domain/figma/sourceNode.js +26 -0
- package/dist/domain/figma/tokens.d.ts +11 -0
- package/dist/domain/figma/tokens.d.ts.map +1 -0
- package/dist/domain/figma/tokens.js +148 -0
- package/dist/domain/figma/visionAugmentation.d.ts +14 -0
- package/dist/domain/figma/visionAugmentation.d.ts.map +1 -0
- package/dist/domain/figma/visionAugmentation.js +48 -0
- package/dist/domain/intentDerivation.d.ts +21 -0
- package/dist/domain/intentDerivation.d.ts.map +1 -0
- package/dist/domain/intentDerivation.js +126 -0
- package/dist/domain/policyProfile.d.ts +37 -0
- package/dist/domain/policyProfile.d.ts.map +1 -0
- package/dist/domain/policyProfile.js +94 -0
- package/dist/domain/requirementExcerpt.d.ts +9 -0
- package/dist/domain/requirementExcerpt.d.ts.map +1 -0
- package/dist/domain/requirementExcerpt.js +39 -0
- package/dist/domain/staleness.d.ts +56 -0
- package/dist/domain/staleness.d.ts.map +1 -0
- package/dist/domain/staleness.js +313 -0
- package/dist/domain/testDesignModel.d.ts +38 -0
- package/dist/domain/testDesignModel.d.ts.map +1 -0
- package/dist/domain/testDesignModel.js +264 -0
- package/dist/domain/testQualityRubric.d.ts +20 -0
- package/dist/domain/testQualityRubric.d.ts.map +1 -0
- package/dist/domain/testQualityRubric.js +38 -0
- package/dist/domain/validation.d.ts +7 -0
- package/dist/domain/validation.d.ts.map +1 -0
- package/dist/domain/validation.js +145 -0
- package/dist/export/adapters/alm.d.ts +4 -0
- package/dist/export/adapters/alm.d.ts.map +1 -0
- package/dist/export/adapters/alm.js +75 -0
- package/dist/export/adapters/csv.d.ts +5 -0
- package/dist/export/adapters/csv.d.ts.map +1 -0
- package/dist/export/adapters/csv.js +55 -0
- package/dist/export/adapters/index.d.ts +13 -0
- package/dist/export/adapters/index.d.ts.map +1 -0
- package/dist/export/adapters/index.js +15 -0
- package/dist/export/adapters/jira.d.ts +5 -0
- package/dist/export/adapters/jira.d.ts.map +1 -0
- package/dist/export/adapters/jira.js +79 -0
- package/dist/export/adapters/json.d.ts +3 -0
- package/dist/export/adapters/json.d.ts.map +1 -0
- package/dist/export/adapters/json.js +54 -0
- package/dist/export/adapters/markdown.d.ts +3 -0
- package/dist/export/adapters/markdown.d.ts.map +1 -0
- package/dist/export/adapters/markdown.js +88 -0
- package/dist/export/adapters/plaintext.d.ts +3 -0
- package/dist/export/adapters/plaintext.d.ts.map +1 -0
- package/dist/export/adapters/plaintext.js +65 -0
- package/dist/export/adapters/polarion.d.ts +4 -0
- package/dist/export/adapters/polarion.d.ts.map +1 -0
- package/dist/export/adapters/polarion.js +67 -0
- package/dist/export/adapters/qtest.d.ts +4 -0
- package/dist/export/adapters/qtest.d.ts.map +1 -0
- package/dist/export/adapters/qtest.js +78 -0
- package/dist/export/adapters/qualityCenter.d.ts +3 -0
- package/dist/export/adapters/qualityCenter.d.ts.map +1 -0
- package/dist/export/adapters/qualityCenter.js +56 -0
- package/dist/export/adapters/spreadsheetSafeCsv.d.ts +36 -0
- package/dist/export/adapters/spreadsheetSafeCsv.d.ts.map +1 -0
- package/dist/export/adapters/spreadsheetSafeCsv.js +157 -0
- package/dist/export/adapters/traceability.d.ts +34 -0
- package/dist/export/adapters/traceability.d.ts.map +1 -0
- package/dist/export/adapters/traceability.js +142 -0
- package/dist/export/adapters/xray.d.ts +4 -0
- package/dist/export/adapters/xray.d.ts.map +1 -0
- package/dist/export/adapters/xray.js +72 -0
- package/dist/export/formats.d.ts +29 -0
- package/dist/export/formats.d.ts.map +1 -0
- package/dist/export/formats.js +34 -0
- package/dist/export/index.d.ts +4 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +10 -0
- package/dist/export/serialize.d.ts +17 -0
- package/dist/export/serialize.d.ts.map +1 -0
- package/dist/export/serialize.js +56 -0
- package/dist/export/textSafety.d.ts +15 -0
- package/dist/export/textSafety.d.ts.map +1 -0
- package/dist/export/textSafety.js +30 -0
- package/dist/generation/candidateBounds.d.ts +10 -0
- package/dist/generation/candidateBounds.d.ts.map +1 -0
- package/dist/generation/candidateBounds.js +14 -0
- package/dist/generation/index.d.ts +4 -0
- package/dist/generation/index.d.ts.map +1 -0
- package/dist/generation/index.js +20 -0
- package/dist/generation/parseGeneratedCandidates.d.ts +27 -0
- package/dist/generation/parseGeneratedCandidates.d.ts.map +1 -0
- package/dist/generation/parseGeneratedCandidates.js +253 -0
- package/dist/generation/prompt.d.ts +16 -0
- package/dist/generation/prompt.d.ts.map +1 -0
- package/dist/generation/prompt.js +151 -0
- package/dist/generation/requirementsIngestion.d.ts +21 -0
- package/dist/generation/requirementsIngestion.d.ts.map +1 -0
- package/dist/generation/requirementsIngestion.js +70 -0
- package/dist/hardening/index.d.ts +6 -0
- package/dist/hardening/index.d.ts.map +1 -0
- package/dist/hardening/index.js +8 -0
- package/dist/hardening/oversizeGuards.d.ts +21 -0
- package/dist/hardening/oversizeGuards.d.ts.map +1 -0
- package/dist/hardening/oversizeGuards.js +35 -0
- package/dist/hardening/pathSafety.d.ts +19 -0
- package/dist/hardening/pathSafety.d.ts.map +1 -0
- package/dist/hardening/pathSafety.js +61 -0
- package/dist/hardening/promptInjectionScrub.d.ts +17 -0
- package/dist/hardening/promptInjectionScrub.d.ts.map +1 -0
- package/dist/hardening/promptInjectionScrub.js +72 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/ingestion/adfParser.d.ts +61 -0
- package/dist/ingestion/adfParser.d.ts.map +1 -0
- package/dist/ingestion/adfParser.js +262 -0
- package/dist/ingestion/index.d.ts +6 -0
- package/dist/ingestion/index.d.ts.map +1 -0
- package/dist/ingestion/index.js +10 -0
- package/dist/ingestion/sourceMixPlanning.d.ts +36 -0
- package/dist/ingestion/sourceMixPlanning.d.ts.map +1 -0
- package/dist/ingestion/sourceMixPlanning.js +65 -0
- package/dist/ingestion/sourceReconciliation.d.ts +39 -0
- package/dist/ingestion/sourceReconciliation.d.ts.map +1 -0
- package/dist/ingestion/sourceReconciliation.js +74 -0
- package/dist/ingestion/untrustedContentNormalisation.d.ts +23 -0
- package/dist/ingestion/untrustedContentNormalisation.d.ts.map +1 -0
- package/dist/ingestion/untrustedContentNormalisation.js +121 -0
- package/dist/ingestion/workspaceAdapter.d.ts +55 -0
- package/dist/ingestion/workspaceAdapter.d.ts.map +1 -0
- package/dist/ingestion/workspaceAdapter.js +113 -0
- package/dist/review/auditEvents.d.ts +61 -0
- package/dist/review/auditEvents.d.ts.map +1 -0
- package/dist/review/auditEvents.js +50 -0
- package/dist/review/fourEyes.d.ts +24 -0
- package/dist/review/fourEyes.d.ts.map +1 -0
- package/dist/review/fourEyes.js +45 -0
- package/dist/review/index.d.ts +5 -0
- package/dist/review/index.d.ts.map +1 -0
- package/dist/review/index.js +14 -0
- package/dist/review/lifecyclePolicy.d.ts +21 -0
- package/dist/review/lifecyclePolicy.d.ts.map +1 -0
- package/dist/review/lifecyclePolicy.js +38 -0
- package/dist/review/stateMachine.d.ts +28 -0
- package/dist/review/stateMachine.d.ts.map +1 -0
- package/dist/review/stateMachine.js +71 -0
- package/package.json +31 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Requirement-to-test traceability export adapters (Epic #734, Issue #740).
|
|
2
|
+
//
|
|
3
|
+
// Pure-domain leaf. Renders the per-atom coverage matrix as an audit-ready, BIDIRECTIONAL
|
|
4
|
+
// requirement<->test traceability matrix in CSV (spreadsheet-safe) and Markdown:
|
|
5
|
+
// * Requirements -> Tests: each requirement atom and the tests that cover it (+ status).
|
|
6
|
+
// * Tests -> Requirements: each test and the requirements it traces to.
|
|
7
|
+
// Both directions are derived purely from the coverage matrix rows plus optional ALREADY-REDACTED
|
|
8
|
+
// display fields — a requirement excerpt per row and a candidate-title lookup (#790) — never raw
|
|
9
|
+
// atom text, so the adapter depends on nothing outside keiko-contracts/keiko-quality-intelligence
|
|
10
|
+
// (ADR-0019 direction rule). Deterministic: rows are sorted by id, confidence is fixed-precision,
|
|
11
|
+
// and there are no timestamps — so the export is byte-stable for identical inputs.
|
|
12
|
+
import { inlineField } from "../textSafety.js";
|
|
13
|
+
import { encodeSpreadsheetSafeRow, startsWithFormulaLead } from "./spreadsheetSafeCsv.js";
|
|
14
|
+
/** CSV header row for the requirement -> tests direction. */
|
|
15
|
+
export const TRACEABILITY_HEADERS = Object.freeze([
|
|
16
|
+
"Requirement ID",
|
|
17
|
+
"Requirement (redacted excerpt)",
|
|
18
|
+
"Status",
|
|
19
|
+
"Confidence",
|
|
20
|
+
"Covering Tests",
|
|
21
|
+
"Test Count",
|
|
22
|
+
]);
|
|
23
|
+
/** CSV header row for the test -> requirements (reverse) direction. */
|
|
24
|
+
export const TRACEABILITY_REVERSE_HEADERS = Object.freeze([
|
|
25
|
+
"Test ID",
|
|
26
|
+
"Test Title",
|
|
27
|
+
"Requirements Covered",
|
|
28
|
+
"Requirement Count",
|
|
29
|
+
]);
|
|
30
|
+
/** Placeholder for an absent display value (legacy rows / unknown candidate). Em-dash, not a formula lead. */
|
|
31
|
+
const ABSENT = "—";
|
|
32
|
+
const byAtomIdAsc = (a, b) => (a.atomId < b.atomId ? -1 : a.atomId > b.atomId ? 1 : 0);
|
|
33
|
+
const ascending = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
34
|
+
const fixed2 = (value) => value.toFixed(2);
|
|
35
|
+
const joinSemicolon = (values) => values.join(" ; ");
|
|
36
|
+
/**
|
|
37
|
+
* Invert the requirement->test rows into deterministic test->requirement rows. A test that covers
|
|
38
|
+
* several requirements appears once with all of them; ordering is fully sorted so the output is
|
|
39
|
+
* byte-stable.
|
|
40
|
+
*/
|
|
41
|
+
function invertToReverseRows(rows) {
|
|
42
|
+
const byCandidate = new Map();
|
|
43
|
+
for (const row of rows) {
|
|
44
|
+
for (const candidateId of row.coveringCandidateIds) {
|
|
45
|
+
const set = byCandidate.get(candidateId) ?? new Set();
|
|
46
|
+
set.add(row.atomId);
|
|
47
|
+
byCandidate.set(candidateId, set);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return [...byCandidate.entries()]
|
|
51
|
+
.map(([candidateId, set]) => ({
|
|
52
|
+
candidateId,
|
|
53
|
+
requirementIds: [...set].sort(ascending),
|
|
54
|
+
}))
|
|
55
|
+
.sort((a, b) => ascending(a.candidateId, b.candidateId));
|
|
56
|
+
}
|
|
57
|
+
// Escape Markdown table delimiters so an id containing a pipe cannot break the row structure, and
|
|
58
|
+
// neutralise a spreadsheet formula lead (=,+,-,@) so a cell stays inert if the table is pasted into
|
|
59
|
+
// a spreadsheet. inlineField runs FIRST so embedded newlines/tabs cannot split one logical row across
|
|
60
|
+
// two physical Markdown lines (CWE-1284). Backslashes are escaped BEFORE pipes so a literal backslash
|
|
61
|
+
// cannot consume the following escape and a pre-existing `\|` cannot smuggle an unescaped pipe (CWE-20).
|
|
62
|
+
const mdCell = (value) => {
|
|
63
|
+
const oneLine = inlineField(value);
|
|
64
|
+
const safe = startsWithFormulaLead(oneLine) ? `'${oneLine}` : oneLine;
|
|
65
|
+
return safe.replace(/\\/gu, "\\\\").replace(/\|/gu, "\\|");
|
|
66
|
+
};
|
|
67
|
+
const mdRow = (cells) => `| ${cells.map(mdCell).join(" | ")} |`;
|
|
68
|
+
/**
|
|
69
|
+
* Render the coverage matrix as a spreadsheet-safe, BIDIRECTIONAL CSV traceability matrix: a
|
|
70
|
+
* requirement->tests section followed by a blank line and a tests->requirements section. Each cell
|
|
71
|
+
* is formula-injection-safe via the shared encoder.
|
|
72
|
+
*/
|
|
73
|
+
export function adaptToTraceabilityCsv(rows, display = {}) {
|
|
74
|
+
const sorted = [...rows].sort(byAtomIdAsc);
|
|
75
|
+
let body = encodeSpreadsheetSafeRow(["Requirements to tests"]);
|
|
76
|
+
body += encodeSpreadsheetSafeRow(TRACEABILITY_HEADERS);
|
|
77
|
+
for (const row of sorted) {
|
|
78
|
+
body += encodeSpreadsheetSafeRow([
|
|
79
|
+
inlineField(row.atomId),
|
|
80
|
+
inlineField(row.requirementExcerptRedacted ?? ABSENT),
|
|
81
|
+
row.status,
|
|
82
|
+
fixed2(row.confidence),
|
|
83
|
+
inlineField(joinSemicolon(row.coveringCandidateIds)),
|
|
84
|
+
String(row.coveringCandidateIds.length),
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
body += "\r\n";
|
|
88
|
+
body += encodeSpreadsheetSafeRow(["Tests to requirements"]);
|
|
89
|
+
body += encodeSpreadsheetSafeRow(TRACEABILITY_REVERSE_HEADERS);
|
|
90
|
+
for (const reverse of invertToReverseRows(sorted)) {
|
|
91
|
+
body += encodeSpreadsheetSafeRow([
|
|
92
|
+
inlineField(reverse.candidateId),
|
|
93
|
+
inlineField(display.candidateTitleById?.get(reverse.candidateId) ?? ABSENT),
|
|
94
|
+
inlineField(joinSemicolon(reverse.requirementIds)),
|
|
95
|
+
String(reverse.requirementIds.length),
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
return body;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Render the coverage matrix as a BIDIRECTIONAL Markdown traceability document: a Requirements ->
|
|
102
|
+
* Tests table followed by a Tests -> Requirements table. Deterministic, pipe-escaped and
|
|
103
|
+
* formula-lead-neutralised.
|
|
104
|
+
*/
|
|
105
|
+
export function adaptToTraceabilityMarkdown(rows, display = {}) {
|
|
106
|
+
const sorted = [...rows].sort(byAtomIdAsc);
|
|
107
|
+
const lines = [
|
|
108
|
+
"# Requirement to test traceability matrix",
|
|
109
|
+
"",
|
|
110
|
+
"## Requirements → Tests",
|
|
111
|
+
"",
|
|
112
|
+
`| ${TRACEABILITY_HEADERS.join(" | ")} |`,
|
|
113
|
+
`| ${TRACEABILITY_HEADERS.map(() => "---").join(" | ")} |`,
|
|
114
|
+
];
|
|
115
|
+
for (const row of sorted) {
|
|
116
|
+
const tests = row.coveringCandidateIds.length > 0 ? joinSemicolon(row.coveringCandidateIds) : ABSENT;
|
|
117
|
+
lines.push(mdRow([
|
|
118
|
+
row.atomId,
|
|
119
|
+
row.requirementExcerptRedacted ?? ABSENT,
|
|
120
|
+
row.status,
|
|
121
|
+
fixed2(row.confidence),
|
|
122
|
+
tests,
|
|
123
|
+
String(row.coveringCandidateIds.length),
|
|
124
|
+
]));
|
|
125
|
+
}
|
|
126
|
+
lines.push("", "## Tests → Requirements", "");
|
|
127
|
+
lines.push(`| ${TRACEABILITY_REVERSE_HEADERS.join(" | ")} |`);
|
|
128
|
+
lines.push(`| ${TRACEABILITY_REVERSE_HEADERS.map(() => "---").join(" | ")} |`);
|
|
129
|
+
const reverseRows = invertToReverseRows(sorted);
|
|
130
|
+
if (reverseRows.length === 0) {
|
|
131
|
+
lines.push(mdRow([ABSENT, ABSENT, ABSENT, "0"]));
|
|
132
|
+
}
|
|
133
|
+
for (const reverse of reverseRows) {
|
|
134
|
+
lines.push(mdRow([
|
|
135
|
+
reverse.candidateId,
|
|
136
|
+
display.candidateTitleById?.get(reverse.candidateId) ?? ABSENT,
|
|
137
|
+
joinSemicolon(reverse.requirementIds),
|
|
138
|
+
String(reverse.requirementIds.length),
|
|
139
|
+
]));
|
|
140
|
+
}
|
|
141
|
+
return lines.join("\n") + "\n";
|
|
142
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { QualityIntelligenceExportBundle, QualityIntelligenceTestCaseCandidate } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export declare const XRAY_CSV_HEADERS: readonly string[];
|
|
3
|
+
export declare function adaptToXray(bundle: QualityIntelligenceExportBundle, candidates: readonly QualityIntelligenceTestCaseCandidate[]): string;
|
|
4
|
+
//# sourceMappingURL=xray.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xray.d.ts","sourceRoot":"","sources":["../../../src/export/adapters/xray.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,+BAA+B,EAC/B,oCAAoC,EACrC,MAAM,+BAA+B,CAAC;AAIvC,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAQ5C,CAAC;AASH,wBAAgB,WAAW,CACzB,MAAM,EAAE,+BAA+B,EACvC,UAAU,EAAE,SAAS,oCAAoC,EAAE,GAC1D,MAAM,CA+CR"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Xray CSV export adapter (Epic #270, Issue #283).
|
|
2
|
+
//
|
|
3
|
+
// Xray's "Test Case Importer" canonical shape: TCID, Summary, Description, Action,
|
|
4
|
+
// Data, Result, Test Type. Like qTest, a multi-step test occupies multiple rows
|
|
5
|
+
// where Summary/Description repeat across step rows.
|
|
6
|
+
//
|
|
7
|
+
// TMS-bound: invariant asserted first. Pure-domain — NO HTTP, NO Xray SDK.
|
|
8
|
+
import { assertExportBundleInvariant } from "@oscharko-dev/keiko-contracts";
|
|
9
|
+
import { encodeSpreadsheetSafeRow } from "./spreadsheetSafeCsv.js";
|
|
10
|
+
export const XRAY_CSV_HEADERS = Object.freeze([
|
|
11
|
+
"TCID",
|
|
12
|
+
"Summary",
|
|
13
|
+
"Description",
|
|
14
|
+
"Action",
|
|
15
|
+
"Data",
|
|
16
|
+
"Result",
|
|
17
|
+
"TestType",
|
|
18
|
+
]);
|
|
19
|
+
const buildDescription = (candidate) => {
|
|
20
|
+
if (candidate.preconditions.length === 0) {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
return candidate.preconditions.join(" ; ");
|
|
24
|
+
};
|
|
25
|
+
export function adaptToXray(bundle, candidates) {
|
|
26
|
+
assertExportBundleInvariant(bundle);
|
|
27
|
+
const byId = new Map();
|
|
28
|
+
for (const candidate of candidates) {
|
|
29
|
+
byId.set(candidate.id, candidate);
|
|
30
|
+
}
|
|
31
|
+
const sortedIds = bundle.contents
|
|
32
|
+
.map((entry) => entry.candidateId)
|
|
33
|
+
.slice()
|
|
34
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
35
|
+
let body = encodeSpreadsheetSafeRow(XRAY_CSV_HEADERS);
|
|
36
|
+
for (const id of sortedIds) {
|
|
37
|
+
const candidate = byId.get(id);
|
|
38
|
+
if (candidate === undefined) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// `steps` and `expectedResults` are independent arrays with no co-length contract invariant, so
|
|
42
|
+
// the row count is the longer of the two — bounding the loop by `steps.length` alone would
|
|
43
|
+
// silently drop trailing expected results (and ALL of them when there are zero steps).
|
|
44
|
+
const rowCount = Math.max(candidate.steps.length, candidate.expectedResults.length);
|
|
45
|
+
if (rowCount === 0) {
|
|
46
|
+
body += encodeSpreadsheetSafeRow([
|
|
47
|
+
candidate.id,
|
|
48
|
+
candidate.title,
|
|
49
|
+
buildDescription(candidate),
|
|
50
|
+
"",
|
|
51
|
+
"",
|
|
52
|
+
"",
|
|
53
|
+
"Manual",
|
|
54
|
+
]);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
for (let i = 0; i < rowCount; i += 1) {
|
|
58
|
+
const action = candidate.steps[i] ?? "";
|
|
59
|
+
const result = candidate.expectedResults[i] ?? "";
|
|
60
|
+
body += encodeSpreadsheetSafeRow([
|
|
61
|
+
candidate.id,
|
|
62
|
+
candidate.title,
|
|
63
|
+
buildDescription(candidate),
|
|
64
|
+
action,
|
|
65
|
+
"",
|
|
66
|
+
result,
|
|
67
|
+
"Manual",
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return body;
|
|
72
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { QualityIntelligenceExportAdapter } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
/**
|
|
3
|
+
* Public type alias for the adapter discriminator. Identical in shape to the
|
|
4
|
+
* contract-side `QualityIntelligenceExportAdapter`. Spec-mandated name.
|
|
5
|
+
*/
|
|
6
|
+
export type QualityIntelligenceExportFormat = QualityIntelligenceExportAdapter;
|
|
7
|
+
/**
|
|
8
|
+
* Frozen per-format adapter descriptor. The `adapterId` mirrors the contract
|
|
9
|
+
* adapter name; `bytesPerRowHint` is a deterministic capacity hint for callers
|
|
10
|
+
* that need to pre-size buffers. The hints are static guidance — the actual
|
|
11
|
+
* serialised output is what counts, not the hint.
|
|
12
|
+
*/
|
|
13
|
+
export interface QualityIntelligenceExportFormatDescriptor {
|
|
14
|
+
readonly format: QualityIntelligenceExportFormat;
|
|
15
|
+
readonly adapterId: QualityIntelligenceExportFormat;
|
|
16
|
+
readonly bytesPerRowHint: number;
|
|
17
|
+
readonly filenameExtension: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Frozen lookup table mapping format → adapter descriptor. Eleven formats,
|
|
21
|
+
* mirrors `QUALITY_INTELLIGENCE_EXPORT_ADAPTERS` exactly.
|
|
22
|
+
*/
|
|
23
|
+
export declare const QUALITY_INTELLIGENCE_EXPORT_FORMAT_TABLE: Readonly<Record<QualityIntelligenceExportFormat, QualityIntelligenceExportFormatDescriptor>>;
|
|
24
|
+
/**
|
|
25
|
+
* Returns the descriptor for `format`. Pure lookup; the table is frozen at
|
|
26
|
+
* module load so the returned reference is stable for the process lifetime.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getExportFormatDescriptor(format: QualityIntelligenceExportFormat): QualityIntelligenceExportFormatDescriptor;
|
|
29
|
+
//# sourceMappingURL=formats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formats.d.ts","sourceRoot":"","sources":["../../src/export/formats.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AAEtF;;;GAGG;AACH,MAAM,MAAM,+BAA+B,GAAG,gCAAgC,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,WAAW,yCAAyC;IACxD,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,SAAS,EAAE,+BAA+B,CAAC;IACpD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AASD;;;GAGG;AACH,eAAO,MAAM,wCAAwC,EAAE,QAAQ,CAC7D,MAAM,CAAC,+BAA+B,EAAE,yCAAyC,CAAC,CAalF,CAAC;AAEH;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,+BAA+B,GACtC,yCAAyC,CAE3C"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Frozen format table for Quality Intelligence export adapters (Epic #270, Issue #283).
|
|
2
|
+
//
|
|
3
|
+
// Reuses the `QualityIntelligenceExportAdapter` discriminator from
|
|
4
|
+
// `@oscharko-dev/keiko-contracts` so the adapter id is the single source of truth.
|
|
5
|
+
// The local re-export under the name `QualityIntelligenceExportFormat` is the
|
|
6
|
+
// type alias the public spec calls for; downstream callers may import either
|
|
7
|
+
// name.
|
|
8
|
+
//
|
|
9
|
+
// Pure-domain leaf. NO IO. NO new runtime dependency.
|
|
10
|
+
const descriptor = (format, bytesPerRowHint, filenameExtension) => Object.freeze({ format, adapterId: format, bytesPerRowHint, filenameExtension });
|
|
11
|
+
/**
|
|
12
|
+
* Frozen lookup table mapping format → adapter descriptor. Eleven formats,
|
|
13
|
+
* mirrors `QUALITY_INTELLIGENCE_EXPORT_ADAPTERS` exactly.
|
|
14
|
+
*/
|
|
15
|
+
export const QUALITY_INTELLIGENCE_EXPORT_FORMAT_TABLE = Object.freeze({
|
|
16
|
+
"jira-issues": descriptor("jira-issues", 512, "csv"),
|
|
17
|
+
qtest: descriptor("qtest", 384, "csv"),
|
|
18
|
+
xray: descriptor("xray", 384, "csv"),
|
|
19
|
+
polarion: descriptor("polarion", 384, "csv"),
|
|
20
|
+
alm: descriptor("alm", 384, "csv"),
|
|
21
|
+
csv: descriptor("csv", 256, "csv"),
|
|
22
|
+
json: descriptor("json", 1024, "json"),
|
|
23
|
+
"spreadsheet-safe-csv": descriptor("spreadsheet-safe-csv", 256, "csv"),
|
|
24
|
+
markdown: descriptor("markdown", 512, "md"),
|
|
25
|
+
"plain-text": descriptor("plain-text", 384, "txt"),
|
|
26
|
+
"quality-center": descriptor("quality-center", 384, "txt"),
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Returns the descriptor for `format`. Pure lookup; the table is frozen at
|
|
30
|
+
* module load so the returned reference is stable for the process lifetime.
|
|
31
|
+
*/
|
|
32
|
+
export function getExportFormatDescriptor(format) {
|
|
33
|
+
return QUALITY_INTELLIGENCE_EXPORT_FORMAT_TABLE[format];
|
|
34
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { QUALITY_INTELLIGENCE_EXPORT_FORMAT_TABLE, getExportFormatDescriptor, type QualityIntelligenceExportFormat, type QualityIntelligenceExportFormatDescriptor, } from "./formats.js";
|
|
2
|
+
export { adaptToAlm, adaptToCsv, adaptToJiraIssues, adaptToJson, adaptToMarkdown, adaptToPlainText, adaptToPolarion, adaptToQtest, adaptToQualityCenter, adaptToSpreadsheetSafeCsv, adaptToXray, ALM_CSV_HEADERS, CSV_HEADERS, encodeSpreadsheetSafeCell, encodeSpreadsheetSafeRow, JIRA_CSV_HEADERS, POLARION_CSV_HEADERS, QTEST_CSV_HEADERS, SPREADSHEET_FORMULA_LEAD_CHARS, SPREADSHEET_SAFE_CSV_HEADERS, startsWithFormulaLead, XRAY_CSV_HEADERS, adaptToTraceabilityCsv, adaptToTraceabilityMarkdown, TRACEABILITY_HEADERS, TRACEABILITY_REVERSE_HEADERS, type QualityIntelligenceTraceabilityDisplayOptions, type QualityIntelligenceTraceabilityRow, } from "./adapters/index.js";
|
|
3
|
+
export { serializeExportBundle, type SerializedExportBundle } from "./serialize.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/export/index.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,wCAAwC,EACxC,yBAAyB,EACzB,KAAK,+BAA+B,EACpC,KAAK,yCAAyC,GAC/C,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,yBAAyB,EACzB,WAAW,EACX,eAAe,EACf,WAAW,EACX,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,8BAA8B,EAC9B,4BAA4B,EAC5B,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,2BAA2B,EAC3B,oBAAoB,EACpB,4BAA4B,EAC5B,KAAK,6CAA6C,EAClD,KAAK,kCAAkC,GACxC,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,qBAAqB,EAAE,KAAK,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Public barrel for the Quality Intelligence export sub-namespace (Epic #270, Issue #283).
|
|
2
|
+
//
|
|
3
|
+
// Pure-domain leaf. NO IO, NO HTTP, NO provider SDK imports, NO new runtime dependency.
|
|
4
|
+
// Adapters consume `QualityIntelligenceExportBundle` + `QualityIntelligenceTestCaseCandidate`
|
|
5
|
+
// from `@oscharko-dev/keiko-contracts` and produce typed serialised bodies. Persistence of
|
|
6
|
+
// these bodies is owned by `@oscharko-dev/keiko-evidence` via the side-file primitive — this
|
|
7
|
+
// layer never touches `node:fs`.
|
|
8
|
+
export { QUALITY_INTELLIGENCE_EXPORT_FORMAT_TABLE, getExportFormatDescriptor, } from "./formats.js";
|
|
9
|
+
export { adaptToAlm, adaptToCsv, adaptToJiraIssues, adaptToJson, adaptToMarkdown, adaptToPlainText, adaptToPolarion, adaptToQtest, adaptToQualityCenter, adaptToSpreadsheetSafeCsv, adaptToXray, ALM_CSV_HEADERS, CSV_HEADERS, encodeSpreadsheetSafeCell, encodeSpreadsheetSafeRow, JIRA_CSV_HEADERS, POLARION_CSV_HEADERS, QTEST_CSV_HEADERS, SPREADSHEET_FORMULA_LEAD_CHARS, SPREADSHEET_SAFE_CSV_HEADERS, startsWithFormulaLead, XRAY_CSV_HEADERS, adaptToTraceabilityCsv, adaptToTraceabilityMarkdown, TRACEABILITY_HEADERS, TRACEABILITY_REVERSE_HEADERS, } from "./adapters/index.js";
|
|
10
|
+
export { serializeExportBundle } from "./serialize.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { QualityIntelligenceExportBundle, QualityIntelligenceTestCaseCandidate } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
import type { QualityIntelligenceExportFormat } from "./formats.js";
|
|
3
|
+
export interface SerializedExportBundle {
|
|
4
|
+
readonly format: QualityIntelligenceExportFormat;
|
|
5
|
+
readonly body: string;
|
|
6
|
+
readonly byteLen: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Serialise an export bundle by dispatching on `bundle.targetAdapter`. The contract
|
|
10
|
+
* invariant is asserted up front (TMS-bound bundles MUST carry
|
|
11
|
+
* `redactionAttested === true`) before any adapter is invoked.
|
|
12
|
+
*
|
|
13
|
+
* `byteLen` is the UTF-8 byte length of the body — what an evidence side-file would
|
|
14
|
+
* persist, which is what callers downstream need for capacity accounting.
|
|
15
|
+
*/
|
|
16
|
+
export declare function serializeExportBundle(bundle: QualityIntelligenceExportBundle, candidates: readonly QualityIntelligenceTestCaseCandidate[]): SerializedExportBundle;
|
|
17
|
+
//# sourceMappingURL=serialize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/export/serialize.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,+BAA+B,EAC/B,oCAAoC,EACrC,MAAM,+BAA+B,CAAC;AAevC,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAqBD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,+BAA+B,EACvC,UAAU,EAAE,SAAS,oCAAoC,EAAE,GAC1D,sBAAsB,CAexB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Dispatcher for Quality Intelligence export-bundle serialisation (Epic #270, Issue #283).
|
|
2
|
+
//
|
|
3
|
+
// Pure-domain leaf. Pre-asserts the contract invariant on the bundle, then dispatches
|
|
4
|
+
// to the format-specific adapter. The dispatch table is `satisfies` typed so every
|
|
5
|
+
// `QualityIntelligenceExportFormat` MUST have a registered adapter — adding a new
|
|
6
|
+
// format is a compile error here, by design.
|
|
7
|
+
import { assertExportBundleInvariant } from "@oscharko-dev/keiko-contracts";
|
|
8
|
+
import { adaptToAlm } from "./adapters/alm.js";
|
|
9
|
+
import { adaptToCsv } from "./adapters/csv.js";
|
|
10
|
+
import { adaptToJiraIssues } from "./adapters/jira.js";
|
|
11
|
+
import { adaptToJson } from "./adapters/json.js";
|
|
12
|
+
import { adaptToMarkdown } from "./adapters/markdown.js";
|
|
13
|
+
import { adaptToPlainText } from "./adapters/plaintext.js";
|
|
14
|
+
import { adaptToPolarion } from "./adapters/polarion.js";
|
|
15
|
+
import { adaptToQtest } from "./adapters/qtest.js";
|
|
16
|
+
import { adaptToQualityCenter } from "./adapters/qualityCenter.js";
|
|
17
|
+
import { adaptToSpreadsheetSafeCsv } from "./adapters/spreadsheetSafeCsv.js";
|
|
18
|
+
import { adaptToXray } from "./adapters/xray.js";
|
|
19
|
+
import { getExportFormatDescriptor } from "./formats.js";
|
|
20
|
+
const DISPATCH = Object.freeze({
|
|
21
|
+
"jira-issues": adaptToJiraIssues,
|
|
22
|
+
qtest: adaptToQtest,
|
|
23
|
+
xray: adaptToXray,
|
|
24
|
+
polarion: adaptToPolarion,
|
|
25
|
+
alm: adaptToAlm,
|
|
26
|
+
csv: adaptToCsv,
|
|
27
|
+
json: adaptToJson,
|
|
28
|
+
"spreadsheet-safe-csv": adaptToSpreadsheetSafeCsv,
|
|
29
|
+
markdown: adaptToMarkdown,
|
|
30
|
+
"plain-text": adaptToPlainText,
|
|
31
|
+
"quality-center": adaptToQualityCenter,
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Serialise an export bundle by dispatching on `bundle.targetAdapter`. The contract
|
|
35
|
+
* invariant is asserted up front (TMS-bound bundles MUST carry
|
|
36
|
+
* `redactionAttested === true`) before any adapter is invoked.
|
|
37
|
+
*
|
|
38
|
+
* `byteLen` is the UTF-8 byte length of the body — what an evidence side-file would
|
|
39
|
+
* persist, which is what callers downstream need for capacity accounting.
|
|
40
|
+
*/
|
|
41
|
+
export function serializeExportBundle(bundle, candidates) {
|
|
42
|
+
assertExportBundleInvariant(bundle);
|
|
43
|
+
const format = bundle.targetAdapter;
|
|
44
|
+
// The dispatch table is total over the format union; the indirection makes a
|
|
45
|
+
// future format addition a compile-time failure here.
|
|
46
|
+
const adapter = DISPATCH[format];
|
|
47
|
+
// Pre-touch the descriptor so a missing format mapping shows up as a clear
|
|
48
|
+
// runtime error rather than `undefined` propagating. Pure lookup, no side effect.
|
|
49
|
+
getExportFormatDescriptor(format);
|
|
50
|
+
const body = adapter(bundle, candidates);
|
|
51
|
+
// TextEncoder is a Web-standard global (no node:* import); identical UTF-8 byte
|
|
52
|
+
// length to Buffer.byteLength but keeps this leaf free of node:* surface so the
|
|
53
|
+
// purity guard rule does not need to be widened for the export sub-namespace.
|
|
54
|
+
const byteLen = new TextEncoder().encode(body).length;
|
|
55
|
+
return { format, body, byteLen };
|
|
56
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapse embedded line-breaking / tab whitespace in a single field value to one space. Leaves all
|
|
3
|
+
* other characters (including ordinary spaces and non-ASCII text) untouched.
|
|
4
|
+
*
|
|
5
|
+
* Scope note: this folds ONLY the line-breaking / tab run above — its sole job is to keep a field on
|
|
6
|
+
* one logical line so it cannot break the structure of a line-oriented serializer. It does NOT strip
|
|
7
|
+
* other C0/C1/DEL control characters (NUL, BEL, backspace, …) or bidi/zero-width code points; the
|
|
8
|
+
* line-oriented Markdown / plain-text serializers assume candidate text is already control-clean and
|
|
9
|
+
* redacted by the upstream persist-time redactor (#278/#284), which is the single content-safety
|
|
10
|
+
* chokepoint. This helper is a structural folder, not a content scrubber.
|
|
11
|
+
*/
|
|
12
|
+
export declare function inlineField(value: string): string;
|
|
13
|
+
/** Apply {@link inlineField} across a list of field values. */
|
|
14
|
+
export declare function inlineFields(values: readonly string[]): string[];
|
|
15
|
+
//# sourceMappingURL=textSafety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textSafety.d.ts","sourceRoot":"","sources":["../../src/export/textSafety.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAEhE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Inline-field sanitisation for Quality Intelligence export serializers (Epic #711).
|
|
2
|
+
//
|
|
3
|
+
// Candidate fields (title, steps, preconditions, expected results, tags) are free text. A value that
|
|
4
|
+
// contains a line break or tab would break the structure of a line-oriented serializer — a newline
|
|
5
|
+
// inside a Markdown list item silently terminates the list; a newline inside a plain-text or
|
|
6
|
+
// key/value row splits one field across two lines. `inlineField` folds any run of line-breaking or
|
|
7
|
+
// tab whitespace into a single space so each field renders as exactly one logical unit, without
|
|
8
|
+
// otherwise altering the content. Pure, deterministic; NO IO, NO new runtime dependency.
|
|
9
|
+
// Matching control whitespace (CR, LF, tab, vertical tab, form feed) and the Unicode line
|
|
10
|
+
// separators is the entire purpose of this helper, so the control characters are intentional.
|
|
11
|
+
// eslint-disable-next-line no-control-regex
|
|
12
|
+
const LINE_BREAKING_RUN = /[\r\n\t\u000b\f\u0085\u2028\u2029]+/gu;
|
|
13
|
+
/**
|
|
14
|
+
* Collapse embedded line-breaking / tab whitespace in a single field value to one space. Leaves all
|
|
15
|
+
* other characters (including ordinary spaces and non-ASCII text) untouched.
|
|
16
|
+
*
|
|
17
|
+
* Scope note: this folds ONLY the line-breaking / tab run above — its sole job is to keep a field on
|
|
18
|
+
* one logical line so it cannot break the structure of a line-oriented serializer. It does NOT strip
|
|
19
|
+
* other C0/C1/DEL control characters (NUL, BEL, backspace, …) or bidi/zero-width code points; the
|
|
20
|
+
* line-oriented Markdown / plain-text serializers assume candidate text is already control-clean and
|
|
21
|
+
* redacted by the upstream persist-time redactor (#278/#284), which is the single content-safety
|
|
22
|
+
* chokepoint. This helper is a structural folder, not a content scrubber.
|
|
23
|
+
*/
|
|
24
|
+
export function inlineField(value) {
|
|
25
|
+
return value.replace(LINE_BREAKING_RUN, " ");
|
|
26
|
+
}
|
|
27
|
+
/** Apply {@link inlineField} across a list of field values. */
|
|
28
|
+
export function inlineFields(values) {
|
|
29
|
+
return values.map(inlineField);
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const GENERATED_CANDIDATE_RESPONSE_MAX_ITEMS = 1024;
|
|
2
|
+
export declare const GENERATED_CANDIDATE_TITLE_MAX_CHARS = 256;
|
|
3
|
+
export declare const GENERATED_CANDIDATE_TEXT_ITEM_MAX_CHARS = 1000;
|
|
4
|
+
export declare const GENERATED_CANDIDATE_TAG_MAX_CHARS = 120;
|
|
5
|
+
export declare const GENERATED_CANDIDATE_PRECONDITION_MAX_ITEMS = 20;
|
|
6
|
+
export declare const GENERATED_CANDIDATE_STEP_MAX_ITEMS = 50;
|
|
7
|
+
export declare const GENERATED_CANDIDATE_EXPECTED_RESULT_MAX_ITEMS = 50;
|
|
8
|
+
export declare const GENERATED_CANDIDATE_TAG_MAX_ITEMS = 30;
|
|
9
|
+
export declare const GENERATED_CANDIDATE_EVIDENCE_INDEX_MAX_ITEMS = 120;
|
|
10
|
+
//# sourceMappingURL=candidateBounds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidateBounds.d.ts","sourceRoot":"","sources":["../../src/generation/candidateBounds.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,sCAAsC,OAAO,CAAC;AAC3D,eAAO,MAAM,mCAAmC,MAAM,CAAC;AACvD,eAAO,MAAM,uCAAuC,OAAO,CAAC;AAC5D,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,0CAA0C,KAAK,CAAC;AAC7D,eAAO,MAAM,kCAAkC,KAAK,CAAC;AACrD,eAAO,MAAM,6CAA6C,KAAK,CAAC;AAChE,eAAO,MAAM,iCAAiC,KAAK,CAAC;AACpD,eAAO,MAAM,4CAA4C,MAAM,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Shared bounds for model-generated Quality Intelligence candidates.
|
|
2
|
+
//
|
|
3
|
+
// These limits keep generated test cases concise enough for persistence, judge prompts, exports, and
|
|
4
|
+
// UI review while still allowing rich manual test steps. They intentionally mirror the stricter UI
|
|
5
|
+
// projection/edit-route envelope instead of the wider raw-provider payload size.
|
|
6
|
+
export const GENERATED_CANDIDATE_RESPONSE_MAX_ITEMS = 1024;
|
|
7
|
+
export const GENERATED_CANDIDATE_TITLE_MAX_CHARS = 256;
|
|
8
|
+
export const GENERATED_CANDIDATE_TEXT_ITEM_MAX_CHARS = 1000;
|
|
9
|
+
export const GENERATED_CANDIDATE_TAG_MAX_CHARS = 120;
|
|
10
|
+
export const GENERATED_CANDIDATE_PRECONDITION_MAX_ITEMS = 20;
|
|
11
|
+
export const GENERATED_CANDIDATE_STEP_MAX_ITEMS = 50;
|
|
12
|
+
export const GENERATED_CANDIDATE_EXPECTED_RESULT_MAX_ITEMS = 50;
|
|
13
|
+
export const GENERATED_CANDIDATE_TAG_MAX_ITEMS = 30;
|
|
14
|
+
export const GENERATED_CANDIDATE_EVIDENCE_INDEX_MAX_ITEMS = 120;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { QI_TEST_DESIGN_SYSTEM_PROMPT, QI_TEST_DESIGN_RESPONSE_SCHEMA, buildTestDesignInstruction, type BuildTestDesignInstructionInput, } from "./prompt.js";
|
|
2
|
+
export { parseGeneratedCandidates, type ParseGeneratedCandidatesInput, type ParseGeneratedCandidatesResult, } from "./parseGeneratedCandidates.js";
|
|
3
|
+
export { splitRequirementsIntoAtoms, type IngestedRequirementAtom, type SplitRequirementsOptions, } from "./requirementsIngestion.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generation/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,4BAA4B,EAC5B,8BAA8B,EAC9B,0BAA0B,EAC1B,KAAK,+BAA+B,GACrC,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,wBAAwB,EACxB,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,GACpC,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACL,0BAA0B,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,GAC9B,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Public barrel for the Quality Intelligence model-routed generation sub-namespace
|
|
2
|
+
// (Epic #270, Issue #272/#278/#279). Pure-domain prompt assembly, requirements-text ingestion,
|
|
3
|
+
// and model-output parsing. No IO, no model call; the server tier supplies the gateway port.
|
|
4
|
+
//
|
|
5
|
+
// DETERMINISM-FIRST INVARIANT (Epic #761, Issue #763):
|
|
6
|
+
// - The structural stages — coverage mapping, deduplication, validation, and candidate-ID
|
|
7
|
+
// derivation — are 100% deterministic and replayable: `parseGeneratedCandidates` derives every
|
|
8
|
+
// candidate id from a content hash (sha256 of ordinal + title + cited atoms), so identical model
|
|
9
|
+
// text over identical evidence yields identical ids regardless of run id, model, seed, or sampling
|
|
10
|
+
// temperature.
|
|
11
|
+
// - The model is invoked ONLY to draft candidate text; its output is an attributed delta. The
|
|
12
|
+
// evidence manifest records which model produced it (`modelId`), the request parameters used
|
|
13
|
+
// (`modelParameters`, e.g. responseFormat), and the seed (`seedUsed`, null when unsupported).
|
|
14
|
+
// - Where the model supports seeding, the same inputs + seed reproduce the same sampling; where it
|
|
15
|
+
// does not, the content-hashed ids still make the run replayable for coverage/dedup/validate.
|
|
16
|
+
// - No model configured → the deterministic baseline still holds (judge accepts all, refinement
|
|
17
|
+
// skipped); only the drafting step is unavailable.
|
|
18
|
+
export { QI_TEST_DESIGN_SYSTEM_PROMPT, QI_TEST_DESIGN_RESPONSE_SCHEMA, buildTestDesignInstruction, } from "./prompt.js";
|
|
19
|
+
export { parseGeneratedCandidates, } from "./parseGeneratedCandidates.js";
|
|
20
|
+
export { splitRequirementsIntoAtoms, } from "./requirementsIngestion.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { QualityIntelligence } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
import type { PolicyProfile } from "../domain/policyProfile.js";
|
|
3
|
+
type Candidate = QualityIntelligence.QualityIntelligenceTestCaseCandidate;
|
|
4
|
+
type RunId = QualityIntelligence.QualityIntelligenceRunId;
|
|
5
|
+
type AtomId = QualityIntelligence.QualityIntelligenceEvidenceAtomId;
|
|
6
|
+
export interface ParseGeneratedCandidatesInput {
|
|
7
|
+
readonly runId: RunId;
|
|
8
|
+
/** Atom IDs in the SAME order they were numbered (1-based) in the prompt evidence block. */
|
|
9
|
+
readonly atomIds: readonly AtomId[];
|
|
10
|
+
readonly profile?: PolicyProfile;
|
|
11
|
+
readonly maxCandidates: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ParseGeneratedCandidatesResult {
|
|
14
|
+
readonly candidates: readonly Candidate[];
|
|
15
|
+
/** True when no JSON object/array could be recovered from the model text at all. */
|
|
16
|
+
readonly recovered: boolean;
|
|
17
|
+
/** Count of raw items skipped because they lacked a usable title or steps. */
|
|
18
|
+
readonly skipped: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse raw model output into validated candidates. Returns `recovered: false` when no JSON value
|
|
22
|
+
* could be located, so the orchestrator can fail the run with a clear, non-secret reason instead
|
|
23
|
+
* of silently emitting zero candidates.
|
|
24
|
+
*/
|
|
25
|
+
export declare const parseGeneratedCandidates: (rawText: string, input: ParseGeneratedCandidatesInput) => ParseGeneratedCandidatesResult;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=parseGeneratedCandidates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseGeneratedCandidates.d.ts","sourceRoot":"","sources":["../../src/generation/parseGeneratedCandidates.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAIpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAYhE,KAAK,SAAS,GAAG,mBAAmB,CAAC,oCAAoC,CAAC;AAC1E,KAAK,KAAK,GAAG,mBAAmB,CAAC,wBAAwB,CAAC;AAC1D,KAAK,MAAM,GAAG,mBAAmB,CAAC,iCAAiC,CAAC;AAWpE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,oFAAoF;IACpF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AA2QD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,MAAM,EACf,OAAO,6BAA6B,KACnC,8BAqBF,CAAC"}
|