project-tiny-context-harness 0.8.1 → 0.8.3
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 +26 -20
- package/assets/README.md +115 -36
- package/assets/README.zh-CN.md +33 -27
- package/assets/agents/AGENTS_CORE.md +6 -6
- package/assets/skills/context_development_engineer/SKILL.md +6 -6
- package/assets/skills/context_uiux_design/SKILL.md +5 -5
- package/assets/skills/design-resource-authoring/SKILL.md +4 -3
- package/assets/skills/design-resource-authoring/references/downstream-handoff.md +9 -6
- package/assets/skills/design-resource-authoring/references/resource-selection.md +3 -2
- package/assets/skills/long-task-workflow/SKILL.md +16 -16
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +8 -6
- package/assets/skills/long-task-workflow/references/contract-authoring.md +18 -14
- package/assets/skills/long-task-workflow/references/evidence-design.md +15 -14
- package/assets/skills/long-task-workflow/references/source-authoring.md +12 -9
- package/assets/skills/source-plan-authoring/SKILL.md +1 -1
- package/dist/commands/long-task-authoring.js +83 -9
- package/dist/lib/context-graph-snapshot.js +7 -0
- package/dist/lib/design-resource-fact-policy.d.ts +25 -0
- package/dist/lib/design-resource-fact-policy.js +35 -0
- package/dist/lib/design-resource-handoff-shape-evidence.d.ts +3 -1
- package/dist/lib/design-resource-handoff-shape-evidence.js +60 -0
- package/dist/lib/design-resource-handoff-shape.js +5 -1
- package/dist/lib/design-resource-handoff-types.d.ts +29 -0
- package/dist/lib/design-resource-handoff-validation-coverage.d.ts +1 -1
- package/dist/lib/design-resource-handoff-validation-coverage.js +42 -14
- package/dist/lib/design-resource-handoff-validation-facts.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-facts.js +97 -0
- package/dist/lib/design-resource-handoff-validation-structure.js +2 -0
- package/dist/lib/design-resource-handoff-validation.js +10 -1
- 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-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 +67 -13
- package/dist/lib/long-task-evidence-capability-codec.js +47 -0
- package/dist/lib/long-task-evidence-capability-policy.js +80 -38
- package/dist/lib/long-task-evidence-capability-runtime.js +39 -0
- package/dist/lib/long-task-evidence-capability-types.d.ts +14 -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 +33 -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 +34 -1
- package/dist/lib/long-task-ui-surface-shape.js +33 -2
- package/dist/lib/long-task-ui-surface-types.d.ts +8 -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 +101 -25
- package/package.json +2 -2
|
@@ -39,8 +39,10 @@ task:
|
|
|
39
39
|
role: product
|
|
40
40
|
runtime_family: process
|
|
41
41
|
root_entrypoint: tests/replace-oracle.mjs
|
|
42
|
+
capabilities: [process-runtime, cold-start, production-root]
|
|
42
43
|
source_paths: [plans/replace-me.md]
|
|
43
44
|
context_refs: [project_context/areas/replace-me.md]
|
|
45
|
+
context_snapshot_mode: full
|
|
44
46
|
source_claims:
|
|
45
47
|
- key: replace-requirement
|
|
46
48
|
source_ref: plans/replace-me.md#replace-requirement
|
|
@@ -48,6 +50,12 @@ source_claims:
|
|
|
48
50
|
disposition:
|
|
49
51
|
type: claim
|
|
50
52
|
refs: [replace-outcome.requirement.replace-requirement]
|
|
53
|
+
- key: replace-architecture
|
|
54
|
+
source_ref: plans/replace-me.md#replace-architecture
|
|
55
|
+
statement: Preserve the declared owner, dependency direction, verifier boundary and architecture conformance.
|
|
56
|
+
disposition:
|
|
57
|
+
type: claim
|
|
58
|
+
refs: [replace-outcome.obligation.architecture]
|
|
51
59
|
stages:
|
|
52
60
|
- key: delivery
|
|
53
61
|
title: Delivery
|
|
@@ -55,13 +63,22 @@ stages:
|
|
|
55
63
|
gate_outcome: replace-outcome
|
|
56
64
|
risk:
|
|
57
65
|
facts: {}
|
|
58
|
-
global:
|
|
66
|
+
global:
|
|
67
|
+
applicability: []
|
|
59
68
|
outcomes:
|
|
60
69
|
- key: replace-outcome
|
|
61
70
|
title: Replace outcome
|
|
62
71
|
stage: delivery
|
|
72
|
+
applicability:
|
|
73
|
+
- key: root-success
|
|
74
|
+
target_ref: replace-runtime
|
|
75
|
+
journey_role: success
|
|
76
|
+
dimensions: [{key: delivery-state, value: ready}]
|
|
77
|
+
given_refs: [source-ready]
|
|
78
|
+
when_refs: [inspect-result]
|
|
63
79
|
product:
|
|
64
80
|
observable_result: Describe what a user or system can observe.
|
|
81
|
+
result_applicability_refs: [root-success]
|
|
65
82
|
success_path_required: true
|
|
66
83
|
degradation_path_required: false
|
|
67
84
|
owner:
|
|
@@ -72,7 +89,17 @@ outcomes:
|
|
|
72
89
|
- key: replace-requirement
|
|
73
90
|
statement: Preserve one atomic source requirement.
|
|
74
91
|
required_proof_surfaces: [runtime_behavior]
|
|
92
|
+
applicability_refs: [root-success]
|
|
93
|
+
control_relation_closure:
|
|
94
|
+
state: not_applicable
|
|
95
|
+
statement: This Outcome declares no user-visible Controls.
|
|
96
|
+
applicability_refs: [root-success]
|
|
75
97
|
technical:
|
|
98
|
+
obligations:
|
|
99
|
+
- key: architecture
|
|
100
|
+
statement: Preserve the declared owner, dependency direction, verifier boundary and architecture conformance.
|
|
101
|
+
required_proof_surfaces: [runtime_behavior]
|
|
102
|
+
applicability_refs: [root-success]
|
|
76
103
|
expected_change_paths: ["src/**"]
|
|
77
104
|
bindings:
|
|
78
105
|
- key: replace-carrier
|
|
@@ -97,21 +124,68 @@ outcomes:
|
|
|
97
124
|
input_paths: [src/replace-me.ts]
|
|
98
125
|
expected_output_paths: [src/replace-me.ts]
|
|
99
126
|
positive_assertions:
|
|
100
|
-
- key: replace-
|
|
101
|
-
criterion: The declared outcome
|
|
102
|
-
claims: [result
|
|
127
|
+
- key: replace-result
|
|
128
|
+
criterion: The declared outcome is observable.
|
|
129
|
+
claims: [result]
|
|
130
|
+
applicability_ref: root-success
|
|
103
131
|
observation: result
|
|
104
132
|
evidence_capabilities: [state_delta, target_runtime]
|
|
105
133
|
operator: equals
|
|
106
134
|
expected: true
|
|
135
|
+
- key: replace-requirement
|
|
136
|
+
criterion: The atomic Source requirement is observable.
|
|
137
|
+
claims: [requirement.replace-requirement]
|
|
138
|
+
applicability_ref: root-success
|
|
139
|
+
observation: requirement_result
|
|
140
|
+
evidence_capabilities: [state_delta, target_runtime]
|
|
141
|
+
operator: equals
|
|
142
|
+
expected: true
|
|
143
|
+
- key: replace-architecture
|
|
144
|
+
criterion: The declared architecture obligation remains conformant.
|
|
145
|
+
claims: [obligation.architecture]
|
|
146
|
+
applicability_ref: root-success
|
|
147
|
+
observation: architecture_result
|
|
148
|
+
evidence_capabilities: [state_delta, target_runtime]
|
|
149
|
+
operator: equals
|
|
150
|
+
expected: true
|
|
151
|
+
- key: replace-liveness
|
|
152
|
+
criterion: The declared target remains live under semantic mutation.
|
|
153
|
+
claims: []
|
|
154
|
+
observation: target_live
|
|
155
|
+
evidence_capabilities: [target_runtime]
|
|
156
|
+
operator: equals
|
|
157
|
+
expected: true
|
|
158
|
+
negative_assertions:
|
|
159
|
+
- key: replace-relations-na
|
|
160
|
+
criterion: Cross-Control relations remain inapplicable when no Controls are declared.
|
|
161
|
+
claims: [control_relation_closure]
|
|
162
|
+
applicability_ref: root-success
|
|
163
|
+
observation: relations_applicable
|
|
164
|
+
evidence_capabilities: [state_delta, target_runtime]
|
|
165
|
+
operator: equals
|
|
166
|
+
expected: false
|
|
107
167
|
counterfactual_controls:
|
|
108
|
-
- key:
|
|
168
|
+
- key: replace-semantic-carrier
|
|
169
|
+
binding_key: replace-carrier
|
|
170
|
+
claims: [result, requirement.replace-requirement, obligation.architecture]
|
|
171
|
+
check_key: replace-check
|
|
172
|
+
mutation:
|
|
173
|
+
type: replace_text
|
|
174
|
+
path: src/replace-me.ts
|
|
175
|
+
match: IMPLEMENTED_STATE
|
|
176
|
+
replacement: SEMANTIC_FAILURE_STATE
|
|
177
|
+
expected_assertion_failures: [replace-result, replace-requirement, replace-architecture]
|
|
178
|
+
preserved_assertions: [replace-liveness]
|
|
179
|
+
- key: replace-relation-applicability
|
|
109
180
|
binding_key: replace-carrier
|
|
110
|
-
claims: [
|
|
181
|
+
claims: [control_relation_closure]
|
|
111
182
|
check_key: replace-check
|
|
112
183
|
mutation:
|
|
113
|
-
type:
|
|
114
|
-
|
|
115
|
-
|
|
184
|
+
type: replace_text
|
|
185
|
+
path: src/replace-me.ts
|
|
186
|
+
match: NO_CROSS_CONTROL_RELATIONS
|
|
187
|
+
replacement: CROSS_CONTROL_RELATIONS_APPLY
|
|
188
|
+
expected_assertion_failures: [replace-relations-na]
|
|
189
|
+
preserved_assertions: [replace-liveness]
|
|
116
190
|
`;
|
|
117
191
|
}
|
|
@@ -15,6 +15,13 @@ export async function captureContextGraphSnapshot(repositoryRoot, contextRefs, m
|
|
|
15
15
|
const explicitlySelected = new Set();
|
|
16
16
|
if (mode === "full") {
|
|
17
17
|
allFiles.forEach((file) => selected.add(file));
|
|
18
|
+
for (const contextRef of contextRefs) {
|
|
19
|
+
const normalized = normalizeRepoPath(contextRef);
|
|
20
|
+
if (!available.has(normalized)) {
|
|
21
|
+
throw new Error(`context_ref_invalid:${contextRef}`);
|
|
22
|
+
}
|
|
23
|
+
explicitlySelected.add(normalized);
|
|
24
|
+
}
|
|
18
25
|
}
|
|
19
26
|
else {
|
|
20
27
|
for (const required of [
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DesignResourceDimension, DesignResourceEvidenceKind, DesignResourceVerificationMethod } from "./design-resource-handoff-types.js";
|
|
2
|
+
export declare const DESIGN_RESOURCE_FACT_INVARIANTS: Readonly<{
|
|
3
|
+
readonly complete_observable_design_fact_delivery: true;
|
|
4
|
+
readonly resource_fact_inventory_closure_required: true;
|
|
5
|
+
readonly exact_target_visual_pixel_required: true;
|
|
6
|
+
readonly exact_fact_set_conservation_required: true;
|
|
7
|
+
readonly design_fact_expression_boundary: true;
|
|
8
|
+
readonly unsupported_design_fact_blocks: true;
|
|
9
|
+
readonly control_granularity_is_not_design_fact_granularity: true;
|
|
10
|
+
readonly public_design_fact_guidance_required: true;
|
|
11
|
+
readonly design_fact_values_stay_in_canonical_resources: true;
|
|
12
|
+
readonly no_second_design_fact_authority: true;
|
|
13
|
+
readonly fact_inventory_required: true;
|
|
14
|
+
readonly contract_evidence_fact_binding_required: true;
|
|
15
|
+
readonly selected_design_fact_antidegradation_required: true;
|
|
16
|
+
readonly design_fact_distribution_context_cost: true;
|
|
17
|
+
readonly forbidden_design_fact_shortcuts_absent: true;
|
|
18
|
+
readonly preflight_is_not_production_conformance: true;
|
|
19
|
+
readonly unsupported_facts_block: true;
|
|
20
|
+
readonly control_relations_unchanged: true;
|
|
21
|
+
}>;
|
|
22
|
+
export declare const EXACT_TARGET_FULL_TARGET_METHODS: readonly ["layout_geometry", "visual_pixel"];
|
|
23
|
+
export declare function assertDesignResourceFactPolicyEnabled(): void;
|
|
24
|
+
export declare function designFactMethodIsCompatible(dimension: DesignResourceDimension, method: DesignResourceVerificationMethod): boolean;
|
|
25
|
+
export declare function designFactEvidenceIsCompatible(dimension: DesignResourceDimension, kind: DesignResourceEvidenceKind): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DESIGN_RESOURCE_EVIDENCE_BY_DIMENSION, DESIGN_RESOURCE_METHODS_BY_DIMENSION, } from "./design-resource-handoff-policy.js";
|
|
2
|
+
export const DESIGN_RESOURCE_FACT_INVARIANTS = Object.freeze({
|
|
3
|
+
complete_observable_design_fact_delivery: true,
|
|
4
|
+
resource_fact_inventory_closure_required: true,
|
|
5
|
+
exact_target_visual_pixel_required: true,
|
|
6
|
+
exact_fact_set_conservation_required: true,
|
|
7
|
+
design_fact_expression_boundary: true,
|
|
8
|
+
unsupported_design_fact_blocks: true,
|
|
9
|
+
control_granularity_is_not_design_fact_granularity: true,
|
|
10
|
+
public_design_fact_guidance_required: true,
|
|
11
|
+
design_fact_values_stay_in_canonical_resources: true,
|
|
12
|
+
no_second_design_fact_authority: true,
|
|
13
|
+
fact_inventory_required: true,
|
|
14
|
+
contract_evidence_fact_binding_required: true,
|
|
15
|
+
selected_design_fact_antidegradation_required: true,
|
|
16
|
+
design_fact_distribution_context_cost: true,
|
|
17
|
+
forbidden_design_fact_shortcuts_absent: true,
|
|
18
|
+
preflight_is_not_production_conformance: true,
|
|
19
|
+
unsupported_facts_block: true,
|
|
20
|
+
control_relations_unchanged: true,
|
|
21
|
+
});
|
|
22
|
+
export const EXACT_TARGET_FULL_TARGET_METHODS = [
|
|
23
|
+
"layout_geometry",
|
|
24
|
+
"visual_pixel",
|
|
25
|
+
];
|
|
26
|
+
export function assertDesignResourceFactPolicyEnabled() {
|
|
27
|
+
if (Object.values(DESIGN_RESOURCE_FACT_INVARIANTS).some((enabled) => enabled !== true))
|
|
28
|
+
throw new Error("design_resource_fact_policy_disabled");
|
|
29
|
+
}
|
|
30
|
+
export function designFactMethodIsCompatible(dimension, method) {
|
|
31
|
+
return DESIGN_RESOURCE_METHODS_BY_DIMENSION[dimension].includes(method);
|
|
32
|
+
}
|
|
33
|
+
export function designFactEvidenceIsCompatible(dimension, kind) {
|
|
34
|
+
return DESIGN_RESOURCE_EVIDENCE_BY_DIMENSION[dimension].includes(kind);
|
|
35
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { DesignResourceHandoffBlockerV1, DesignResourceHandoffCoverageV1, DesignResourceHandoffEvidenceV1 } from "./design-resource-handoff-types.js";
|
|
1
|
+
import type { DesignResourceHandoffBlockerV1, DesignResourceHandoffCoverageV1, DesignResourceHandoffEvidenceV1, DesignResourceHandoffFactV1, DesignResourceHandoffResourceFactClosureV1 } from "./design-resource-handoff-types.js";
|
|
2
2
|
export declare function parseDesignResourceHandoffEvidence(value: unknown): DesignResourceHandoffEvidenceV1[];
|
|
3
3
|
export declare function parseDesignResourceHandoffCoverage(value: unknown): DesignResourceHandoffCoverageV1[];
|
|
4
|
+
export declare function parseDesignResourceHandoffFacts(value: unknown): DesignResourceHandoffFactV1[];
|
|
5
|
+
export declare function parseDesignResourceHandoffResourceFactClosure(value: unknown): DesignResourceHandoffResourceFactClosureV1[];
|
|
4
6
|
export declare function parseDesignResourceHandoffBlockers(value: unknown): DesignResourceHandoffBlockerV1[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DESIGN_RESOURCE_DIMENSIONS, DESIGN_RESOURCE_EVIDENCE_KINDS, DESIGN_RESOURCE_LOCATOR_KINDS, } from "./design-resource-handoff-types.js";
|
|
2
2
|
import { contractKey, contractKeys, sourceItemKeys, stableKey, stableKeys, verificationMethods, } from "./design-resource-handoff-shape-primitives.js";
|
|
3
3
|
import { array, literal, object, string, } from "./long-task-shape-primitives.js";
|
|
4
|
+
import { EXECUTION_TARGET_CAPABILITIES } from "./execution-target-capabilities.js";
|
|
4
5
|
export function parseDesignResourceHandoffEvidence(value) {
|
|
5
6
|
return array(value, "design_resource_handoff.evidence").map((item, index) => {
|
|
6
7
|
const label = `design_resource_handoff.evidence[${index}]`;
|
|
@@ -35,6 +36,7 @@ export function parseDesignResourceHandoffCoverage(value) {
|
|
|
35
36
|
"target_refs",
|
|
36
37
|
"condition_refs",
|
|
37
38
|
"evidence_refs",
|
|
39
|
+
"fact_refs",
|
|
38
40
|
"source_item_refs",
|
|
39
41
|
"verification_methods",
|
|
40
42
|
"rationale",
|
|
@@ -53,12 +55,68 @@ export function parseDesignResourceHandoffCoverage(value) {
|
|
|
53
55
|
target_refs: contractKeys(row.target_refs, `${label}.target_refs`),
|
|
54
56
|
condition_refs: contractKeys(row.condition_refs, `${label}.condition_refs`),
|
|
55
57
|
evidence_refs: stableKeys(row.evidence_refs, `${label}.evidence_refs`),
|
|
58
|
+
fact_refs: stableKeys(row.fact_refs, `${label}.fact_refs`),
|
|
56
59
|
source_item_refs: sourceItemKeys(row.source_item_refs, `${label}.source_item_refs`),
|
|
57
60
|
verification_methods: verificationMethods(row.verification_methods, `${label}.verification_methods`),
|
|
58
61
|
rationale: string(row.rationale, `${label}.rationale`),
|
|
59
62
|
};
|
|
60
63
|
});
|
|
61
64
|
}
|
|
65
|
+
export function parseDesignResourceHandoffFacts(value) {
|
|
66
|
+
return array(value, "design_resource_handoff.facts").map((item, index) => {
|
|
67
|
+
const label = `design_resource_handoff.facts[${index}]`;
|
|
68
|
+
const row = object(item, label, [
|
|
69
|
+
"key",
|
|
70
|
+
"subject_ref",
|
|
71
|
+
"target_ref",
|
|
72
|
+
"condition_ref",
|
|
73
|
+
"dimension",
|
|
74
|
+
"observation_scope",
|
|
75
|
+
"evidence_refs",
|
|
76
|
+
"source_item_refs",
|
|
77
|
+
"verification_method",
|
|
78
|
+
]);
|
|
79
|
+
return {
|
|
80
|
+
key: stableKey(row.key, `${label}.key`),
|
|
81
|
+
subject_ref: stableKey(row.subject_ref, `${label}.subject_ref`),
|
|
82
|
+
target_ref: contractKey(row.target_ref, `${label}.target_ref`),
|
|
83
|
+
condition_ref: contractKey(row.condition_ref, `${label}.condition_ref`),
|
|
84
|
+
dimension: literal(row.dimension, DESIGN_RESOURCE_DIMENSIONS, `${label}.dimension`),
|
|
85
|
+
observation_scope: literal(row.observation_scope, ["subject", "full_target"], `${label}.observation_scope`),
|
|
86
|
+
evidence_refs: stableKeys(row.evidence_refs, `${label}.evidence_refs`),
|
|
87
|
+
source_item_refs: sourceItemKeys(row.source_item_refs, `${label}.source_item_refs`),
|
|
88
|
+
verification_method: verificationMethods([row.verification_method], `${label}.verification_method`)[0],
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export function parseDesignResourceHandoffResourceFactClosure(value) {
|
|
93
|
+
return array(value, "design_resource_handoff.resource_fact_closure").map((item, index) => {
|
|
94
|
+
const label = `design_resource_handoff.resource_fact_closure[${index}]`;
|
|
95
|
+
const row = object(item, label, [
|
|
96
|
+
"key",
|
|
97
|
+
"resource_ref",
|
|
98
|
+
"disposition",
|
|
99
|
+
"fact_refs",
|
|
100
|
+
"inspection",
|
|
101
|
+
"rationale",
|
|
102
|
+
]);
|
|
103
|
+
const inspection = object(row.inspection, `${label}.inspection`, [
|
|
104
|
+
"status",
|
|
105
|
+
"inspector",
|
|
106
|
+
]);
|
|
107
|
+
return {
|
|
108
|
+
key: stableKey(row.key, `${label}.key`),
|
|
109
|
+
resource_ref: stableKey(row.resource_ref, `${label}.resource_ref`),
|
|
110
|
+
disposition: literal(row.disposition, ["material_with_facts", "supporting_only"], `${label}.disposition`),
|
|
111
|
+
fact_refs: stableKeys(row.fact_refs, `${label}.fact_refs`),
|
|
112
|
+
inspection: {
|
|
113
|
+
status: literal(inspection.status, ["complete"], `${label}.inspection.status`),
|
|
114
|
+
inspector: string(inspection.inspector, `${label}.inspection.inspector`),
|
|
115
|
+
},
|
|
116
|
+
rationale: string(row.rationale, `${label}.rationale`),
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
62
120
|
export function parseDesignResourceHandoffBlockers(value) {
|
|
63
121
|
return array(value, "design_resource_handoff.acceptance_blockers").map((item, index) => {
|
|
64
122
|
const label = `design_resource_handoff.acceptance_blockers[${index}]`;
|
|
@@ -69,6 +127,7 @@ export function parseDesignResourceHandoffBlockers(value) {
|
|
|
69
127
|
"dimensions",
|
|
70
128
|
"source_item_refs",
|
|
71
129
|
"verification_methods",
|
|
130
|
+
"required_capabilities",
|
|
72
131
|
"description",
|
|
73
132
|
]);
|
|
74
133
|
return {
|
|
@@ -78,6 +137,7 @@ export function parseDesignResourceHandoffBlockers(value) {
|
|
|
78
137
|
dimensions: array(row.dimensions, `${label}.dimensions`).map((dimension, itemIndex) => literal(dimension, DESIGN_RESOURCE_DIMENSIONS, `${label}.dimensions[${itemIndex}]`)),
|
|
79
138
|
source_item_refs: sourceItemKeys(row.source_item_refs, `${label}.source_item_refs`),
|
|
80
139
|
verification_methods: verificationMethods(row.verification_methods, `${label}.verification_methods`),
|
|
140
|
+
required_capabilities: array(row.required_capabilities, `${label}.required_capabilities`).map((capability, capabilityIndex) => literal(capability, EXECUTION_TARGET_CAPABILITIES, `${label}.required_capabilities[${capabilityIndex}]`)),
|
|
81
141
|
description: string(row.description, `${label}.description`),
|
|
82
142
|
};
|
|
83
143
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseDesignResourceHandoffBlockers, parseDesignResourceHandoffCoverage, parseDesignResourceHandoffEvidence, } from "./design-resource-handoff-shape-evidence.js";
|
|
1
|
+
import { parseDesignResourceHandoffBlockers, parseDesignResourceHandoffCoverage, parseDesignResourceHandoffEvidence, parseDesignResourceHandoffFacts, parseDesignResourceHandoffResourceFactClosure, } from "./design-resource-handoff-shape-evidence.js";
|
|
2
2
|
import { contractKey, stableKeys, } from "./design-resource-handoff-shape-primitives.js";
|
|
3
3
|
import { parseDesignResourceHandoffConditions, parseDesignResourceHandoffResources, parseDesignResourceHandoffSubjects, parseDesignResourceHandoffTargets, } from "./design-resource-handoff-shape-structure.js";
|
|
4
4
|
import { literal, object, repositoryFile, string, strings, } from "./long-task-shape-primitives.js";
|
|
@@ -13,6 +13,8 @@ export function parseDesignResourceHandoffShape(value) {
|
|
|
13
13
|
"subjects",
|
|
14
14
|
"targets",
|
|
15
15
|
"evidence",
|
|
16
|
+
"facts",
|
|
17
|
+
"resource_fact_closure",
|
|
16
18
|
"coverage",
|
|
17
19
|
"acceptance_blockers",
|
|
18
20
|
"proposal",
|
|
@@ -64,6 +66,8 @@ export function parseDesignResourceHandoffShape(value) {
|
|
|
64
66
|
subjects: parseDesignResourceHandoffSubjects(root.subjects),
|
|
65
67
|
targets: parseDesignResourceHandoffTargets(root.targets),
|
|
66
68
|
evidence: parseDesignResourceHandoffEvidence(root.evidence),
|
|
69
|
+
facts: parseDesignResourceHandoffFacts(root.facts),
|
|
70
|
+
resource_fact_closure: parseDesignResourceHandoffResourceFactClosure(root.resource_fact_closure),
|
|
67
71
|
coverage: parseDesignResourceHandoffCoverage(root.coverage),
|
|
68
72
|
acceptance_blockers: parseDesignResourceHandoffBlockers(root.acceptance_blockers),
|
|
69
73
|
proposal: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ExecutionTargetCapabilityV2 } from "./execution-target-capabilities.js";
|
|
1
2
|
export declare const DESIGN_RESOURCE_DIMENSIONS: readonly ["surface_flow", "visual_content", "component_control", "state_interaction", "motion", "adaptation_input", "accessibility", "assets"];
|
|
2
3
|
export type DesignResourceDimension = (typeof DESIGN_RESOURCE_DIMENSIONS)[number];
|
|
3
4
|
export declare const DESIGN_RESOURCE_EVIDENCE_KINDS: readonly ["frame", "component_variant", "prototype_state", "prototype_transition", "motion_spec", "motion_capture", "responsive_spec", "input_spec", "accessibility_spec", "semantic_tree", "token_spec", "asset", "annotation"];
|
|
@@ -33,6 +34,8 @@ export interface DesignResourceHandoffV1 {
|
|
|
33
34
|
subjects: DesignResourceHandoffSubjectV1[];
|
|
34
35
|
targets: DesignResourceHandoffTargetV1[];
|
|
35
36
|
evidence: DesignResourceHandoffEvidenceV1[];
|
|
37
|
+
facts: DesignResourceHandoffFactV1[];
|
|
38
|
+
resource_fact_closure: DesignResourceHandoffResourceFactClosureV1[];
|
|
36
39
|
coverage: DesignResourceHandoffCoverageV1[];
|
|
37
40
|
acceptance_blockers: DesignResourceHandoffBlockerV1[];
|
|
38
41
|
proposal: {
|
|
@@ -96,6 +99,28 @@ export interface DesignResourceHandoffEvidenceV1 {
|
|
|
96
99
|
};
|
|
97
100
|
condition_refs: string[];
|
|
98
101
|
}
|
|
102
|
+
export interface DesignResourceHandoffFactV1 {
|
|
103
|
+
key: string;
|
|
104
|
+
subject_ref: string;
|
|
105
|
+
target_ref: string;
|
|
106
|
+
condition_ref: string;
|
|
107
|
+
dimension: DesignResourceDimension;
|
|
108
|
+
observation_scope: "subject" | "full_target";
|
|
109
|
+
evidence_refs: string[];
|
|
110
|
+
source_item_refs: string[];
|
|
111
|
+
verification_method: DesignResourceVerificationMethod;
|
|
112
|
+
}
|
|
113
|
+
export interface DesignResourceHandoffResourceFactClosureV1 {
|
|
114
|
+
key: string;
|
|
115
|
+
resource_ref: string;
|
|
116
|
+
disposition: "material_with_facts" | "supporting_only";
|
|
117
|
+
fact_refs: string[];
|
|
118
|
+
inspection: {
|
|
119
|
+
status: "complete";
|
|
120
|
+
inspector: string;
|
|
121
|
+
};
|
|
122
|
+
rationale: string;
|
|
123
|
+
}
|
|
99
124
|
export interface DesignResourceHandoffCoverageV1 {
|
|
100
125
|
key: string;
|
|
101
126
|
subject_refs: string[];
|
|
@@ -104,6 +129,7 @@ export interface DesignResourceHandoffCoverageV1 {
|
|
|
104
129
|
target_refs: string[];
|
|
105
130
|
condition_refs: string[];
|
|
106
131
|
evidence_refs: string[];
|
|
132
|
+
fact_refs: string[];
|
|
107
133
|
source_item_refs: string[];
|
|
108
134
|
verification_methods: DesignResourceVerificationMethod[];
|
|
109
135
|
rationale: string;
|
|
@@ -115,6 +141,7 @@ export interface DesignResourceHandoffBlockerV1 {
|
|
|
115
141
|
dimensions: DesignResourceDimension[];
|
|
116
142
|
source_item_refs: string[];
|
|
117
143
|
verification_methods: DesignResourceVerificationMethod[];
|
|
144
|
+
required_capabilities: ExecutionTargetCapabilityV2[];
|
|
118
145
|
description: string;
|
|
119
146
|
}
|
|
120
147
|
export interface ParsedDesignResourceHandoffV1 {
|
|
@@ -133,6 +160,8 @@ export interface DesignResourceHandoffPreflightV1 extends ParsedDesignResourceHa
|
|
|
133
160
|
subjects: number;
|
|
134
161
|
targets: number;
|
|
135
162
|
evidence: number;
|
|
163
|
+
facts: number;
|
|
164
|
+
resource_fact_closure: number;
|
|
136
165
|
coverage: number;
|
|
137
166
|
acceptance_blockers: number;
|
|
138
167
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type DesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
|
|
2
|
-
export declare function validateDesignResourceCoverage(handoff: DesignResourceHandoffV1, subjects: Map<string, unknown>, targets: Map<string, DesignResourceHandoffV1["targets"][number]>, conditions: Map<string, unknown>, evidence: Map<string, DesignResourceHandoffV1["evidence"][number]>, sourceItems: Map<string, string>): void;
|
|
2
|
+
export declare function validateDesignResourceCoverage(handoff: DesignResourceHandoffV1, subjects: Map<string, unknown>, targets: Map<string, DesignResourceHandoffV1["targets"][number]>, conditions: Map<string, unknown>, evidence: Map<string, DesignResourceHandoffV1["evidence"][number]>, facts: Map<string, DesignResourceHandoffV1["facts"][number]>, sourceItems: Map<string, string>): void;
|
|
3
3
|
export declare function validateDesignResourceBlockers(handoff: DesignResourceHandoffV1, subjects: Map<string, unknown>, targets: Map<string, unknown>, sourceItems: Map<string, string>): void;
|
|
4
4
|
export declare function validateDesignResourceReachability(handoff: DesignResourceHandoffV1): void;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { DESIGN_RESOURCE_EVIDENCE_BY_DIMENSION, DESIGN_RESOURCE_METHODS_BY_DIMENSION, } from "./design-resource-handoff-policy.js";
|
|
2
2
|
import { DESIGN_RESOURCE_DIMENSIONS, } from "./design-resource-handoff-types.js";
|
|
3
3
|
import { invalidDesignResourceHandoff, requireDesignSourceItemKind, requireKnownDesignResourceRef, requireNonemptyDesignResourceValues, requireUniqueDesignResourceValues, } from "./design-resource-handoff-validation-primitives.js";
|
|
4
|
-
export function validateDesignResourceCoverage(handoff, subjects, targets, conditions, evidence, sourceItems) {
|
|
4
|
+
export function validateDesignResourceCoverage(handoff, subjects, targets, conditions, evidence, facts, sourceItems) {
|
|
5
5
|
const cells = new Set();
|
|
6
|
+
const referencedFacts = new Set();
|
|
6
7
|
const unresolved = [];
|
|
7
8
|
for (const row of handoff.coverage) {
|
|
8
9
|
requireNonemptyDesignResourceValues(row.subject_refs, `coverage_subject_refs_required:${row.key}`);
|
|
@@ -12,6 +13,7 @@ export function validateDesignResourceCoverage(handoff, subjects, targets, condi
|
|
|
12
13
|
["target_ref", row.target_refs],
|
|
13
14
|
["condition_ref", row.condition_refs],
|
|
14
15
|
["evidence_ref", row.evidence_refs],
|
|
16
|
+
["fact_ref", row.fact_refs],
|
|
15
17
|
["source_item_ref", row.source_item_refs],
|
|
16
18
|
["verification_method", row.verification_methods],
|
|
17
19
|
])
|
|
@@ -24,12 +26,12 @@ export function validateDesignResourceCoverage(handoff, subjects, targets, condi
|
|
|
24
26
|
}
|
|
25
27
|
validateCoverageApplicability(row, handoff, targets, conditions, cells);
|
|
26
28
|
if (row.disposition === "covered")
|
|
27
|
-
validateCoveredRow(row, targets, conditions, evidence);
|
|
28
|
-
else
|
|
29
|
-
validateNoncoveredRow(row);
|
|
30
|
-
if (row.disposition === "decision_required" ||
|
|
29
|
+
validateCoveredRow(row, handoff, targets, conditions, evidence, facts, referencedFacts);
|
|
30
|
+
else if (row.disposition === "decision_required" ||
|
|
31
31
|
row.disposition === "unavailable")
|
|
32
32
|
unresolved.push(row.key);
|
|
33
|
+
else
|
|
34
|
+
validateNoncoveredRow(row);
|
|
33
35
|
}
|
|
34
36
|
for (const subject of handoff.subjects)
|
|
35
37
|
for (const targetRef of subject.target_refs) {
|
|
@@ -43,6 +45,9 @@ export function validateDesignResourceCoverage(handoff, subjects, targets, condi
|
|
|
43
45
|
}
|
|
44
46
|
if (unresolved.length)
|
|
45
47
|
invalidDesignResourceHandoff("unresolved_coverage", unresolved.sort().join(","));
|
|
48
|
+
for (const fact of handoff.facts)
|
|
49
|
+
if (!referencedFacts.has(fact.key))
|
|
50
|
+
invalidDesignResourceHandoff("design_resource_fact_unreferenced", fact.key);
|
|
46
51
|
}
|
|
47
52
|
function validateCoverageApplicability(row, handoff, targets, conditions, cells) {
|
|
48
53
|
requireNonemptyDesignResourceValues(row.target_refs, `coverage_target_refs_required:${row.key}`);
|
|
@@ -80,6 +85,7 @@ export function validateDesignResourceBlockers(handoff, subjects, targets, sourc
|
|
|
80
85
|
["dimensions", blocker.dimensions],
|
|
81
86
|
["source_item_refs", blocker.source_item_refs],
|
|
82
87
|
["verification_methods", blocker.verification_methods],
|
|
88
|
+
["required_capabilities", blocker.required_capabilities],
|
|
83
89
|
]) {
|
|
84
90
|
requireNonemptyDesignResourceValues(values, `acceptance_blocker_${name}_required:${blocker.key}`);
|
|
85
91
|
requireUniqueDesignResourceValues(values, `acceptance_blocker_${name}_duplicate:${blocker.key}`);
|
|
@@ -125,11 +131,12 @@ export function validateDesignResourceReachability(handoff) {
|
|
|
125
131
|
if (!coverageEvidence.has(item.key))
|
|
126
132
|
invalidDesignResourceHandoff("evidence_unreferenced", item.key);
|
|
127
133
|
}
|
|
128
|
-
function validateCoveredRow(row, targets, conditions, evidence) {
|
|
134
|
+
function validateCoveredRow(row, handoff, targets, conditions, evidence, facts, referencedFacts) {
|
|
129
135
|
for (const [name, values] of [
|
|
130
136
|
["target_refs", row.target_refs],
|
|
131
137
|
["condition_refs", row.condition_refs],
|
|
132
138
|
["evidence_refs", row.evidence_refs],
|
|
139
|
+
["fact_refs", row.fact_refs],
|
|
133
140
|
["verification_methods", row.verification_methods],
|
|
134
141
|
])
|
|
135
142
|
requireNonemptyDesignResourceValues(values, `coverage_${name}_required:${row.key}`);
|
|
@@ -139,6 +146,12 @@ function validateCoveredRow(row, targets, conditions, evidence) {
|
|
|
139
146
|
requireKnownDesignResourceRef(conditions, ref, "condition");
|
|
140
147
|
for (const ref of row.evidence_refs)
|
|
141
148
|
requireKnownDesignResourceRef(evidence, ref, "evidence");
|
|
149
|
+
for (const ref of row.fact_refs) {
|
|
150
|
+
requireKnownDesignResourceRef(facts, ref, "fact");
|
|
151
|
+
if (referencedFacts.has(ref))
|
|
152
|
+
invalidDesignResourceHandoff("coverage_fact_ref_duplicate", `${row.key}:${ref}`);
|
|
153
|
+
referencedFacts.add(ref);
|
|
154
|
+
}
|
|
142
155
|
const allowedEvidence = new Set(DESIGN_RESOURCE_EVIDENCE_BY_DIMENSION[row.dimension]);
|
|
143
156
|
const allowedMethods = new Set(DESIGN_RESOURCE_METHODS_BY_DIMENSION[row.dimension]);
|
|
144
157
|
for (const ref of row.evidence_refs) {
|
|
@@ -149,20 +162,35 @@ function validateCoveredRow(row, targets, conditions, evidence) {
|
|
|
149
162
|
for (const method of row.verification_methods)
|
|
150
163
|
if (!allowedMethods.has(method))
|
|
151
164
|
invalidDesignResourceHandoff("coverage_verification_method_incompatible", `${row.key}:${row.dimension}:${method}`);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
165
|
+
const expectedFacts = handoff.facts.filter((fact) => fact.dimension === row.dimension &&
|
|
166
|
+
row.subject_refs.includes(fact.subject_ref) &&
|
|
167
|
+
row.target_refs.includes(fact.target_ref) &&
|
|
168
|
+
row.condition_refs.includes(fact.condition_ref));
|
|
169
|
+
assertSameSet(row.fact_refs, expectedFacts.map((fact) => fact.key), "coverage_fact_refs_mismatch", row.key);
|
|
170
|
+
assertSameSet(row.evidence_refs, expectedFacts.flatMap((fact) => fact.evidence_refs), "coverage_fact_evidence_refs_mismatch", row.key);
|
|
171
|
+
assertSameSet(row.source_item_refs, expectedFacts.flatMap((fact) => fact.source_item_refs), "coverage_fact_source_item_refs_mismatch", row.key);
|
|
172
|
+
assertSameSet(row.verification_methods, expectedFacts.map((fact) => fact.verification_method), "coverage_fact_verification_methods_mismatch", row.key);
|
|
173
|
+
for (const subjectRef of row.subject_refs)
|
|
174
|
+
for (const targetRef of row.target_refs)
|
|
175
|
+
for (const conditionRef of row.condition_refs)
|
|
176
|
+
if (!expectedFacts.some((fact) => fact.subject_ref === subjectRef &&
|
|
177
|
+
fact.target_ref === targetRef &&
|
|
178
|
+
fact.condition_ref === conditionRef))
|
|
179
|
+
invalidDesignResourceHandoff("coverage_cell_without_fact", `${row.key}:${subjectRef}:${targetRef}:${conditionRef}`);
|
|
160
180
|
}
|
|
161
181
|
function validateNoncoveredRow(row) {
|
|
162
182
|
for (const [name, values] of [
|
|
163
183
|
["evidence_refs", row.evidence_refs],
|
|
184
|
+
["fact_refs", row.fact_refs],
|
|
164
185
|
["verification_methods", row.verification_methods],
|
|
165
186
|
])
|
|
166
187
|
if (values.length)
|
|
167
188
|
invalidDesignResourceHandoff("noncovered_binding_forbidden", `${row.key}:${row.disposition}:${name}`);
|
|
168
189
|
}
|
|
190
|
+
function assertSameSet(actual, expected, code, detail) {
|
|
191
|
+
const left = [...new Set(actual)].sort();
|
|
192
|
+
const right = [...new Set(expected)].sort();
|
|
193
|
+
if (left.length !== right.length ||
|
|
194
|
+
left.some((item, index) => item !== right[index]))
|
|
195
|
+
invalidDesignResourceHandoff(code, `${detail}:${left.join(",")}:${right.join(",")}`);
|
|
196
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { DesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
|
|
2
|
+
export declare function validateDesignResourceFacts(handoff: DesignResourceHandoffV1, resources: Map<string, DesignResourceHandoffV1["resources"][number]>, conditions: Map<string, DesignResourceHandoffV1["conditions"][number]>, subjects: Map<string, DesignResourceHandoffV1["subjects"][number]>, targets: Map<string, DesignResourceHandoffV1["targets"][number]>, evidence: Map<string, DesignResourceHandoffV1["evidence"][number]>, sourceItems: Map<string, string>): void;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { assertDesignResourceFactPolicyEnabled, designFactEvidenceIsCompatible, designFactMethodIsCompatible, EXACT_TARGET_FULL_TARGET_METHODS, } from "./design-resource-fact-policy.js";
|
|
2
|
+
import { invalidDesignResourceHandoff, requireDesignSourceItemKind, requireKnownDesignResourceRef, requireNonemptyDesignResourceValues, requireUniqueDesignResourceValues, } from "./design-resource-handoff-validation-primitives.js";
|
|
3
|
+
export function validateDesignResourceFacts(handoff, resources, conditions, subjects, targets, evidence, sourceItems) {
|
|
4
|
+
assertDesignResourceFactPolicyEnabled();
|
|
5
|
+
for (const fact of handoff.facts) {
|
|
6
|
+
requireNonemptyDesignResourceValues(fact.evidence_refs, `fact_evidence_refs_required:${fact.key}`);
|
|
7
|
+
requireNonemptyDesignResourceValues(fact.source_item_refs, `fact_source_item_refs_required:${fact.key}`);
|
|
8
|
+
requireUniqueDesignResourceValues(fact.evidence_refs, `fact_evidence_ref_duplicate:${fact.key}`);
|
|
9
|
+
requireUniqueDesignResourceValues(fact.source_item_refs, `fact_source_item_ref_duplicate:${fact.key}`);
|
|
10
|
+
requireKnownDesignResourceRef(subjects, fact.subject_ref, "subject");
|
|
11
|
+
requireKnownDesignResourceRef(targets, fact.target_ref, "target");
|
|
12
|
+
requireKnownDesignResourceRef(conditions, fact.condition_ref, "condition");
|
|
13
|
+
const subject = subjects.get(fact.subject_ref);
|
|
14
|
+
const target = targets.get(fact.target_ref);
|
|
15
|
+
if (!subject.target_refs.includes(fact.target_ref))
|
|
16
|
+
invalidDesignResourceHandoff("fact_target_outside_subject", `${fact.key}:${fact.subject_ref}:${fact.target_ref}`);
|
|
17
|
+
if (!target.condition_refs.includes(fact.condition_ref))
|
|
18
|
+
invalidDesignResourceHandoff("fact_condition_outside_target", `${fact.key}:${fact.target_ref}:${fact.condition_ref}`);
|
|
19
|
+
if (fact.observation_scope === "full_target" &&
|
|
20
|
+
(subject.kind !== "surface" ||
|
|
21
|
+
!subject.stable_keys.some((key) => handoff.scope.surface_keys.includes(key))))
|
|
22
|
+
invalidDesignResourceHandoff("full_target_fact_surface_required", `${fact.key}:${fact.subject_ref}`);
|
|
23
|
+
if (!designFactMethodIsCompatible(fact.dimension, fact.verification_method))
|
|
24
|
+
invalidDesignResourceHandoff("fact_verification_method_incompatible", `${fact.key}:${fact.dimension}:${fact.verification_method}`);
|
|
25
|
+
for (const sourceItemRef of fact.source_item_refs) {
|
|
26
|
+
requireKnownDesignResourceRef(sourceItems, sourceItemRef, "source_item");
|
|
27
|
+
requireDesignSourceItemKind(sourceItems, sourceItemRef);
|
|
28
|
+
}
|
|
29
|
+
let exactResourceEvidence = false;
|
|
30
|
+
for (const evidenceRef of fact.evidence_refs) {
|
|
31
|
+
requireKnownDesignResourceRef(evidence, evidenceRef, "evidence");
|
|
32
|
+
const item = evidence.get(evidenceRef);
|
|
33
|
+
if (!target.resource_refs.includes(item.resource_ref))
|
|
34
|
+
invalidDesignResourceHandoff("fact_evidence_outside_target", `${fact.key}:${evidenceRef}:${fact.target_ref}`);
|
|
35
|
+
if (!item.condition_refs.includes(fact.condition_ref))
|
|
36
|
+
invalidDesignResourceHandoff("fact_evidence_condition_mismatch", `${fact.key}:${evidenceRef}:${fact.condition_ref}`);
|
|
37
|
+
if (!designFactEvidenceIsCompatible(fact.dimension, item.kind))
|
|
38
|
+
invalidDesignResourceHandoff("fact_evidence_kind_incompatible", `${fact.key}:${fact.dimension}:${evidenceRef}:${item.kind}`);
|
|
39
|
+
if (resources.get(item.resource_ref)?.role === "exact_target")
|
|
40
|
+
exactResourceEvidence = true;
|
|
41
|
+
}
|
|
42
|
+
if (fact.observation_scope === "full_target" && !exactResourceEvidence)
|
|
43
|
+
invalidDesignResourceHandoff("full_target_fact_exact_resource_required", fact.key);
|
|
44
|
+
}
|
|
45
|
+
validateExactTargetFacts(handoff);
|
|
46
|
+
validateResourceFactClosure(handoff, resources, evidence);
|
|
47
|
+
}
|
|
48
|
+
function validateExactTargetFacts(handoff) {
|
|
49
|
+
for (const target of handoff.targets) {
|
|
50
|
+
if (target.interpretation !== "exact_target")
|
|
51
|
+
continue;
|
|
52
|
+
for (const conditionRef of target.condition_refs)
|
|
53
|
+
for (const method of EXACT_TARGET_FULL_TARGET_METHODS)
|
|
54
|
+
if (!handoff.facts.some((fact) => fact.target_ref === target.key &&
|
|
55
|
+
fact.condition_ref === conditionRef &&
|
|
56
|
+
fact.observation_scope === "full_target" &&
|
|
57
|
+
fact.verification_method === method))
|
|
58
|
+
invalidDesignResourceHandoff("exact_target_full_target_fact_missing", `${target.key}:${conditionRef}:${method}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function validateResourceFactClosure(handoff, resources, evidence) {
|
|
62
|
+
const closuresByResource = new Map();
|
|
63
|
+
for (const closure of handoff.resource_fact_closure) {
|
|
64
|
+
requireKnownDesignResourceRef(resources, closure.resource_ref, "resource_fact_closure_resource");
|
|
65
|
+
if (closuresByResource.has(closure.resource_ref))
|
|
66
|
+
invalidDesignResourceHandoff("resource_fact_closure_duplicate", closure.resource_ref);
|
|
67
|
+
closuresByResource.set(closure.resource_ref, closure.key);
|
|
68
|
+
requireUniqueDesignResourceValues(closure.fact_refs, `resource_fact_closure_fact_ref_duplicate:${closure.key}`);
|
|
69
|
+
const expected = handoff.facts
|
|
70
|
+
.filter((fact) => fact.evidence_refs.some((evidenceRef) => evidence.get(evidenceRef)?.resource_ref === closure.resource_ref))
|
|
71
|
+
.map((fact) => fact.key);
|
|
72
|
+
for (const factRef of closure.fact_refs)
|
|
73
|
+
if (!handoff.facts.some((fact) => fact.key === factRef))
|
|
74
|
+
invalidDesignResourceHandoff("resource_fact_closure_fact_unknown", `${closure.key}:${factRef}`);
|
|
75
|
+
assertSameSet(closure.fact_refs, expected, "resource_fact_closure_mismatch", closure.resource_ref);
|
|
76
|
+
const resource = resources.get(closure.resource_ref);
|
|
77
|
+
if (closure.disposition === "material_with_facts" &&
|
|
78
|
+
closure.fact_refs.length === 0)
|
|
79
|
+
invalidDesignResourceHandoff("resource_fact_closure_facts_required", closure.key);
|
|
80
|
+
if (closure.disposition === "supporting_only" &&
|
|
81
|
+
closure.fact_refs.length > 0)
|
|
82
|
+
invalidDesignResourceHandoff("supporting_only_resource_fact_forbidden", closure.key);
|
|
83
|
+
if (resource.role !== "supporting" &&
|
|
84
|
+
closure.disposition !== "material_with_facts")
|
|
85
|
+
invalidDesignResourceHandoff("target_resource_fact_closure_required", `${closure.key}:${resource.role}`);
|
|
86
|
+
}
|
|
87
|
+
for (const resource of handoff.resources)
|
|
88
|
+
if (!closuresByResource.has(resource.key))
|
|
89
|
+
invalidDesignResourceHandoff("resource_fact_closure_missing", resource.key);
|
|
90
|
+
}
|
|
91
|
+
function assertSameSet(actual, expected, code, detail) {
|
|
92
|
+
const left = [...new Set(actual)].sort();
|
|
93
|
+
const right = [...new Set(expected)].sort();
|
|
94
|
+
if (left.length !== right.length ||
|
|
95
|
+
left.some((item, index) => item !== right[index]))
|
|
96
|
+
invalidDesignResourceHandoff(code, `${detail}:${left.join(",")}:${right.join(",")}`);
|
|
97
|
+
}
|