project-tiny-context-harness 0.8.0 → 0.8.2
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/README.md +40 -32
- package/assets/README.md +141 -60
- package/assets/README.zh-CN.md +48 -40
- package/assets/agents/AGENTS_CORE.md +16 -12
- package/assets/context_templates/architecture.md +5 -5
- package/assets/context_templates/area.md +5 -5
- package/assets/context_templates/global.md +6 -6
- package/assets/context_templates/screen-contract.md +18 -18
- package/assets/github/harness.yml +2 -2
- package/assets/skills/context_development_engineer/SKILL.md +7 -5
- package/assets/skills/context_full_project_export/SKILL.md +42 -42
- package/assets/skills/context_uiux_design/SKILL.md +27 -13
- package/assets/skills/design-resource-authoring/SKILL.md +7 -7
- package/assets/skills/design-resource-authoring/references/downstream-handoff.md +12 -12
- package/assets/skills/design-resource-authoring/references/open-design-provider.md +21 -21
- package/assets/skills/design-resource-authoring/references/resource-selection.md +3 -3
- package/assets/skills/long-task-workflow/SKILL.md +17 -17
- package/assets/skills/long-task-workflow/agents/openai.yaml +4 -4
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +11 -9
- package/assets/skills/long-task-workflow/references/contract-authoring.md +19 -15
- package/assets/skills/long-task-workflow/references/evidence-design.md +18 -17
- package/assets/skills/long-task-workflow/references/source-authoring.md +15 -12
- package/assets/skills/normal-long-task/SKILL.md +12 -12
- package/dist/commands/design-resource.js +2 -2
- package/dist/commands/index.js +26 -26
- package/dist/commands/long-task-authoring.js +83 -9
- package/dist/commands/long-task.js +15 -15
- package/dist/lib/context-graph-snapshot.js +7 -0
- package/dist/lib/design-md.js +5 -4
- package/dist/lib/design-resource-handoff-shape-evidence.js +3 -0
- package/dist/lib/design-resource-handoff-shape-primitives.js +1 -3
- package/dist/lib/design-resource-handoff-types.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-coverage.js +1 -0
- package/dist/lib/design-resource-handoff-validation-structure.js +2 -0
- package/dist/lib/doctor.js +2 -2
- package/dist/lib/execution-target-capabilities.d.ts +2 -0
- package/dist/lib/execution-target-capabilities.js +31 -0
- package/dist/lib/long-task-acceptance-shape.js +81 -20
- package/dist/lib/long-task-applicability-shape.d.ts +4 -0
- package/dist/lib/long-task-applicability-shape.js +54 -0
- package/dist/lib/long-task-assertions-v2.js +8 -0
- package/dist/lib/long-task-authoring-preflight-diagnostics.js +5 -0
- package/dist/lib/long-task-authoring-preflight-repair-order.js +3 -0
- package/dist/lib/long-task-authoring-preflight.js +12 -8
- package/dist/lib/long-task-authority-materials.js +46 -4
- package/dist/lib/long-task-authority-policy.d.ts +54 -0
- package/dist/lib/long-task-authority-policy.js +61 -0
- package/dist/lib/long-task-authority-types.d.ts +12 -2
- package/dist/lib/long-task-authority.js +9 -2
- package/dist/lib/long-task-check-shape.js +6 -1
- package/dist/lib/long-task-claim-definitions.js +27 -42
- package/dist/lib/long-task-claims.js +275 -146
- package/dist/lib/long-task-contract-types.d.ts +42 -8
- package/dist/lib/long-task-control-fields.d.ts +12 -0
- package/dist/lib/long-task-control-fields.js +66 -0
- package/dist/lib/long-task-control-types.d.ts +1 -0
- package/dist/lib/long-task-control-types.js +1 -0
- package/dist/lib/long-task-counterfactual-claim-policy.js +7 -0
- package/dist/lib/long-task-counterfactual-sandbox.js +1 -2
- package/dist/lib/long-task-counterfactual-types.d.ts +12 -0
- package/dist/lib/long-task-delivery-compiler.js +1 -0
- package/dist/lib/long-task-delivery-preflight.js +24 -0
- package/dist/lib/long-task-delivery-shape.d.ts +1 -0
- package/dist/lib/long-task-delivery-shape.js +1 -0
- package/dist/lib/long-task-delivery-types.d.ts +1 -0
- package/dist/lib/long-task-delivery-types.js +1 -0
- package/dist/lib/long-task-delivery-validation.js +46 -0
- package/dist/lib/long-task-design-resource-handoff.js +48 -7
- package/dist/lib/long-task-evidence-capability-codec.js +37 -0
- package/dist/lib/long-task-evidence-capability-policy.js +80 -38
- package/dist/lib/long-task-evidence-capability-runtime.js +36 -0
- package/dist/lib/long-task-evidence-capability-types.d.ts +13 -1
- package/dist/lib/long-task-evidence-findings.js +3 -0
- package/dist/lib/long-task-evidence-sensitivity-policy.js +38 -0
- package/dist/lib/long-task-evidence-v2.js +7 -1
- package/dist/lib/long-task-final-integrity.d.ts +16 -0
- package/dist/lib/long-task-final-integrity.js +98 -0
- package/dist/lib/long-task-final-v2.js +17 -34
- package/dist/lib/long-task-outcome-parser.js +22 -4
- package/dist/lib/long-task-playwright-case-evidence.d.ts +2 -0
- package/dist/lib/long-task-playwright-case-evidence.js +10 -2
- package/dist/lib/long-task-playwright-evidence.d.ts +1 -0
- package/dist/lib/long-task-playwright-evidence.js +32 -0
- package/dist/lib/long-task-product-shape.d.ts +3 -1
- package/dist/lib/long-task-product-shape.js +100 -4
- package/dist/lib/long-task-requirement-shape.js +3 -0
- package/dist/lib/long-task-root-shape.js +12 -6
- package/dist/lib/long-task-runner-freeze.js +16 -0
- package/dist/lib/long-task-runtime-types.d.ts +11 -2
- package/dist/lib/long-task-semantic-assurance-policy.d.ts +4 -0
- package/dist/lib/long-task-semantic-assurance-policy.js +52 -0
- package/dist/lib/long-task-semantic-contract-types.d.ts +18 -1
- package/dist/lib/long-task-semantic-drift-migration.js +33 -2
- package/dist/lib/long-task-semantic-mutation.d.ts +7 -0
- package/dist/lib/long-task-semantic-mutation.js +81 -0
- package/dist/lib/long-task-shape-primitives.d.ts +1 -1
- package/dist/lib/long-task-shape-primitives.js +1 -0
- package/dist/lib/long-task-source-authority-types.d.ts +2 -0
- package/dist/lib/long-task-source-item-parser.d.ts +1 -1
- package/dist/lib/long-task-source-item-parser.js +150 -104
- package/dist/lib/long-task-source-markers.d.ts +12 -0
- package/dist/lib/long-task-source-markers.js +146 -0
- package/dist/lib/long-task-source-target-continuity.d.ts +1 -1
- package/dist/lib/long-task-source-target-continuity.js +80 -55
- package/dist/lib/long-task-source-target-index.js +7 -29
- package/dist/lib/long-task-status-v2.d.ts +1 -0
- package/dist/lib/long-task-status-v2.js +2 -2
- package/dist/lib/long-task-target-policy.js +20 -0
- package/dist/lib/long-task-ui-design-policy.js +27 -1
- package/dist/lib/long-task-ui-surface-shape.js +21 -1
- package/dist/lib/long-task-ui-surface-types.d.ts +7 -0
- package/dist/lib/long-task-verification-preview.d.ts +10 -0
- package/dist/lib/long-task-verifier-dependency-closure.d.ts +3 -0
- package/dist/lib/long-task-verifier-dependency-closure.js +179 -0
- package/dist/lib/long-task-verifier-v2.js +12 -2
- package/dist/lib/long-task-workspace.js +5 -1
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +99 -25
- package/migrations/README.md +8 -8
- package/package.json +84 -84
|
@@ -1,161 +1,29 @@
|
|
|
1
1
|
import { assertAllClaimsCovered, assertAllGlobalClaimsCovered, generateClaims, generateGlobalClaims, validateGlobalProofPolarity, validateProofSurface, } from "./long-task-claim-definitions.js";
|
|
2
2
|
import { fail } from "./long-task-delivery-shape.js";
|
|
3
3
|
import { validateClaimAssertionOperator, validateGlobalAssertionOperator, } from "./long-task-claim-proof-policy.js";
|
|
4
|
+
import { matchesRepoPattern } from "./long-task-paths.js";
|
|
4
5
|
export function compileProductClaimCoverage(contract, options = {}) {
|
|
5
6
|
const byGlobal = generateGlobalClaims(contract.global);
|
|
6
7
|
const byOutcome = Object.fromEntries(contract.outcomes.map((outcome) => [outcome.key, generateClaims(outcome)]));
|
|
7
8
|
for (const [outcomeKey, claims] of Object.entries(byOutcome))
|
|
8
9
|
if (!claims.some((claim) => claim.kind !== "result"))
|
|
9
10
|
fail("outcome_atomic_claim_required", outcomeKey);
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
.flat()
|
|
13
|
-
.map((claim) => claim.id));
|
|
14
|
-
const globalProofs = new Map();
|
|
15
|
-
const addGlobalProof = (claimKey, proof) => {
|
|
16
|
-
const claim = globalClaimMap.get(claimKey);
|
|
17
|
-
if (!claim) {
|
|
18
|
-
if (outcomeClaimIds.has(claimKey))
|
|
19
|
-
fail("global_assertion_claim_cross_scope", claimKey);
|
|
20
|
-
fail("global_assertion_claim_unknown", claimKey);
|
|
21
|
-
}
|
|
22
|
-
validateGlobalProofPolarity(claim, proof);
|
|
23
|
-
const rows = globalProofs.get(claimKey) ?? [];
|
|
24
|
-
rows.push(proof);
|
|
25
|
-
globalProofs.set(claimKey, rows);
|
|
26
|
-
};
|
|
27
|
-
for (const check of contract.global.acceptance.checks) {
|
|
28
|
-
const assertionKeys = new Set();
|
|
29
|
-
for (const [polarity, assertions] of [
|
|
30
|
-
["positive", check.positive_assertions],
|
|
31
|
-
["negative", check.negative_assertions],
|
|
32
|
-
])
|
|
33
|
-
for (const assertion of assertions) {
|
|
34
|
-
if (assertionKeys.has(assertion.key))
|
|
35
|
-
fail("assertion_key_duplicate", `GLOBAL:${check.key}:${assertion.key}`);
|
|
36
|
-
assertionKeys.add(assertion.key);
|
|
37
|
-
validateGlobalAssertionOperator(assertion, check.key);
|
|
38
|
-
for (const claimKey of assertion.claims)
|
|
39
|
-
addGlobalProof(claimKey, {
|
|
40
|
-
check_key: check.key,
|
|
41
|
-
assertion_key: assertion.key,
|
|
42
|
-
polarity,
|
|
43
|
-
proof_surface: check.proof_surface,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const globalRows = {};
|
|
48
|
-
const uncoveredGlobal = [];
|
|
49
|
-
for (const claim of byGlobal) {
|
|
50
|
-
const proofs = globalProofs.get(claim.local_key) ?? [];
|
|
51
|
-
const covered = proofs.length > 0;
|
|
52
|
-
globalRows[claim.local_key] = { covered, proofs };
|
|
53
|
-
if (!covered)
|
|
54
|
-
uncoveredGlobal.push(claim.id);
|
|
55
|
-
}
|
|
11
|
+
const globalProfileMap = validateProfiles("GLOBAL", contract.global.applicability, contract.global.acceptance.checks, contract.task.execution_targets.map((target) => target.key));
|
|
12
|
+
const globalRows = compileGlobalScope(contract, byGlobal, globalProfileMap);
|
|
56
13
|
const outcomeKeys = new Set(contract.outcomes.map((outcome) => outcome.key));
|
|
57
14
|
const globalLocalKeys = new Set(byGlobal.map((claim) => claim.local_key));
|
|
58
|
-
const
|
|
59
|
-
const uncoveredOutcome = [];
|
|
15
|
+
const outcomeRows = {};
|
|
60
16
|
for (const outcome of contract.outcomes) {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const proofs = new Map();
|
|
64
|
-
const addProof = (claimKey, proof, observation, assertion) => {
|
|
65
|
-
const claim = claimMap.get(claimKey);
|
|
66
|
-
if (!claim) {
|
|
67
|
-
if (globalLocalKeys.has(claimKey))
|
|
68
|
-
fail("assertion_claim_cross_scope", `${outcome.key}:${claimKey}`);
|
|
69
|
-
const first = claimKey.split(".")[0];
|
|
70
|
-
if (outcomeKeys.has(first) && first !== outcome.key)
|
|
71
|
-
fail("assertion_claim_cross_outcome", `${outcome.key}:${claimKey}`);
|
|
72
|
-
fail("assertion_claim_unknown", `${outcome.key}:${claimKey}`);
|
|
73
|
-
}
|
|
74
|
-
if (observation === "playwright.passed" &&
|
|
75
|
-
(claim.kind === "requirement" || claim.kind === "control"))
|
|
76
|
-
fail("fine_grained_claim_requires_ac_observation", `${outcome.key}:${claim.local_key}`);
|
|
77
|
-
validateProofSurface(claim, proof, outcome.key);
|
|
78
|
-
if (assertion)
|
|
79
|
-
validateClaimAssertionOperator(claim, assertion, proof.proof_surface, outcome.key, proof.check_key);
|
|
80
|
-
const rows = proofs.get(claimKey) ?? [];
|
|
81
|
-
rows.push(proof);
|
|
82
|
-
proofs.set(claimKey, rows);
|
|
83
|
-
};
|
|
84
|
-
for (const check of outcome.acceptance.checks) {
|
|
85
|
-
const assertionKeys = new Set();
|
|
86
|
-
for (const [polarity, assertions] of [
|
|
87
|
-
["positive", check.positive_assertions],
|
|
88
|
-
["negative", check.negative_assertions],
|
|
89
|
-
]) {
|
|
90
|
-
for (const assertion of assertions) {
|
|
91
|
-
if (assertionKeys.has(assertion.key))
|
|
92
|
-
fail("assertion_key_duplicate", `${outcome.key}:${check.key}:${assertion.key}`);
|
|
93
|
-
assertionKeys.add(assertion.key);
|
|
94
|
-
for (const claimKey of assertion.claims)
|
|
95
|
-
addProof(claimKey, {
|
|
96
|
-
check_key: check.key,
|
|
97
|
-
assertion_key: assertion.key,
|
|
98
|
-
polarity,
|
|
99
|
-
proof_surface: check.proof_surface,
|
|
100
|
-
}, assertion.observation, assertion);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (outcome.acceptance.population) {
|
|
105
|
-
const population = outcome.acceptance.population;
|
|
106
|
-
const check = outcome.acceptance.checks.find((candidate) => candidate.key === population.check_key);
|
|
107
|
-
if (!check)
|
|
108
|
-
fail("outcome_check_reference_unknown", `${outcome.key}:${population.check_key}`);
|
|
109
|
-
for (const claimKey of population.claims)
|
|
110
|
-
addProof(claimKey, {
|
|
111
|
-
check_key: check.key,
|
|
112
|
-
assertion_key: null,
|
|
113
|
-
polarity: "population",
|
|
114
|
-
proof_surface: check.proof_surface,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
for (const counterfactual of outcome.acceptance.counterfactual_controls) {
|
|
118
|
-
const check = outcome.acceptance.checks.find((candidate) => candidate.key === counterfactual.check_key);
|
|
119
|
-
if (!check)
|
|
120
|
-
fail("outcome_check_reference_unknown", `${outcome.key}:${counterfactual.check_key}`);
|
|
121
|
-
const assertionKeys = new Set([...check.positive_assertions, ...check.negative_assertions].map((assertion) => assertion.key));
|
|
122
|
-
if (!counterfactual.expected_assertion_failures.length)
|
|
123
|
-
fail("counterfactual_expected_assertion_required", `${outcome.key}:${counterfactual.key}`);
|
|
124
|
-
for (const assertionKey of counterfactual.expected_assertion_failures)
|
|
125
|
-
if (!assertionKeys.has(assertionKey))
|
|
126
|
-
fail("counterfactual_assertion_unknown", `${outcome.key}:${counterfactual.key}:${assertionKey}`);
|
|
127
|
-
for (const claimKey of counterfactual.claims)
|
|
128
|
-
addProof(claimKey, {
|
|
129
|
-
check_key: check.key,
|
|
130
|
-
assertion_key: counterfactual.expected_assertion_failures.join(","),
|
|
131
|
-
polarity: "counterfactual",
|
|
132
|
-
proof_surface: check.proof_surface,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
const rows = {};
|
|
136
|
-
for (const claim of claims) {
|
|
137
|
-
const claimProofs = proofs.get(claim.local_key) ?? [];
|
|
138
|
-
const requiredSurfaces = [...claim.required_proof_surfaces].sort();
|
|
139
|
-
const coveredSurfaces = [
|
|
140
|
-
...new Set(claimProofs.map((proof) => proof.proof_surface)),
|
|
141
|
-
].sort();
|
|
142
|
-
const missingSurfaces = requiredSurfaces.filter((surface) => !coveredSurfaces.includes(surface));
|
|
143
|
-
const covered = claimProofs.length > 0 &&
|
|
144
|
-
(requiredSurfaces.length === 0 || missingSurfaces.length === 0);
|
|
145
|
-
rows[claim.local_key] = {
|
|
146
|
-
required_surfaces: requiredSurfaces,
|
|
147
|
-
covered_surfaces: coveredSurfaces,
|
|
148
|
-
missing_surfaces: missingSurfaces,
|
|
149
|
-
covered,
|
|
150
|
-
proofs: claimProofs,
|
|
151
|
-
};
|
|
152
|
-
if (claimProofs.length && missingSurfaces.length)
|
|
153
|
-
fail("product_claim_required_surfaces_missing", `${outcome.key}:${claim.local_key}:${missingSurfaces.join(",")}`);
|
|
154
|
-
if (!covered)
|
|
155
|
-
uncoveredOutcome.push(claim.id);
|
|
156
|
-
}
|
|
157
|
-
summaryRows[outcome.key] = rows;
|
|
17
|
+
const profiles = validateProfiles(outcome.key, outcome.applicability, outcome.acceptance.checks, contract.task.execution_targets.map((target) => target.key));
|
|
18
|
+
outcomeRows[outcome.key] = compileOutcomeScope(contract, outcome.key, byOutcome[outcome.key], profiles, outcomeKeys, globalLocalKeys);
|
|
158
19
|
}
|
|
20
|
+
const uncoveredGlobal = byGlobal
|
|
21
|
+
.filter((claim) => !globalRows[claim.local_key]?.covered)
|
|
22
|
+
.map((claim) => claim.id);
|
|
23
|
+
const uncoveredOutcome = Object.values(byOutcome)
|
|
24
|
+
.flat()
|
|
25
|
+
.filter((claim) => !outcomeRows[claim.outcome_key]?.[claim.local_key]?.covered)
|
|
26
|
+
.map((claim) => claim.id);
|
|
159
27
|
const uncovered = [...uncoveredGlobal, ...uncoveredOutcome].sort();
|
|
160
28
|
const total = byGlobal.length +
|
|
161
29
|
Object.values(byOutcome).reduce((sum, claims) => sum + claims.length, 0);
|
|
@@ -167,14 +35,264 @@ export function compileProductClaimCoverage(contract, options = {}) {
|
|
|
167
35
|
claims_covered: total - uncovered.length,
|
|
168
36
|
uncovered_claims: uncovered,
|
|
169
37
|
claims_by_global: globalRows,
|
|
170
|
-
claims_by_outcome:
|
|
38
|
+
claims_by_outcome: outcomeRows,
|
|
171
39
|
},
|
|
172
40
|
};
|
|
173
41
|
if (!options.allow_uncovered)
|
|
174
42
|
assertCompiledClaimsCovered(compiled);
|
|
175
43
|
return compiled;
|
|
176
44
|
}
|
|
45
|
+
function compileGlobalScope(contract, claims, profiles) {
|
|
46
|
+
const claimMap = new Map(claims.map((claim) => [claim.local_key, claim]));
|
|
47
|
+
const outcomeClaimIds = new Set(contract.outcomes.flatMap((outcome) => generateClaims(outcome).map((claim) => claim.id)));
|
|
48
|
+
const proofs = new Map();
|
|
49
|
+
for (const proof of scopeAssertions(contract.global.acceptance.checks)) {
|
|
50
|
+
validateAssertionArity(proof.assertion, "GLOBAL", proof.check.key);
|
|
51
|
+
if (!proof.assertion.claims.length)
|
|
52
|
+
continue;
|
|
53
|
+
const claimKey = proof.assertion.claims[0];
|
|
54
|
+
const claim = claimMap.get(claimKey);
|
|
55
|
+
if (!claim) {
|
|
56
|
+
if (outcomeClaimIds.has(claimKey))
|
|
57
|
+
fail("global_assertion_claim_cross_scope", claimKey);
|
|
58
|
+
fail("global_assertion_claim_unknown", claimKey);
|
|
59
|
+
}
|
|
60
|
+
const applicabilityRef = validateAssertionApplicability(claim.applicability_refs, proof, profiles, "GLOBAL");
|
|
61
|
+
const claimProof = {
|
|
62
|
+
check_key: proof.check.key,
|
|
63
|
+
assertion_key: proof.assertion.key,
|
|
64
|
+
polarity: proof.polarity,
|
|
65
|
+
proof_surface: proof.check.proof_surface,
|
|
66
|
+
applicability_ref: applicabilityRef,
|
|
67
|
+
};
|
|
68
|
+
validateGlobalProofPolarity(claim, claimProof);
|
|
69
|
+
validateGlobalAssertionOperator(proof.assertion, proof.check.key);
|
|
70
|
+
addProof(proofs, claimKey, claimProof);
|
|
71
|
+
}
|
|
72
|
+
validateProfileUse("GLOBAL", profiles, claims);
|
|
73
|
+
return Object.fromEntries(claims.map((claim) => {
|
|
74
|
+
const claimProofs = proofs.get(claim.local_key) ?? [];
|
|
75
|
+
const coverage = applicabilityCoverage(claim.applicability_refs, claimProofs, [], claim.required_polarity);
|
|
76
|
+
return [
|
|
77
|
+
claim.local_key,
|
|
78
|
+
{
|
|
79
|
+
covered: coverage.uncovered.length === 0,
|
|
80
|
+
applicability_refs: [...claim.applicability_refs].sort(),
|
|
81
|
+
uncovered_applicability_refs: coverage.uncovered,
|
|
82
|
+
proofs: claimProofs,
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
function compileOutcomeScope(contract, outcomeKey, claims, profiles, outcomeKeys, globalLocalKeys) {
|
|
88
|
+
const outcome = contract.outcomes.find((candidate) => candidate.key === outcomeKey);
|
|
89
|
+
const claimMap = new Map(claims.map((claim) => [claim.local_key, claim]));
|
|
90
|
+
const proofs = new Map();
|
|
91
|
+
for (const proof of scopeAssertions(outcome.acceptance.checks)) {
|
|
92
|
+
validateAssertionArity(proof.assertion, outcomeKey, proof.check.key);
|
|
93
|
+
if (!proof.assertion.claims.length)
|
|
94
|
+
continue;
|
|
95
|
+
const claimKey = proof.assertion.claims[0];
|
|
96
|
+
const claim = claimMap.get(claimKey);
|
|
97
|
+
if (!claim) {
|
|
98
|
+
if (globalLocalKeys.has(claimKey))
|
|
99
|
+
fail("assertion_claim_cross_scope", `${outcomeKey}:${claimKey}`);
|
|
100
|
+
const first = claimKey.split(".")[0];
|
|
101
|
+
if (outcomeKeys.has(first) && first !== outcomeKey)
|
|
102
|
+
fail("assertion_claim_cross_outcome", `${outcomeKey}:${claimKey}`);
|
|
103
|
+
fail("assertion_claim_unknown", `${outcomeKey}:${claimKey}`);
|
|
104
|
+
}
|
|
105
|
+
if (proof.assertion.observation === "playwright.passed" &&
|
|
106
|
+
(claim.kind === "requirement" ||
|
|
107
|
+
claim.kind === "control" ||
|
|
108
|
+
claim.kind === "control_relation"))
|
|
109
|
+
fail("fine_grained_claim_requires_ac_observation", `${outcomeKey}:${claim.local_key}`);
|
|
110
|
+
const applicabilityRef = validateAssertionApplicability(claim.applicability_refs, proof, profiles, outcomeKey);
|
|
111
|
+
const claimProof = {
|
|
112
|
+
check_key: proof.check.key,
|
|
113
|
+
assertion_key: proof.assertion.key,
|
|
114
|
+
polarity: proof.polarity,
|
|
115
|
+
proof_surface: proof.check.proof_surface,
|
|
116
|
+
applicability_ref: applicabilityRef,
|
|
117
|
+
};
|
|
118
|
+
validateProofSurface(claim, claimProof, outcomeKey);
|
|
119
|
+
validateClaimAssertionOperator(claim, proof.assertion, proof.check.proof_surface, outcomeKey, proof.check.key);
|
|
120
|
+
addProof(proofs, claimKey, claimProof);
|
|
121
|
+
}
|
|
122
|
+
validatePopulationReferences(outcome, claimMap);
|
|
123
|
+
validateCounterfactualReferences(outcome, claimMap);
|
|
124
|
+
validateProfileUse(outcomeKey, profiles, claims);
|
|
125
|
+
return Object.fromEntries(claims.map((claim) => {
|
|
126
|
+
const claimProofs = proofs.get(claim.local_key) ?? [];
|
|
127
|
+
const requiredSurfaces = [...claim.required_proof_surfaces].sort();
|
|
128
|
+
const coveredSurfaces = [
|
|
129
|
+
...new Set(claimProofs.map((proof) => proof.proof_surface)),
|
|
130
|
+
].sort();
|
|
131
|
+
const missingSurfaces = requiredSurfaces.filter((surface) => !coveredSurfaces.includes(surface));
|
|
132
|
+
const coverage = applicabilityCoverage(claim.applicability_refs, claimProofs, requiredSurfaces, claim.required_polarity);
|
|
133
|
+
return [
|
|
134
|
+
claim.local_key,
|
|
135
|
+
{
|
|
136
|
+
required_surfaces: requiredSurfaces,
|
|
137
|
+
covered_surfaces: coveredSurfaces,
|
|
138
|
+
missing_surfaces: missingSurfaces,
|
|
139
|
+
covered: coverage.uncovered.length === 0,
|
|
140
|
+
applicability_refs: [...claim.applicability_refs].sort(),
|
|
141
|
+
uncovered_applicability_refs: coverage.uncovered,
|
|
142
|
+
proofs: claimProofs,
|
|
143
|
+
},
|
|
144
|
+
];
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
function validateProfiles(scope, profiles, checks, targetRefs) {
|
|
148
|
+
const result = new Map();
|
|
149
|
+
const targets = new Set(targetRefs);
|
|
150
|
+
for (const profile of profiles) {
|
|
151
|
+
if (result.has(profile.key))
|
|
152
|
+
fail("claim_applicability_key_duplicate", `${scope}:${profile.key}`);
|
|
153
|
+
if (!targets.has(profile.target_ref))
|
|
154
|
+
fail("claim_applicability_target_unknown", `${scope}:${profile.key}:${profile.target_ref}`);
|
|
155
|
+
validateUniqueNonemptyRefs(profile.given_refs, "claim_applicability_given_refs", `${scope}:${profile.key}`);
|
|
156
|
+
validateUniqueNonemptyRefs(profile.when_refs, "claim_applicability_when_refs", `${scope}:${profile.key}`);
|
|
157
|
+
result.set(profile.key, profile);
|
|
158
|
+
}
|
|
159
|
+
validateScenarioStepSemantics(scope, checks);
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
function validateScenarioStepSemantics(scope, checks) {
|
|
163
|
+
for (const phase of ["given", "when"]) {
|
|
164
|
+
const semantics = new Map();
|
|
165
|
+
for (const check of checks) {
|
|
166
|
+
const seen = new Set();
|
|
167
|
+
for (const step of check.scenario[phase]) {
|
|
168
|
+
if (seen.has(step.key))
|
|
169
|
+
fail("check_scenario_step_duplicate", `${scope}:${check.key}:${phase}:${step.key}`);
|
|
170
|
+
seen.add(step.key);
|
|
171
|
+
const previous = semantics.get(step.key);
|
|
172
|
+
if (previous !== undefined && previous !== step.statement)
|
|
173
|
+
fail("check_scenario_step_semantics_conflict", `${scope}:${phase}:${step.key}`);
|
|
174
|
+
semantics.set(step.key, step.statement);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function validateAssertionArity(assertion, scope, checkKey) {
|
|
180
|
+
if (assertion.claims.length > 1)
|
|
181
|
+
fail("assertion_single_claim_required", `${scope}:${checkKey}:${assertion.key}`);
|
|
182
|
+
if (assertion.claims.length === 1 && !assertion.applicability_ref)
|
|
183
|
+
fail("assertion_applicability_ref_required", `${scope}:${checkKey}:${assertion.key}`);
|
|
184
|
+
if (!assertion.claims.length && assertion.applicability_ref)
|
|
185
|
+
fail("claimless_assertion_applicability_ref_forbidden", `${scope}:${checkKey}:${assertion.key}`);
|
|
186
|
+
}
|
|
187
|
+
function validateAssertionApplicability(claimRefs, proof, profiles, scope) {
|
|
188
|
+
validateUniqueNonemptyRefs(claimRefs, "claim_applicability_refs", `${scope}:${proof.assertion.claims[0]}`);
|
|
189
|
+
const reference = proof.assertion.applicability_ref;
|
|
190
|
+
if (!claimRefs.includes(reference))
|
|
191
|
+
fail("claim_assertion_applicability_not_declared", `${scope}:${proof.assertion.claims[0]}:${reference}`);
|
|
192
|
+
const profile = profiles.get(reference);
|
|
193
|
+
if (!profile)
|
|
194
|
+
fail("claim_applicability_ref_unknown", `${scope}:${proof.assertion.claims[0]}:${reference}`);
|
|
195
|
+
const label = `${scope}:${proof.check.key}:${proof.assertion.key}:${reference}`;
|
|
196
|
+
if (proof.check.execution_target.target_ref !== profile.target_ref)
|
|
197
|
+
fail("claim_applicability_target_mismatch", label);
|
|
198
|
+
if (!proof.check.journey_roles.includes(profile.journey_role))
|
|
199
|
+
fail("claim_applicability_journey_mismatch", label);
|
|
200
|
+
if (!sameSet(proof.check.scenario.given.map((step) => step.key), profile.given_refs))
|
|
201
|
+
fail("claim_applicability_given_mismatch", label);
|
|
202
|
+
if (proof.check.scenario.when.map((step) => step.key).join("\0") !==
|
|
203
|
+
profile.when_refs.join("\0"))
|
|
204
|
+
fail("claim_applicability_when_mismatch", label);
|
|
205
|
+
return reference;
|
|
206
|
+
}
|
|
207
|
+
function validatePopulationReferences(outcome, claims) {
|
|
208
|
+
const population = outcome.acceptance.population;
|
|
209
|
+
if (!population)
|
|
210
|
+
return;
|
|
211
|
+
const check = outcome.acceptance.checks.find((candidate) => candidate.key === population.check_key);
|
|
212
|
+
if (!check)
|
|
213
|
+
fail("outcome_check_reference_unknown", `${outcome.key}:${population.check_key}`);
|
|
214
|
+
const universeBinding = outcome.technical.bindings.find((binding) => binding.key === population.universe_binding_key);
|
|
215
|
+
if (!universeBinding)
|
|
216
|
+
fail("population_universe_binding_unknown", `${outcome.key}:${population.universe_binding_key}`);
|
|
217
|
+
const missingUniverseInputs = universeBinding.carrier_paths.filter((carrier) => !check.input_paths.some((pattern) => matchesRepoPattern(carrier, pattern)));
|
|
218
|
+
if (missingUniverseInputs.length)
|
|
219
|
+
fail("population_universe_carrier_input_missing", `${outcome.key}:${population.check_key}:${missingUniverseInputs.join(",")}`);
|
|
220
|
+
for (const claim of population.claims)
|
|
221
|
+
if (!claims.has(claim))
|
|
222
|
+
fail("assertion_claim_unknown", `${outcome.key}:${claim}`);
|
|
223
|
+
}
|
|
224
|
+
function validateCounterfactualReferences(outcome, claims) {
|
|
225
|
+
for (const control of outcome.acceptance.counterfactual_controls) {
|
|
226
|
+
const check = outcome.acceptance.checks.find((candidate) => candidate.key === control.check_key);
|
|
227
|
+
if (!check)
|
|
228
|
+
fail("outcome_check_reference_unknown", `${outcome.key}:${control.check_key}`);
|
|
229
|
+
const assertions = new Set([...check.positive_assertions, ...check.negative_assertions].map((assertion) => assertion.key));
|
|
230
|
+
if (!control.expected_assertion_failures.length)
|
|
231
|
+
fail("counterfactual_expected_assertion_required", `${outcome.key}:${control.key}`);
|
|
232
|
+
for (const assertion of [
|
|
233
|
+
...control.expected_assertion_failures,
|
|
234
|
+
...control.preserved_assertions,
|
|
235
|
+
])
|
|
236
|
+
if (!assertions.has(assertion))
|
|
237
|
+
fail("counterfactual_assertion_unknown", `${outcome.key}:${control.key}:${assertion}`);
|
|
238
|
+
for (const claim of control.claims)
|
|
239
|
+
if (!claims.has(claim))
|
|
240
|
+
fail("assertion_claim_unknown", `${outcome.key}:${claim}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function scopeAssertions(checks) {
|
|
244
|
+
return checks.flatMap((check) => [
|
|
245
|
+
...check.positive_assertions.map((assertion) => ({
|
|
246
|
+
check,
|
|
247
|
+
assertion,
|
|
248
|
+
polarity: "positive",
|
|
249
|
+
})),
|
|
250
|
+
...check.negative_assertions.map((assertion) => ({
|
|
251
|
+
check,
|
|
252
|
+
assertion,
|
|
253
|
+
polarity: "negative",
|
|
254
|
+
})),
|
|
255
|
+
]);
|
|
256
|
+
}
|
|
257
|
+
function applicabilityCoverage(refs, proofs, requiredSurfaces, requiredPolarity) {
|
|
258
|
+
const uncovered = refs.filter((reference) => {
|
|
259
|
+
const matching = proofs.filter((proof) => proof.applicability_ref === reference &&
|
|
260
|
+
proof.polarity === requiredPolarity);
|
|
261
|
+
if (!matching.length)
|
|
262
|
+
return true;
|
|
263
|
+
return requiredSurfaces.some((surface) => !matching.some((proof) => proof.proof_surface === surface));
|
|
264
|
+
});
|
|
265
|
+
return { uncovered: [...uncovered].sort() };
|
|
266
|
+
}
|
|
267
|
+
function validateProfileUse(scope, profiles, claims) {
|
|
268
|
+
const used = new Set(claims.flatMap((claim) => claim.applicability_refs));
|
|
269
|
+
for (const reference of used)
|
|
270
|
+
if (!profiles.has(reference))
|
|
271
|
+
fail("claim_applicability_ref_unknown", `${scope}:${reference}`);
|
|
272
|
+
for (const reference of profiles.keys())
|
|
273
|
+
if (!used.has(reference))
|
|
274
|
+
fail("claim_applicability_profile_unused", `${scope}:${reference}`);
|
|
275
|
+
}
|
|
276
|
+
function validateUniqueNonemptyRefs(refs, code, detail) {
|
|
277
|
+
if (!refs.length)
|
|
278
|
+
fail(`${code}_required`, detail);
|
|
279
|
+
if (new Set(refs).size !== refs.length)
|
|
280
|
+
fail(`${code}_duplicate`, detail);
|
|
281
|
+
}
|
|
282
|
+
function addProof(proofs, claim, proof) {
|
|
283
|
+
const rows = proofs.get(claim) ?? [];
|
|
284
|
+
rows.push(proof);
|
|
285
|
+
proofs.set(claim, rows);
|
|
286
|
+
}
|
|
287
|
+
function sameSet(left, right) {
|
|
288
|
+
return (left.length === right.length &&
|
|
289
|
+
new Set(left).size === left.length &&
|
|
290
|
+
left.every((item) => right.includes(item)));
|
|
291
|
+
}
|
|
177
292
|
export function assertCompiledClaimsCovered(compiled) {
|
|
293
|
+
for (const [outcomeKey, claims] of Object.entries(compiled.by_outcome))
|
|
294
|
+
for (const claim of claims)
|
|
295
|
+
assertRequiredSurfacesForEveryApplicability(outcomeKey, claim, compiled.summary.claims_by_outcome[outcomeKey]?.[claim.local_key]);
|
|
178
296
|
const uncoveredGlobal = compiled.by_global
|
|
179
297
|
.filter((claim) => !compiled.summary.claims_by_global[claim.local_key]?.covered)
|
|
180
298
|
.map((claim) => claim.id);
|
|
@@ -185,3 +303,14 @@ export function assertCompiledClaimsCovered(compiled) {
|
|
|
185
303
|
assertAllGlobalClaimsCovered(uncoveredGlobal);
|
|
186
304
|
assertAllClaimsCovered(uncoveredOutcome);
|
|
187
305
|
}
|
|
306
|
+
function assertRequiredSurfacesForEveryApplicability(scope, claim, summary) {
|
|
307
|
+
if (!summary || !claim.required_proof_surfaces.length)
|
|
308
|
+
return;
|
|
309
|
+
for (const applicabilityRef of claim.applicability_refs) {
|
|
310
|
+
const proofs = summary.proofs.filter((proof) => proof.applicability_ref === applicabilityRef &&
|
|
311
|
+
proof.polarity === claim.required_polarity);
|
|
312
|
+
const missing = claim.required_proof_surfaces.filter((surface) => !proofs.some((proof) => proof.proof_surface === surface));
|
|
313
|
+
if (missing.length)
|
|
314
|
+
fail("product_claim_required_surfaces_missing", `${scope}:${claim.local_key}:${applicabilityRef}:${missing.sort().join(",")}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LongTaskRiskFacts, RequestedRiskLevel } from "./long-task-risk-types.js";
|
|
2
2
|
import type { CounterfactualControlV2, GlobalCounterfactualControlV2 } from "./long-task-counterfactual-types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { DeliveryControlFieldNameV2 } from "./long-task-control-types.js";
|
|
4
|
+
import type { ApplicableKeyedStatementV2, CheckExecutionTargetV2, ClaimApplicabilityV2, DeliveryJourneyRoleV2, DeliveryScenarioV2, DeliveryStageV2, EvidenceCapabilityV2, ExecutionTargetV2, ExternalConfirmationKindV2, KeyedStatementV2, TargetProfileV2 } from "./long-task-semantic-contract-types.js";
|
|
4
5
|
import type { DeliverySurfaceBindingV2 } from "./long-task-ui-surface-types.js";
|
|
5
6
|
export type SourceClaimDispositionV2 = {
|
|
6
7
|
type: "claim";
|
|
@@ -51,6 +52,7 @@ interface DeliveryAssertionBaseV2 {
|
|
|
51
52
|
key: string;
|
|
52
53
|
criterion?: string;
|
|
53
54
|
claims: string[];
|
|
55
|
+
applicability_ref?: string;
|
|
54
56
|
observation: string;
|
|
55
57
|
evidence_capabilities: EvidenceCapabilityV2[];
|
|
56
58
|
}
|
|
@@ -125,8 +127,33 @@ export interface DeliveryControlV2 {
|
|
|
125
127
|
permission: string;
|
|
126
128
|
feedback: string;
|
|
127
129
|
accessibility: string;
|
|
130
|
+
field_coverage: DeliveryControlFieldCoverageV2[];
|
|
128
131
|
}
|
|
129
|
-
|
|
132
|
+
export type DeliveryControlFieldCoverageV2 = {
|
|
133
|
+
fields: DeliveryControlFieldNameV2[];
|
|
134
|
+
state: "specified";
|
|
135
|
+
applicability_refs: string[];
|
|
136
|
+
} | {
|
|
137
|
+
fields: DeliveryControlFieldNameV2[];
|
|
138
|
+
state: "not_applicable";
|
|
139
|
+
statement: string;
|
|
140
|
+
applicability_refs: string[];
|
|
141
|
+
} | {
|
|
142
|
+
fields: DeliveryControlFieldNameV2[];
|
|
143
|
+
state: "unresolved";
|
|
144
|
+
statement: string;
|
|
145
|
+
applicability_refs: string[];
|
|
146
|
+
};
|
|
147
|
+
export interface DeliveryControlRelationV2 extends ApplicableKeyedStatementV2 {
|
|
148
|
+
control_refs: string[];
|
|
149
|
+
required_proof_surfaces: ProofSurface[];
|
|
150
|
+
}
|
|
151
|
+
export interface DeliveryControlRelationClosureV2 {
|
|
152
|
+
state: "specified" | "not_applicable" | "unresolved";
|
|
153
|
+
statement: string;
|
|
154
|
+
applicability_refs: string[];
|
|
155
|
+
}
|
|
156
|
+
interface DeliveryRequirementV2 extends ApplicableKeyedStatementV2 {
|
|
130
157
|
required_proof_surfaces: ProofSurface[];
|
|
131
158
|
}
|
|
132
159
|
export interface DeliveryOwnerV2 {
|
|
@@ -134,7 +161,7 @@ export interface DeliveryOwnerV2 {
|
|
|
134
161
|
context_refs: string[];
|
|
135
162
|
path_globs: string[];
|
|
136
163
|
}
|
|
137
|
-
export interface DeliveryObligationV2 extends
|
|
164
|
+
export interface DeliveryObligationV2 extends ApplicableKeyedStatementV2 {
|
|
138
165
|
required_proof_surfaces: ProofSurface[];
|
|
139
166
|
}
|
|
140
167
|
export interface DeliveryBindingV2 {
|
|
@@ -152,8 +179,10 @@ export interface RollbackRecoveryV2 {
|
|
|
152
179
|
}
|
|
153
180
|
export interface PopulationRequirementV2 {
|
|
154
181
|
check_key: string;
|
|
182
|
+
universe_binding_key: string;
|
|
155
183
|
claims: string[];
|
|
156
184
|
observations: {
|
|
185
|
+
universe_ids: string;
|
|
157
186
|
eligible_ids: string;
|
|
158
187
|
observed_ids: string;
|
|
159
188
|
excluded_items: string;
|
|
@@ -165,16 +194,20 @@ export interface DeliveryOutcomeV2 {
|
|
|
165
194
|
title: string;
|
|
166
195
|
stage: string;
|
|
167
196
|
depends_on: string[];
|
|
197
|
+
applicability: ClaimApplicabilityV2[];
|
|
168
198
|
product: {
|
|
169
199
|
observable_result: string;
|
|
200
|
+
result_applicability_refs: string[];
|
|
170
201
|
success_path_required: boolean;
|
|
171
202
|
degradation_path_required: boolean;
|
|
172
203
|
owner: DeliveryOwnerV2;
|
|
173
204
|
requirements: DeliveryRequirementV2[];
|
|
174
205
|
owner_surfaces: string[];
|
|
175
206
|
controls: DeliveryControlV2[];
|
|
207
|
+
control_relation_closure: DeliveryControlRelationClosureV2;
|
|
208
|
+
control_relations: DeliveryControlRelationV2[];
|
|
176
209
|
surface_bindings: DeliverySurfaceBindingV2[];
|
|
177
|
-
non_completing_outcomes:
|
|
210
|
+
non_completing_outcomes: ApplicableKeyedStatementV2[];
|
|
178
211
|
};
|
|
179
212
|
technical: {
|
|
180
213
|
obligations: DeliveryObligationV2[];
|
|
@@ -182,7 +215,7 @@ export interface DeliveryOutcomeV2 {
|
|
|
182
215
|
allowed_support_paths: string[];
|
|
183
216
|
forbidden_paths: string[];
|
|
184
217
|
bindings: DeliveryBindingV2[];
|
|
185
|
-
forbidden_shortcuts:
|
|
218
|
+
forbidden_shortcuts: ApplicableKeyedStatementV2[];
|
|
186
219
|
rollback_and_recovery: RollbackRecoveryV2 | null;
|
|
187
220
|
};
|
|
188
221
|
acceptance: {
|
|
@@ -210,13 +243,14 @@ export interface DeliveryContractV2 {
|
|
|
210
243
|
facts: LongTaskRiskFacts;
|
|
211
244
|
};
|
|
212
245
|
global: {
|
|
246
|
+
applicability: ClaimApplicabilityV2[];
|
|
213
247
|
product: {
|
|
214
|
-
non_goals:
|
|
248
|
+
non_goals: ApplicableKeyedStatementV2[];
|
|
215
249
|
};
|
|
216
250
|
technical: {
|
|
217
|
-
constraints:
|
|
251
|
+
constraints: ApplicableKeyedStatementV2[];
|
|
218
252
|
forbidden_paths: KeyedPathV2[];
|
|
219
|
-
forbidden_shortcuts:
|
|
253
|
+
forbidden_shortcuts: ApplicableKeyedStatementV2[];
|
|
220
254
|
};
|
|
221
255
|
acceptance: {
|
|
222
256
|
checks: DeliveryCheckV2[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DeliveryControlV2 } from "./long-task-contract-types.js";
|
|
2
|
+
import type { DeliveryControlFieldNameV2 } from "./long-task-control-types.js";
|
|
3
|
+
export declare const CONTROL_FIELD_NAMES: readonly ["surface", "region", "location", "control_type", "label_content", "user_task", "visibility", "availability", "trigger", "input", "validation", "default_value", "interaction", "navigation_result", "loading_state", "empty_state", "success_state", "failure_state", "recovery", "permission", "feedback", "accessibility"];
|
|
4
|
+
export interface ControlFieldFactV2 {
|
|
5
|
+
contract_field: DeliveryControlFieldNameV2;
|
|
6
|
+
claim_field: string;
|
|
7
|
+
statement: string;
|
|
8
|
+
state: "specified" | "not_applicable" | "unresolved";
|
|
9
|
+
applicability_refs: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function controlFieldFacts(control: DeliveryControlV2): ControlFieldFactV2[];
|
|
12
|
+
export declare function controlFieldState(control: DeliveryControlV2, field: DeliveryControlFieldNameV2): ControlFieldFactV2["state"];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export const CONTROL_FIELD_NAMES = [
|
|
2
|
+
"surface",
|
|
3
|
+
"region",
|
|
4
|
+
"location",
|
|
5
|
+
"control_type",
|
|
6
|
+
"label_content",
|
|
7
|
+
"user_task",
|
|
8
|
+
"visibility",
|
|
9
|
+
"availability",
|
|
10
|
+
"trigger",
|
|
11
|
+
"input",
|
|
12
|
+
"validation",
|
|
13
|
+
"default_value",
|
|
14
|
+
"interaction",
|
|
15
|
+
"navigation_result",
|
|
16
|
+
"loading_state",
|
|
17
|
+
"empty_state",
|
|
18
|
+
"success_state",
|
|
19
|
+
"failure_state",
|
|
20
|
+
"recovery",
|
|
21
|
+
"permission",
|
|
22
|
+
"feedback",
|
|
23
|
+
"accessibility",
|
|
24
|
+
];
|
|
25
|
+
const CLAIM_FIELD_BY_CONTRACT_FIELD = {
|
|
26
|
+
surface: "surface",
|
|
27
|
+
region: "region",
|
|
28
|
+
location: "location",
|
|
29
|
+
control_type: "control_type",
|
|
30
|
+
label_content: "label_content",
|
|
31
|
+
user_task: "user_task",
|
|
32
|
+
visibility: "visibility",
|
|
33
|
+
availability: "availability",
|
|
34
|
+
trigger: "trigger",
|
|
35
|
+
input: "input",
|
|
36
|
+
validation: "validation",
|
|
37
|
+
default_value: "default_value",
|
|
38
|
+
interaction: "interaction",
|
|
39
|
+
navigation_result: "navigation_result",
|
|
40
|
+
loading_state: "loading",
|
|
41
|
+
empty_state: "empty",
|
|
42
|
+
success_state: "success",
|
|
43
|
+
failure_state: "failure",
|
|
44
|
+
recovery: "recovery",
|
|
45
|
+
permission: "permission",
|
|
46
|
+
feedback: "feedback",
|
|
47
|
+
accessibility: "accessibility",
|
|
48
|
+
};
|
|
49
|
+
export function controlFieldFacts(control) {
|
|
50
|
+
const coverage = new Map(control.field_coverage.flatMap((entry) => entry.fields.map((field) => [field, entry])));
|
|
51
|
+
return CONTROL_FIELD_NAMES.map((field) => {
|
|
52
|
+
const entry = coverage.get(field);
|
|
53
|
+
if (!entry)
|
|
54
|
+
throw new Error(`control_field_coverage_missing:${control.key}:${field}`);
|
|
55
|
+
return {
|
|
56
|
+
contract_field: field,
|
|
57
|
+
claim_field: CLAIM_FIELD_BY_CONTRACT_FIELD[field],
|
|
58
|
+
statement: entry.state === "specified" ? control[field] : entry.statement,
|
|
59
|
+
state: entry.state,
|
|
60
|
+
applicability_refs: [...entry.applicability_refs],
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function controlFieldState(control, field) {
|
|
65
|
+
return controlFieldFacts(control).find((candidate) => candidate.contract_field === field).state;
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type DeliveryControlFieldNameV2 = "surface" | "region" | "location" | "control_type" | "label_content" | "user_task" | "visibility" | "availability" | "trigger" | "input" | "validation" | "default_value" | "interaction" | "navigation_result" | "loading_state" | "empty_state" | "success_state" | "failure_state" | "recovery" | "permission" | "feedback" | "accessibility";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -54,4 +54,11 @@ function validateExpectedAssertions(control, check, unknownPrefix) {
|
|
|
54
54
|
for (const key of control.expected_assertion_failures)
|
|
55
55
|
if (!assertions.has(key))
|
|
56
56
|
throw new Error(`${unknownPrefix}:${key}`);
|
|
57
|
+
const failures = new Set(control.expected_assertion_failures);
|
|
58
|
+
for (const key of control.preserved_assertions) {
|
|
59
|
+
if (!assertions.has(key))
|
|
60
|
+
throw new Error(`${unknownPrefix}:${key}`);
|
|
61
|
+
if (failures.has(key))
|
|
62
|
+
throw new Error(`counterfactual_preserved_assertion_conflict:${unknownPrefix}:${key}`);
|
|
63
|
+
}
|
|
57
64
|
}
|