project-tiny-context-harness 0.8.2 → 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.
Files changed (66) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +387 -387
  3. package/assets/README.md +553 -553
  4. package/assets/README.zh-CN.md +331 -331
  5. package/assets/agents/.gitkeep +1 -1
  6. package/assets/agents/AGENTS_CORE.md +69 -69
  7. package/assets/context_templates/architecture.md +28 -28
  8. package/assets/context_templates/area.md +34 -34
  9. package/assets/context_templates/context.toml +30 -30
  10. package/assets/context_templates/deployment.md +35 -35
  11. package/assets/context_templates/global.md +57 -57
  12. package/assets/context_templates/product-surface-contract.md +70 -70
  13. package/assets/context_templates/screen-contract.md +189 -189
  14. package/assets/context_templates/verification.md +32 -32
  15. package/assets/github/.gitkeep +1 -1
  16. package/assets/github/harness.yml +39 -39
  17. package/assets/make/.gitkeep +1 -1
  18. package/assets/make/ty-context.mk +48 -48
  19. package/assets/skills/context_development_engineer/SKILL.md +137 -137
  20. package/assets/skills/context_full_project_export/SKILL.md +28 -28
  21. package/assets/skills/context_harness_upgrade/SKILL.md +60 -60
  22. package/assets/skills/context_product_plan/SKILL.md +88 -88
  23. package/assets/skills/context_surface_contract/SKILL.md +191 -191
  24. package/assets/skills/context_uiux_design/SKILL.md +172 -172
  25. package/assets/skills/design-resource-authoring/SKILL.md +86 -85
  26. package/assets/skills/design-resource-authoring/references/downstream-handoff.md +138 -135
  27. package/assets/skills/design-resource-authoring/references/open-design-provider.md +135 -135
  28. package/assets/skills/design-resource-authoring/references/resource-selection.md +174 -173
  29. package/assets/skills/design-system-authoring/SKILL.md +57 -57
  30. package/assets/skills/design-system-authoring/agents/openai.yaml +6 -6
  31. package/assets/skills/design-system-authoring/references/authority-adoption.md +48 -48
  32. package/assets/skills/design-system-authoring/references/open-design-design-system-provider.md +110 -110
  33. package/assets/skills/long-task-workflow/SKILL.md +93 -93
  34. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +69 -69
  35. package/assets/skills/long-task-workflow/references/contract-authoring.md +111 -111
  36. package/assets/skills/long-task-workflow/references/evidence-design.md +68 -68
  37. package/assets/skills/long-task-workflow/references/source-authoring.md +95 -95
  38. package/assets/skills/source-plan-authoring/SKILL.md +14 -14
  39. package/assets/tools/validate_context.py +442 -442
  40. package/dist/commands/design-resource.js +2 -2
  41. package/dist/commands/index.js +26 -26
  42. package/dist/commands/long-task.js +15 -15
  43. package/dist/lib/design-resource-fact-policy.d.ts +25 -0
  44. package/dist/lib/design-resource-fact-policy.js +35 -0
  45. package/dist/lib/design-resource-handoff-shape-evidence.d.ts +3 -1
  46. package/dist/lib/design-resource-handoff-shape-evidence.js +57 -0
  47. package/dist/lib/design-resource-handoff-shape.js +5 -1
  48. package/dist/lib/design-resource-handoff-types.d.ts +27 -0
  49. package/dist/lib/design-resource-handoff-validation-coverage.d.ts +1 -1
  50. package/dist/lib/design-resource-handoff-validation-coverage.js +41 -14
  51. package/dist/lib/design-resource-handoff-validation-facts.d.ts +2 -0
  52. package/dist/lib/design-resource-handoff-validation-facts.js +97 -0
  53. package/dist/lib/design-resource-handoff-validation.js +10 -1
  54. package/dist/lib/long-task-design-resource-handoff.js +19 -6
  55. package/dist/lib/long-task-evidence-capability-codec.js +10 -0
  56. package/dist/lib/long-task-evidence-capability-runtime.js +3 -0
  57. package/dist/lib/long-task-evidence-capability-types.d.ts +1 -0
  58. package/dist/lib/long-task-playwright-evidence.js +1 -0
  59. package/dist/lib/long-task-semantic-drift-migration.js +1 -1
  60. package/dist/lib/long-task-ui-design-policy.js +7 -0
  61. package/dist/lib/long-task-ui-surface-shape.js +12 -1
  62. package/dist/lib/long-task-ui-surface-types.d.ts +1 -0
  63. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +3 -1
  64. package/migrations/README.md +15 -15
  65. package/package.json +83 -83
  66. package/source-mappings.yaml +25 -25
@@ -26,7 +26,7 @@ export async function designResource(args) {
26
26
  console.log(`Acceptance blockers: ${result.counts.acceptance_blockers}`);
27
27
  }
28
28
  function help() {
29
- console.log(`ty-context design-resource commands:
30
- preflight <handoff.md> [--json]
29
+ console.log(`ty-context design-resource commands:
30
+ preflight <handoff.md> [--json]
31
31
  Validate one selected implementation handoff`);
32
32
  }
@@ -36,32 +36,32 @@ export const commands = {
36
36
  package: packageSource,
37
37
  };
38
38
  export function help() {
39
- console.log(`ty-context commands:
40
- init [--adopt] [--harness-folder <path>]
41
- Initialize/adopt a project; without --harness-folder, choose target agent first
42
- enable long-task Install Long-Task Skill, Source Plan pointer, Stop Hook and templates
43
- disable long-task Remove only package-owned long-task profile assets
44
- sync Refresh managed assets; does not run migrations
45
- upgrade [--check] [--json]
46
- Run safe migrations, sync managed assets and doctor
47
- doctor Diagnose project configuration and drift
48
- check-modularity --touched|--file <path>|--base <ref> [--limit 300] [--fail-on-warning]
49
- Warn when selected handwritten source files exceed a line-count limit
50
- export-context --full|--code|--all|--source-pack|--code-index|--task-context
51
- Export temporary Context, code snapshot or bounded Source Pack artifacts
52
- validate <gate> Run a Harness validation gate
53
- validate-context Validate Minimal Context fact-source recoverability
54
- validate-code-modularity
55
- Enforce touched handwritten source file modularity
56
- validate-harness Run validate-context and validate-code-modularity
57
- design-resource <subcommand>
58
- Validate a selected design-resource implementation handoff
59
- long-task <subcommand>
60
- Manage one Canonical Delivery Contract in the current workspace
61
- delivery-set <subcommand>
62
- Retired; use one complete Contract with semantic Outcomes
63
- composite-long-task Retired command; use ty-context long-task
64
- composite-campaign Retired command; use ty-context long-task
39
+ console.log(`ty-context commands:
40
+ init [--adopt] [--harness-folder <path>]
41
+ Initialize/adopt a project; without --harness-folder, choose target agent first
42
+ enable long-task Install Long-Task Skill, Source Plan pointer, Stop Hook and templates
43
+ disable long-task Remove only package-owned long-task profile assets
44
+ sync Refresh managed assets; does not run migrations
45
+ upgrade [--check] [--json]
46
+ Run safe migrations, sync managed assets and doctor
47
+ doctor Diagnose project configuration and drift
48
+ check-modularity --touched|--file <path>|--base <ref> [--limit 300] [--fail-on-warning]
49
+ Warn when selected handwritten source files exceed a line-count limit
50
+ export-context --full|--code|--all|--source-pack|--code-index|--task-context
51
+ Export temporary Context, code snapshot or bounded Source Pack artifacts
52
+ validate <gate> Run a Harness validation gate
53
+ validate-context Validate Minimal Context fact-source recoverability
54
+ validate-code-modularity
55
+ Enforce touched handwritten source file modularity
56
+ validate-harness Run validate-context and validate-code-modularity
57
+ design-resource <subcommand>
58
+ Validate a selected design-resource implementation handoff
59
+ long-task <subcommand>
60
+ Manage one Canonical Delivery Contract in the current workspace
61
+ delivery-set <subcommand>
62
+ Retired; use one complete Contract with semantic Outcomes
63
+ composite-long-task Retired command; use ty-context long-task
64
+ composite-campaign Retired command; use ty-context long-task
65
65
  package <subcommand> Maintain package canonical source`);
66
66
  }
67
67
  async function withLongTaskProfile(args, handler) {
@@ -124,20 +124,20 @@ async function finalGate(workdir, args) {
124
124
  process.exitCode = 1;
125
125
  }
126
126
  function help() {
127
- console.log(`ty-context long-task commands:
128
- init <workdir>
129
- preflight <workdir>
130
- compile <workdir>
131
- compile <workdir> --revise
132
- diagnose-revision <workdir> [--outcome <key>] [--check <key>]
133
- approve-authority-revision <workdir> --revision <sha>
134
- explain <workdir>
135
- verify <workdir> [--outcome <key>] [--check <key>] [--explain]
136
- status <workdir>
137
- resume <workdir>
138
- doctor <workdir>
139
- final-gate <workdir>
140
- stop-check <workdir> [--message <text>]
141
- close <workdir>
127
+ console.log(`ty-context long-task commands:
128
+ init <workdir>
129
+ preflight <workdir>
130
+ compile <workdir>
131
+ compile <workdir> --revise
132
+ diagnose-revision <workdir> [--outcome <key>] [--check <key>]
133
+ approve-authority-revision <workdir> --revision <sha>
134
+ explain <workdir>
135
+ verify <workdir> [--outcome <key>] [--check <key>] [--explain]
136
+ status <workdir>
137
+ resume <workdir>
138
+ doctor <workdir>
139
+ final-gate <workdir>
140
+ stop-check <workdir> [--message <text>]
141
+ close <workdir>
142
142
  abandon <workdir> [--force-corrupt-state]`);
143
143
  }
@@ -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[];
@@ -36,6 +36,7 @@ export function parseDesignResourceHandoffCoverage(value) {
36
36
  "target_refs",
37
37
  "condition_refs",
38
38
  "evidence_refs",
39
+ "fact_refs",
39
40
  "source_item_refs",
40
41
  "verification_methods",
41
42
  "rationale",
@@ -54,12 +55,68 @@ export function parseDesignResourceHandoffCoverage(value) {
54
55
  target_refs: contractKeys(row.target_refs, `${label}.target_refs`),
55
56
  condition_refs: contractKeys(row.condition_refs, `${label}.condition_refs`),
56
57
  evidence_refs: stableKeys(row.evidence_refs, `${label}.evidence_refs`),
58
+ fact_refs: stableKeys(row.fact_refs, `${label}.fact_refs`),
57
59
  source_item_refs: sourceItemKeys(row.source_item_refs, `${label}.source_item_refs`),
58
60
  verification_methods: verificationMethods(row.verification_methods, `${label}.verification_methods`),
59
61
  rationale: string(row.rationale, `${label}.rationale`),
60
62
  };
61
63
  });
62
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
+ }
63
120
  export function parseDesignResourceHandoffBlockers(value) {
64
121
  return array(value, "design_resource_handoff.acceptance_blockers").map((item, index) => {
65
122
  const label = `design_resource_handoff.acceptance_blockers[${index}]`;
@@ -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: {
@@ -34,6 +34,8 @@ export interface DesignResourceHandoffV1 {
34
34
  subjects: DesignResourceHandoffSubjectV1[];
35
35
  targets: DesignResourceHandoffTargetV1[];
36
36
  evidence: DesignResourceHandoffEvidenceV1[];
37
+ facts: DesignResourceHandoffFactV1[];
38
+ resource_fact_closure: DesignResourceHandoffResourceFactClosureV1[];
37
39
  coverage: DesignResourceHandoffCoverageV1[];
38
40
  acceptance_blockers: DesignResourceHandoffBlockerV1[];
39
41
  proposal: {
@@ -97,6 +99,28 @@ export interface DesignResourceHandoffEvidenceV1 {
97
99
  };
98
100
  condition_refs: string[];
99
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
+ }
100
124
  export interface DesignResourceHandoffCoverageV1 {
101
125
  key: string;
102
126
  subject_refs: string[];
@@ -105,6 +129,7 @@ export interface DesignResourceHandoffCoverageV1 {
105
129
  target_refs: string[];
106
130
  condition_refs: string[];
107
131
  evidence_refs: string[];
132
+ fact_refs: string[];
108
133
  source_item_refs: string[];
109
134
  verification_methods: DesignResourceVerificationMethod[];
110
135
  rationale: string;
@@ -135,6 +160,8 @@ export interface DesignResourceHandoffPreflightV1 extends ParsedDesignResourceHa
135
160
  subjects: number;
136
161
  targets: number;
137
162
  evidence: number;
163
+ facts: number;
164
+ resource_fact_closure: number;
138
165
  coverage: number;
139
166
  acceptance_blockers: number;
140
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}`);
@@ -126,11 +131,12 @@ export function validateDesignResourceReachability(handoff) {
126
131
  if (!coverageEvidence.has(item.key))
127
132
  invalidDesignResourceHandoff("evidence_unreferenced", item.key);
128
133
  }
129
- function validateCoveredRow(row, targets, conditions, evidence) {
134
+ function validateCoveredRow(row, handoff, targets, conditions, evidence, facts, referencedFacts) {
130
135
  for (const [name, values] of [
131
136
  ["target_refs", row.target_refs],
132
137
  ["condition_refs", row.condition_refs],
133
138
  ["evidence_refs", row.evidence_refs],
139
+ ["fact_refs", row.fact_refs],
134
140
  ["verification_methods", row.verification_methods],
135
141
  ])
136
142
  requireNonemptyDesignResourceValues(values, `coverage_${name}_required:${row.key}`);
@@ -140,6 +146,12 @@ function validateCoveredRow(row, targets, conditions, evidence) {
140
146
  requireKnownDesignResourceRef(conditions, ref, "condition");
141
147
  for (const ref of row.evidence_refs)
142
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
+ }
143
155
  const allowedEvidence = new Set(DESIGN_RESOURCE_EVIDENCE_BY_DIMENSION[row.dimension]);
144
156
  const allowedMethods = new Set(DESIGN_RESOURCE_METHODS_BY_DIMENSION[row.dimension]);
145
157
  for (const ref of row.evidence_refs) {
@@ -150,20 +162,35 @@ function validateCoveredRow(row, targets, conditions, evidence) {
150
162
  for (const method of row.verification_methods)
151
163
  if (!allowedMethods.has(method))
152
164
  invalidDesignResourceHandoff("coverage_verification_method_incompatible", `${row.key}:${row.dimension}:${method}`);
153
- for (const targetRef of row.target_refs)
154
- for (const conditionRef of row.condition_refs)
155
- if (!row.evidence_refs.some((evidenceRef) => {
156
- const item = evidence.get(evidenceRef);
157
- return (targets.get(targetRef).resource_refs.includes(item.resource_ref) &&
158
- item.condition_refs.includes(conditionRef));
159
- }))
160
- invalidDesignResourceHandoff("coverage_cell_without_evidence", `${row.key}:${targetRef}:${conditionRef}`);
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}`);
161
180
  }
162
181
  function validateNoncoveredRow(row) {
163
182
  for (const [name, values] of [
164
183
  ["evidence_refs", row.evidence_refs],
184
+ ["fact_refs", row.fact_refs],
165
185
  ["verification_methods", row.verification_methods],
166
186
  ])
167
187
  if (values.length)
168
188
  invalidDesignResourceHandoff("noncovered_binding_forbidden", `${row.key}:${row.disposition}:${name}`);
169
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
+ }
@@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { parseDesignResourceHandoffMarkdown } from "./design-resource-handoff-parser.js";
4
4
  import { validateDesignResourceFiles } from "./design-resource-handoff-file-validation.js";
5
+ import { validateDesignResourceFacts } from "./design-resource-handoff-validation-facts.js";
5
6
  import { validateDesignResourceBlockers, validateDesignResourceCoverage, validateDesignResourceReachability, } from "./design-resource-handoff-validation-coverage.js";
6
7
  import { indexDesignResourceItems, invalidDesignResourceHandoff, requireNonemptyDesignResourceValues, requireUniqueDesignResourceObjects, requireUniqueDesignResourceValues, } from "./design-resource-handoff-validation-primitives.js";
7
8
  import { validateDesignResourceConditions, validateDesignResourceEvidence, validateDesignResourceScope, validateDesignResourceSubjects, validateDesignResourceTargets, } from "./design-resource-handoff-validation-structure.js";
@@ -25,6 +26,8 @@ export async function preflightDesignResourceHandoff(repository, handoffPath) {
25
26
  subjects: handoff.subjects.length,
26
27
  targets: handoff.targets.length,
27
28
  evidence: handoff.evidence.length,
29
+ facts: handoff.facts.length,
30
+ resource_fact_closure: handoff.resource_fact_closure.length,
28
31
  coverage: handoff.coverage.length,
29
32
  acceptance_blockers: handoff.acceptance_blockers.length,
30
33
  },
@@ -38,6 +41,8 @@ export function validateDesignResourceHandoffSemantics(parsed) {
38
41
  requireNonemptyDesignResourceValues(handoff.subjects, "subjects_required");
39
42
  requireNonemptyDesignResourceValues(handoff.targets, "targets_required");
40
43
  requireNonemptyDesignResourceValues(handoff.evidence, "evidence_required");
44
+ requireNonemptyDesignResourceValues(handoff.facts, "facts_required");
45
+ requireNonemptyDesignResourceValues(handoff.resource_fact_closure, "resource_fact_closure_required");
41
46
  requireNonemptyDesignResourceValues(handoff.coverage, "coverage_required");
42
47
  requireUniqueDesignResourceValues(handoff.scope.surface_keys, "scope_surface_key_duplicate");
43
48
  requireUniqueDesignResourceObjects(handoff.resources, "resource_key_duplicate");
@@ -46,6 +51,8 @@ export function validateDesignResourceHandoffSemantics(parsed) {
46
51
  requireUniqueDesignResourceObjects(handoff.subjects, "subject_key_duplicate");
47
52
  requireUniqueDesignResourceObjects(handoff.targets, "target_key_duplicate");
48
53
  requireUniqueDesignResourceObjects(handoff.evidence, "evidence_key_duplicate");
54
+ requireUniqueDesignResourceObjects(handoff.facts, "fact_key_duplicate");
55
+ requireUniqueDesignResourceObjects(handoff.resource_fact_closure, "resource_fact_closure_key_duplicate");
49
56
  requireUniqueDesignResourceObjects(handoff.coverage, "coverage_key_duplicate");
50
57
  requireUniqueDesignResourceObjects(handoff.acceptance_blockers, "acceptance_blocker_key_duplicate");
51
58
  const resources = indexDesignResourceItems(handoff.resources);
@@ -53,13 +60,15 @@ export function validateDesignResourceHandoffSemantics(parsed) {
53
60
  const subjects = indexDesignResourceItems(handoff.subjects);
54
61
  const targets = indexDesignResourceItems(handoff.targets);
55
62
  const evidence = indexDesignResourceItems(handoff.evidence);
63
+ const facts = indexDesignResourceItems(handoff.facts);
56
64
  const sourceItems = new Map(Object.entries(parsed.source_item_kinds));
57
65
  validateDesignResourceScope(handoff);
58
66
  validateDesignResourceConditions(handoff);
59
67
  validateDesignResourceSubjects(handoff, targets);
60
68
  validateDesignResourceTargets(handoff, resources, conditions);
61
69
  validateDesignResourceEvidence(handoff, resources, conditions);
62
- validateDesignResourceCoverage(handoff, subjects, targets, conditions, evidence, sourceItems);
70
+ validateDesignResourceCoverage(handoff, subjects, targets, conditions, evidence, facts, sourceItems);
71
+ validateDesignResourceFacts(handoff, resources, conditions, subjects, targets, evidence, sourceItems);
63
72
  validateDesignResourceBlockers(handoff, subjects, targets, sourceItems);
64
73
  validateDesignResourceReachability(handoff);
65
74
  }