project-tiny-context-harness 0.8.4 → 0.8.5
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/LICENSE +21 -21
- package/README.md +393 -371
- package/assets/README.md +573 -526
- package/assets/README.zh-CN.md +333 -311
- package/assets/agents/.gitkeep +1 -1
- package/assets/agents/AGENTS_CORE.md +44 -25
- package/assets/context_templates/architecture.md +28 -28
- package/assets/context_templates/area.md +34 -34
- package/assets/context_templates/context.toml +30 -30
- package/assets/context_templates/deployment.md +35 -35
- package/assets/context_templates/global.md +57 -57
- package/assets/context_templates/product-surface-contract.md +70 -70
- package/assets/context_templates/screen-contract.md +189 -189
- package/assets/context_templates/verification.md +32 -32
- package/assets/github/.gitkeep +1 -1
- package/assets/github/harness.yml +39 -39
- package/assets/make/.gitkeep +1 -1
- package/assets/make/ty-context.mk +48 -48
- package/assets/skills/context_development_engineer/SKILL.md +148 -125
- package/assets/skills/context_full_project_export/SKILL.md +28 -28
- package/assets/skills/context_harness_upgrade/SKILL.md +60 -60
- package/assets/skills/context_product_plan/SKILL.md +99 -79
- package/assets/skills/context_surface_contract/SKILL.md +191 -191
- package/assets/skills/context_uiux_design/SKILL.md +171 -171
- package/assets/skills/design-resource-authoring/SKILL.md +86 -84
- package/assets/skills/design-resource-authoring/references/downstream-handoff.md +138 -123
- package/assets/skills/design-resource-authoring/references/open-design-provider.md +132 -127
- package/assets/skills/design-resource-authoring/references/resource-selection.md +181 -154
- package/assets/skills/design-system-authoring/SKILL.md +57 -57
- package/assets/skills/design-system-authoring/agents/openai.yaml +6 -6
- package/assets/skills/design-system-authoring/references/authority-adoption.md +48 -48
- package/assets/skills/design-system-authoring/references/open-design-design-system-provider.md +110 -110
- package/assets/skills/long-task-workflow/SKILL.md +98 -92
- package/assets/skills/long-task-workflow/agents/openai.yaml +3 -1
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +72 -72
- package/assets/skills/long-task-workflow/references/contract-authoring.md +122 -101
- package/assets/skills/long-task-workflow/references/evidence-design.md +82 -61
- package/assets/skills/long-task-workflow/references/source-authoring.md +109 -90
- package/assets/skills/source-plan-authoring/SKILL.md +14 -14
- package/assets/tools/validate_context.py +442 -442
- package/dist/commands/long-task-authoring.js +27 -2
- package/dist/lib/context-templates.js +1 -1
- package/dist/lib/design-resource-fact-enums.d.ts +20 -0
- package/dist/lib/design-resource-fact-enums.js +170 -0
- package/dist/lib/design-resource-fact-locator-extractors.d.ts +8 -0
- package/dist/lib/design-resource-fact-locator-extractors.js +108 -0
- package/dist/lib/design-resource-fact-locator-resolver.d.ts +3 -0
- package/dist/lib/design-resource-fact-locator-resolver.js +112 -0
- package/dist/lib/design-resource-fact-locator-validation.d.ts +5 -0
- package/dist/lib/design-resource-fact-locator-validation.js +16 -0
- package/dist/lib/design-resource-fact-manifest-catalog.d.ts +4 -0
- package/dist/lib/design-resource-fact-manifest-catalog.js +249 -0
- package/dist/lib/design-resource-fact-manifest-model.d.ts +91 -0
- package/dist/lib/design-resource-fact-manifest-model.js +21 -0
- package/dist/lib/design-resource-fact-manifest-shape-axes.d.ts +6 -0
- package/dist/lib/design-resource-fact-manifest-shape-axes.js +159 -0
- package/dist/lib/design-resource-fact-manifest-shape-evidence.d.ts +4 -0
- package/dist/lib/design-resource-fact-manifest-shape-evidence.js +59 -0
- package/dist/lib/design-resource-fact-manifest-shape-facts.d.ts +4 -0
- package/dist/lib/design-resource-fact-manifest-shape-facts.js +109 -0
- package/dist/lib/design-resource-fact-manifest-shape-inspector.d.ts +5 -0
- package/dist/lib/design-resource-fact-manifest-shape-inspector.js +144 -0
- package/dist/lib/design-resource-fact-manifest-shape.d.ts +7 -0
- package/dist/lib/design-resource-fact-manifest-shape.js +75 -0
- package/dist/lib/design-resource-fact-manifest-types.d.ts +3 -0
- package/dist/lib/design-resource-fact-manifest-types.js +3 -0
- package/dist/lib/design-resource-fact-manifest-universe.d.ts +5 -0
- package/dist/lib/design-resource-fact-manifest-universe.js +50 -0
- package/dist/lib/design-resource-fact-manifest-validation.d.ts +3 -0
- package/dist/lib/design-resource-fact-manifest-validation.js +106 -0
- package/dist/lib/design-resource-fact-policy.d.ts +18 -0
- package/dist/lib/design-resource-fact-policy.js +72 -1
- package/dist/lib/design-resource-fact-property-methods.d.ts +3 -0
- package/dist/lib/design-resource-fact-property-methods.js +158 -0
- package/dist/lib/design-resource-fact-shape-primitives.d.ts +3 -0
- package/dist/lib/design-resource-fact-shape-primitives.js +18 -0
- package/dist/lib/design-resource-fact-types.d.ts +164 -0
- package/dist/lib/design-resource-fact-types.js +1 -0
- package/dist/lib/design-resource-fact-universe-assets.d.ts +4 -0
- package/dist/lib/design-resource-fact-universe-assets.js +71 -0
- package/dist/lib/design-resource-fact-universe-catalog.d.ts +4 -0
- package/dist/lib/design-resource-fact-universe-catalog.js +151 -0
- package/dist/lib/design-resource-fact-universe-conditions.d.ts +2 -0
- package/dist/lib/design-resource-fact-universe-conditions.js +92 -0
- package/dist/lib/design-resource-fact-universe-facts.d.ts +4 -0
- package/dist/lib/design-resource-fact-universe-facts.js +108 -0
- package/dist/lib/design-resource-fact-universe-helpers.d.ts +30 -0
- package/dist/lib/design-resource-fact-universe-helpers.js +167 -0
- package/dist/lib/design-resource-fact-universe-inspector.d.ts +7 -0
- package/dist/lib/design-resource-fact-universe-inspector.js +119 -0
- package/dist/lib/design-resource-fact-universe-proof.d.ts +4 -0
- package/dist/lib/design-resource-fact-universe-proof.js +113 -0
- package/dist/lib/design-resource-fact-universe-subjects.d.ts +5 -0
- package/dist/lib/design-resource-fact-universe-subjects.js +97 -0
- package/dist/lib/design-resource-fact-universe-variations.d.ts +2 -0
- package/dist/lib/design-resource-fact-universe-variations.js +75 -0
- package/dist/lib/design-resource-fact-value-validation.d.ts +2 -0
- package/dist/lib/design-resource-fact-value-validation.js +80 -0
- package/dist/lib/design-resource-handoff-file-primitives.js +2 -0
- package/dist/lib/design-resource-handoff-file-validation.js +13 -100
- package/dist/lib/design-resource-handoff-policy.d.ts +1 -0
- package/dist/lib/design-resource-handoff-policy.js +118 -11
- package/dist/lib/design-resource-handoff-shape-evidence.js +46 -3
- package/dist/lib/design-resource-handoff-shape-primitives.d.ts +5 -0
- package/dist/lib/design-resource-handoff-shape-primitives.js +28 -0
- package/dist/lib/design-resource-handoff-shape-structure.js +143 -22
- package/dist/lib/design-resource-handoff-shape.js +25 -0
- package/dist/lib/design-resource-handoff-types.d.ts +97 -21
- package/dist/lib/design-resource-handoff-types.js +22 -0
- package/dist/lib/design-resource-handoff-validation-coverage.d.ts +1 -1
- package/dist/lib/design-resource-handoff-validation-coverage.js +145 -118
- package/dist/lib/design-resource-handoff-validation-fact-cells.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-fact-cells.js +60 -0
- package/dist/lib/design-resource-handoff-validation-fact-records.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-fact-records.js +90 -0
- package/dist/lib/design-resource-handoff-validation-facts.js +10 -94
- package/dist/lib/design-resource-handoff-validation-proofs.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-proofs.js +67 -0
- package/dist/lib/design-resource-handoff-validation-resource-closure.d.ts +3 -0
- package/dist/lib/design-resource-handoff-validation-resource-closure.js +88 -0
- package/dist/lib/design-resource-handoff-validation-structure.js +43 -14
- package/dist/lib/design-resource-handoff-validation.js +32 -0
- package/dist/lib/design-resource-handoff-web-dependency-validation.d.ts +2 -1
- package/dist/lib/design-resource-handoff-web-dependency-validation.js +67 -7
- package/dist/lib/execution-target-capabilities.d.ts +1 -1
- package/dist/lib/execution-target-capabilities.js +14 -0
- package/dist/lib/long-task-activation-validation.js +6 -2
- package/dist/lib/long-task-applicability-shape.js +3 -3
- package/dist/lib/long-task-authoring-authority-preview.js +1 -0
- package/dist/lib/long-task-authority-policy.d.ts +1 -0
- package/dist/lib/long-task-authority-policy.js +1 -0
- package/dist/lib/long-task-authority.js +13 -0
- package/dist/lib/long-task-claim-definitions.js +13 -1
- package/dist/lib/long-task-claims.js +23 -1
- package/dist/lib/long-task-contract-types.d.ts +3 -0
- package/dist/lib/long-task-delivery-compiler.js +1 -0
- package/dist/lib/long-task-delivery-parser.js +11 -1
- 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 +17 -0
- package/dist/lib/long-task-design-resource-handoff.js +62 -38
- package/dist/lib/long-task-design-target-capabilities.d.ts +14 -0
- package/dist/lib/long-task-design-target-capabilities.js +126 -0
- package/dist/lib/long-task-evidence-capability-codec.d.ts +94 -0
- package/dist/lib/long-task-evidence-capability-codec.js +395 -22
- package/dist/lib/long-task-evidence-capability-policy.js +18 -0
- package/dist/lib/long-task-evidence-capability-runtime.js +78 -0
- package/dist/lib/long-task-evidence-capability-types.d.ts +114 -1
- package/dist/lib/long-task-outcome-parser.js +4 -0
- package/dist/lib/long-task-playwright-capability-records.d.ts +5 -0
- package/dist/lib/long-task-playwright-capability-records.js +146 -0
- package/dist/lib/long-task-playwright-case-evidence.d.ts +3 -2
- package/dist/lib/long-task-playwright-case-evidence.js +10 -79
- package/dist/lib/long-task-playwright-case-primitives.d.ts +28 -0
- package/dist/lib/long-task-playwright-case-primitives.js +143 -0
- package/dist/lib/long-task-playwright-evidence.d.ts +1 -1
- package/dist/lib/long-task-playwright-evidence.js +10 -75
- package/dist/lib/long-task-runner-freeze.d.ts +2 -2
- package/dist/lib/long-task-runner-freeze.js +15 -1
- package/dist/lib/long-task-runtime-types.d.ts +4 -1
- package/dist/lib/long-task-semantic-contract-types.d.ts +1 -1
- package/dist/lib/long-task-semantic-drift-migration.js +2 -2
- package/dist/lib/long-task-semantic-fact-binding-types.d.ts +57 -0
- package/dist/lib/long-task-semantic-fact-binding-types.js +1 -0
- package/dist/lib/long-task-semantic-fact-closure-primitives.d.ts +5 -0
- package/dist/lib/long-task-semantic-fact-closure-primitives.js +43 -0
- package/dist/lib/long-task-semantic-fact-closure.d.ts +13 -0
- package/dist/lib/long-task-semantic-fact-closure.js +48 -0
- package/dist/lib/long-task-semantic-fact-contract-closure.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-contract-closure.js +24 -0
- package/dist/lib/long-task-semantic-fact-contract-facts.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-contract-facts.js +30 -0
- package/dist/lib/long-task-semantic-fact-contract-proofs.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-contract-proofs.js +97 -0
- package/dist/lib/long-task-semantic-fact-evidence.d.ts +21 -0
- package/dist/lib/long-task-semantic-fact-evidence.js +165 -0
- package/dist/lib/long-task-semantic-fact-input-closure.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-input-closure.js +101 -0
- package/dist/lib/long-task-semantic-fact-provenance-closure.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-provenance-closure.js +140 -0
- package/dist/lib/long-task-semantic-fact-shape.d.ts +3 -0
- package/dist/lib/long-task-semantic-fact-shape.js +89 -0
- package/dist/lib/long-task-semantic-fact-value-closure.d.ts +3 -0
- package/dist/lib/long-task-semantic-fact-value-closure.js +110 -0
- package/dist/lib/long-task-shape-primitives.d.ts +2 -1
- package/dist/lib/long-task-shape-primitives.js +7 -0
- package/dist/lib/long-task-source-item-parser.js +25 -6
- package/dist/lib/long-task-source-target-continuity.js +4 -1
- package/dist/lib/long-task-source-target-index.d.ts +1 -1
- package/dist/lib/long-task-source-target-index.js +5 -1
- package/dist/lib/long-task-ui-design-policy.js +8 -4
- package/dist/lib/long-task-ui-surface-shape.js +83 -1
- package/dist/lib/long-task-ui-surface-types.d.ts +29 -0
- package/dist/lib/modularity.js +7 -1
- package/dist/lib/semantic-fact-base-types.d.ts +14 -0
- package/dist/lib/semantic-fact-base-types.js +1 -0
- package/dist/lib/semantic-fact-catalog.d.ts +8 -0
- package/dist/lib/semantic-fact-catalog.js +191 -0
- package/dist/lib/semantic-fact-condition-shape.d.ts +48 -0
- package/dist/lib/semantic-fact-condition-shape.js +111 -0
- package/dist/lib/semantic-fact-input-shape.d.ts +34 -0
- package/dist/lib/semantic-fact-input-shape.js +111 -0
- package/dist/lib/semantic-fact-inventory-types.d.ts +147 -0
- package/dist/lib/semantic-fact-inventory-types.js +1 -0
- package/dist/lib/semantic-fact-manifest-shape.d.ts +4 -0
- package/dist/lib/semantic-fact-manifest-shape.js +135 -0
- package/dist/lib/semantic-fact-manifest-types.d.ts +58 -0
- package/dist/lib/semantic-fact-manifest-types.js +19 -0
- package/dist/lib/semantic-fact-policy-authority.d.ts +8 -0
- package/dist/lib/semantic-fact-policy-authority.js +79 -0
- package/dist/lib/semantic-fact-policy-census.d.ts +2 -0
- package/dist/lib/semantic-fact-policy-census.js +80 -0
- package/dist/lib/semantic-fact-policy-condition-references.d.ts +7 -0
- package/dist/lib/semantic-fact-policy-condition-references.js +19 -0
- package/dist/lib/semantic-fact-policy-conditions.d.ts +2 -0
- package/dist/lib/semantic-fact-policy-conditions.js +105 -0
- package/dist/lib/semantic-fact-policy-facts.d.ts +3 -0
- package/dist/lib/semantic-fact-policy-facts.js +73 -0
- package/dist/lib/semantic-fact-policy-primitives.d.ts +26 -0
- package/dist/lib/semantic-fact-policy-primitives.js +169 -0
- package/dist/lib/semantic-fact-policy-proofs.d.ts +2 -0
- package/dist/lib/semantic-fact-policy-proofs.js +75 -0
- package/dist/lib/semantic-fact-policy-properties.d.ts +3 -0
- package/dist/lib/semantic-fact-policy-properties.js +84 -0
- package/dist/lib/semantic-fact-policy-units.d.ts +4 -0
- package/dist/lib/semantic-fact-policy-units.js +103 -0
- package/dist/lib/semantic-fact-policy.d.ts +17 -0
- package/dist/lib/semantic-fact-policy.js +41 -0
- package/dist/lib/semantic-fact-proof-shape.d.ts +51 -0
- package/dist/lib/semantic-fact-proof-shape.js +121 -0
- package/dist/lib/semantic-fact-proof-types.d.ts +74 -0
- package/dist/lib/semantic-fact-proof-types.js +1 -0
- package/dist/lib/semantic-fact-property-shape.d.ts +29 -0
- package/dist/lib/semantic-fact-property-shape.js +77 -0
- package/dist/lib/semantic-fact-shape-constants.d.ts +3 -0
- package/dist/lib/semantic-fact-shape-constants.js +38 -0
- package/dist/lib/semantic-fact-shape-primitives.d.ts +15 -0
- package/dist/lib/semantic-fact-shape-primitives.js +68 -0
- package/dist/lib/semantic-fact-source-parser.d.ts +9 -0
- package/dist/lib/semantic-fact-source-parser.js +46 -0
- package/dist/lib/semantic-fact-support-shape.d.ts +21 -0
- package/dist/lib/semantic-fact-support-shape.js +59 -0
- package/dist/lib/semantic-fact-types.d.ts +5 -0
- package/dist/lib/semantic-fact-types.js +5 -0
- package/dist/lib/semantic-fact-unit-shape.d.ts +34 -0
- package/dist/lib/semantic-fact-unit-shape.js +89 -0
- package/dist/lib/semantic-fact-value-shape.d.ts +12 -0
- package/dist/lib/semantic-fact-value-shape.js +39 -0
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +1339 -131
- package/migrations/README.md +15 -15
- package/package.json +84 -84
- package/source-mappings.yaml +25 -25
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { SEMANTIC_FACT_REQUIRED_INSPECTOR_CAPABILITIES } from "./semantic-fact-catalog.js";
|
|
2
|
+
import { assertSameSemanticFactSet, requireSemanticFactBasis, requireSemanticFactSubset, semanticFactCollections, semanticFactInvalid, uniqueNonemptySemanticFacts, uniqueSemanticFacts, } from "./semantic-fact-policy-primitives.js";
|
|
3
|
+
import { SEMANTIC_FACT_MANIFEST_COLLECTIONS, } from "./semantic-fact-types.js";
|
|
4
|
+
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
5
|
+
export function validateSemanticFactInspector(manifest) {
|
|
6
|
+
uniqueNonemptySemanticFacts(manifest.inspector.capabilities, "inspector_capabilities");
|
|
7
|
+
requireSemanticFactSubset([...SEMANTIC_FACT_REQUIRED_INSPECTOR_CAPABILITIES], manifest.inspector.capabilities, "inspector_capability_missing", manifest.key);
|
|
8
|
+
if (manifest.inspector.trust === "frozen_executable"
|
|
9
|
+
? !manifest.inspector.implementation_sha256
|
|
10
|
+
: manifest.inspector.implementation_sha256 !== null)
|
|
11
|
+
semanticFactInvalid("inspector_identity_digest_mismatch", `${manifest.inspector.trust}:${manifest.inspector.implementation_sha256 ?? "missing"}`);
|
|
12
|
+
}
|
|
13
|
+
export function validateSemanticFactSourceLineage(manifest) {
|
|
14
|
+
const validate = (label, row) => {
|
|
15
|
+
uniqueNonemptySemanticFacts(row.source_item_refs, `${label}_source_items:${row.key}`);
|
|
16
|
+
requireSemanticFactSubset(row.source_item_refs, manifest.scope.source_item_refs, `${label}_source_item_unknown`, row.key);
|
|
17
|
+
};
|
|
18
|
+
for (const row of manifest.scope.exclusions) {
|
|
19
|
+
validate("scope_exclusion", row);
|
|
20
|
+
uniqueNonemptySemanticFacts(row.affected_refs, `scope_exclusion_affected_refs:${row.key}`);
|
|
21
|
+
requireSemanticFactBasis(row, `scope_exclusion:${row.key}`);
|
|
22
|
+
}
|
|
23
|
+
for (const row of manifest.family_dispositions)
|
|
24
|
+
validate("family", row);
|
|
25
|
+
for (const row of manifest.subjects)
|
|
26
|
+
validate("subject", row);
|
|
27
|
+
for (const row of manifest.relations)
|
|
28
|
+
validate("relation", row);
|
|
29
|
+
for (const row of manifest.populations)
|
|
30
|
+
validate("population", row);
|
|
31
|
+
for (const row of manifest.axis_dispositions) {
|
|
32
|
+
validate("axis", row);
|
|
33
|
+
for (const value of row.values)
|
|
34
|
+
validate(`axis_value:${row.key}`, value);
|
|
35
|
+
}
|
|
36
|
+
for (const row of manifest.condition_rules)
|
|
37
|
+
validate("condition_rule", row);
|
|
38
|
+
for (const row of manifest.conditions)
|
|
39
|
+
validate("condition", row);
|
|
40
|
+
for (const row of manifest.condition_exclusions)
|
|
41
|
+
validate("condition_exclusion", row);
|
|
42
|
+
for (const row of manifest.property_dispositions)
|
|
43
|
+
validate("property", row);
|
|
44
|
+
for (const row of manifest.fact_cells)
|
|
45
|
+
validate("fact_cell", row);
|
|
46
|
+
for (const row of manifest.facts)
|
|
47
|
+
validate("fact", row);
|
|
48
|
+
for (const row of manifest.blockers) {
|
|
49
|
+
validate("blocker", row);
|
|
50
|
+
uniqueNonemptySemanticFacts(row.affected_refs, `blocker_affected_refs:${row.key}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function semanticFactCollectionIdentity(rows) {
|
|
54
|
+
return sha256Hex(canonicalValueJson(rows.map((item) => item.key).sort()));
|
|
55
|
+
}
|
|
56
|
+
export function validateSemanticFactUniqueIdentities(manifest) {
|
|
57
|
+
uniqueNonemptySemanticFacts(manifest.scope.outcome_refs, "scope_outcome_refs");
|
|
58
|
+
uniqueNonemptySemanticFacts(manifest.scope.source_item_refs, "scope_source_item_refs");
|
|
59
|
+
uniqueSemanticFacts(manifest.inputs.map((item) => `${item.kind}\0${item.source_ref}`), "input_resource");
|
|
60
|
+
for (const [name, rows] of Object.entries(semanticFactCollections(manifest)))
|
|
61
|
+
uniqueSemanticFacts(rows.map((item) => item.key), `${name}_key`);
|
|
62
|
+
uniqueSemanticFacts(manifest.inspector.census.map((item) => item.key), "inspector_census_key");
|
|
63
|
+
uniqueSemanticFacts(manifest.generation.collections.map((item) => item.name), "generation_collection_name");
|
|
64
|
+
uniqueSemanticFacts(manifest.generation.chunk_indexes, "generation_chunk_index");
|
|
65
|
+
const expectedChunks = Array.from({ length: manifest.generation.chunk_count }, (_unused, index) => index);
|
|
66
|
+
assertSameSemanticFactSet(manifest.generation.chunk_indexes, expectedChunks, "generation_chunk_indexes");
|
|
67
|
+
}
|
|
68
|
+
export function validateSemanticFactGeneration(manifest) {
|
|
69
|
+
assertSameSemanticFactSet(manifest.generation.collections.map((item) => item.name), [...SEMANTIC_FACT_MANIFEST_COLLECTIONS], "generation_collection_set");
|
|
70
|
+
const actual = semanticFactCollections(manifest);
|
|
71
|
+
for (const declaration of manifest.generation.collections) {
|
|
72
|
+
const rows = actual[declaration.name];
|
|
73
|
+
if (declaration.expected_count !== rows.length)
|
|
74
|
+
semanticFactInvalid("generation_collection_count_mismatch", `${declaration.name}:${declaration.expected_count}:${rows.length}`);
|
|
75
|
+
const digest = semanticFactCollectionIdentity(rows);
|
|
76
|
+
if (declaration.identity_sha256 !== digest)
|
|
77
|
+
semanticFactInvalid("generation_collection_identity_mismatch", `${declaration.name}:${declaration.identity_sha256}:${digest}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { assertSameSemanticFactSet, requireSemanticFactBasis, semanticFactInvalid, validateSemanticFactLocator, } from "./semantic-fact-policy-primitives.js";
|
|
2
|
+
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
3
|
+
export function validateSemanticFactInspectorCensus(manifest) {
|
|
4
|
+
const factRefs = new Set(manifest.facts.map((item) => item.key));
|
|
5
|
+
const factsFor = (predicate) => manifest.facts.filter(predicate).map((item) => item.key);
|
|
6
|
+
const expected = expectedCensusRows(manifest, factsFor);
|
|
7
|
+
const actual = new Set();
|
|
8
|
+
for (const entry of manifest.inspector.census) {
|
|
9
|
+
const identity = `${entry.kind}:${entry.key}`;
|
|
10
|
+
actual.add(identity);
|
|
11
|
+
const expectedEntry = expected.get(identity);
|
|
12
|
+
if (!expectedEntry)
|
|
13
|
+
semanticFactInvalid("census_identity_unknown", identity);
|
|
14
|
+
if (entry.locator.material_ref !== manifest.key ||
|
|
15
|
+
entry.locator.kind !== "manifest_pointer" ||
|
|
16
|
+
entry.locator.value !== expectedEntry.pointer)
|
|
17
|
+
semanticFactInvalid("census_locator_mismatch", identity);
|
|
18
|
+
const identityDigest = sha256Hex(canonicalValueJson(expectedEntry.row));
|
|
19
|
+
if (entry.identity_sha256 !== identityDigest)
|
|
20
|
+
semanticFactInvalid("census_identity_digest_mismatch", `${identity}:${entry.identity_sha256}:${identityDigest}`);
|
|
21
|
+
for (const factRef of entry.fact_refs)
|
|
22
|
+
if (!factRefs.has(factRef))
|
|
23
|
+
semanticFactInvalid("census_fact_unknown", `${entry.key}:${factRef}`);
|
|
24
|
+
assertSameSemanticFactSet(entry.fact_refs, expectedEntry.fact_refs, `census_fact_set:${identity}`);
|
|
25
|
+
const expectedDisposition = expectedEntry.fact_refs.length
|
|
26
|
+
? "material_with_facts"
|
|
27
|
+
: "supporting_only";
|
|
28
|
+
if (entry.disposition !== expectedDisposition)
|
|
29
|
+
semanticFactInvalid("census_disposition_mismatch", `${identity}:${entry.disposition}:${expectedDisposition}`);
|
|
30
|
+
validateSemanticFactLocator(manifest, entry.locator, `census:${entry.key}`);
|
|
31
|
+
requireSemanticFactBasis(entry, `census:${entry.key}`);
|
|
32
|
+
}
|
|
33
|
+
assertSameSemanticFactSet([...actual], [...expected.keys()], "inspector_census_universe");
|
|
34
|
+
}
|
|
35
|
+
function expectedCensusRows(manifest, factsFor) {
|
|
36
|
+
const result = new Map();
|
|
37
|
+
const add = (kind, key, pointer, row, factRefs) => result.set(`${kind}:${key}`, {
|
|
38
|
+
pointer,
|
|
39
|
+
row,
|
|
40
|
+
fact_refs: [...new Set(factRefs)].sort(),
|
|
41
|
+
});
|
|
42
|
+
manifest.inputs.forEach((row, index) => add("input", row.key, `/inputs/${index}`, row, row.fact_refs));
|
|
43
|
+
manifest.scope.exclusions.forEach((row, index) => add("scope_exclusion", row.key, `/scope/exclusions/${index}`, row, row.affected_refs.filter((ref) => manifest.facts.some((fact) => fact.key === ref))));
|
|
44
|
+
manifest.family_dispositions.forEach((row, index) => add("family", row.key, `/family_dispositions/${index}`, row, factsFor((fact) => fact.family_ref === row.key)));
|
|
45
|
+
manifest.subjects.forEach((row, index) => add("subject", row.key, `/subjects/${index}`, row, factsFor((fact) => fact.unit_ref === row.key)));
|
|
46
|
+
manifest.relations.forEach((row, index) => add("relation", row.key, `/relations/${index}`, row, factsFor((fact) => fact.unit_ref === row.key)));
|
|
47
|
+
manifest.populations.forEach((row, index) => add("population", row.key, `/populations/${index}`, row, factsFor((fact) => fact.unit_ref === row.key ||
|
|
48
|
+
fact.quantifier.population_ref === row.key)));
|
|
49
|
+
addAxisCensusRows(manifest, factsFor, add);
|
|
50
|
+
manifest.condition_rules.forEach((row, index) => add("condition_rule", row.key, `/condition_rules/${index}`, row, factsFor((fact) => row.condition_refs.includes(fact.condition_ref))));
|
|
51
|
+
manifest.conditions.forEach((row, index) => add("condition", row.key, `/conditions/${index}`, row, factsFor((fact) => fact.condition_ref === row.key)));
|
|
52
|
+
manifest.condition_exclusions.forEach((row, index) => add("condition", row.key, `/condition_exclusions/${index}`, row, []));
|
|
53
|
+
manifest.property_dispositions.forEach((row, index) => add("property", row.key, `/property_dispositions/${index}`, row, factsFor((fact) => fact.property_ref === row.key)));
|
|
54
|
+
manifest.fact_cells.forEach((row, index) => add("fact_cell", row.key, `/fact_cells/${index}`, row, row.fact_ref ? [row.fact_ref] : []));
|
|
55
|
+
manifest.facts.forEach((row, index) => add("fact", row.key, `/facts/${index}`, row, [row.key]));
|
|
56
|
+
manifest.proof_obligations.forEach((row, index) => add("proof_obligation", row.key, `/proof_obligations/${index}`, row, [
|
|
57
|
+
row.fact_ref,
|
|
58
|
+
]));
|
|
59
|
+
addProofSupportCensusRows(manifest, add);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
function addAxisCensusRows(manifest, factsFor, add) {
|
|
63
|
+
manifest.axis_dispositions.forEach((row, axisIndex) => {
|
|
64
|
+
add("axis", row.key, `/axis_dispositions/${axisIndex}`, row, factsFor((fact) => manifest.conditions
|
|
65
|
+
.find((condition) => condition.key === fact.condition_ref)
|
|
66
|
+
?.axis_values.some((value) => value.axis_ref === row.key) ?? false));
|
|
67
|
+
row.values.forEach((value, valueIndex) => add("axis_value", `${row.key}:${value.key}`, `/axis_dispositions/${axisIndex}/values/${valueIndex}`, value, factsFor((fact) => manifest.conditions
|
|
68
|
+
.find((condition) => condition.key === fact.condition_ref)
|
|
69
|
+
?.axis_values.some((entry) => entry.axis_ref === row.key && entry.value_ref === value.key) ?? false)));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function addProofSupportCensusRows(manifest, add) {
|
|
73
|
+
manifest.oracles.forEach((row, index) => add("oracle", row.key, `/oracles/${index}`, row, manifest.proof_obligations
|
|
74
|
+
.filter((proof) => proof.oracle_ref === row.key)
|
|
75
|
+
.map((proof) => proof.fact_ref)));
|
|
76
|
+
manifest.environments.forEach((row, index) => add("environment", row.key, `/environments/${index}`, row, manifest.proof_obligations
|
|
77
|
+
.filter((proof) => proof.environment_ref === row.key)
|
|
78
|
+
.map((proof) => proof.fact_ref)));
|
|
79
|
+
manifest.blockers.forEach((row, index) => add("blocker", row.key, `/blockers/${index}`, row, row.affected_refs.filter((ref) => manifest.facts.some((fact) => fact.key === ref))));
|
|
80
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SemanticFactManifestV1 } from "./semantic-fact-types.js";
|
|
2
|
+
type Axis = SemanticFactManifestV1["axis_dispositions"][number];
|
|
3
|
+
type Condition = SemanticFactManifestV1["conditions"][number];
|
|
4
|
+
type Exclusion = SemanticFactManifestV1["condition_exclusions"][number];
|
|
5
|
+
export declare function validateSemanticFactConditionRuleReferences(rule: SemanticFactManifestV1["condition_rules"][number], outcomeRef: string, conditionByRef: Map<string, Condition>, exclusionByRef: Map<string, Exclusion>): void;
|
|
6
|
+
export declare function validateSemanticFactAxisValueCoverage(outcomeRef: string, axes: Map<string, Axis>, rows: Array<Condition | Exclusion>): void;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { semanticFactInvalid } from "./semantic-fact-policy-primitives.js";
|
|
2
|
+
export function validateSemanticFactConditionRuleReferences(rule, outcomeRef, conditionByRef, exclusionByRef) {
|
|
3
|
+
for (const ref of rule.condition_refs) {
|
|
4
|
+
const row = conditionByRef.get(ref);
|
|
5
|
+
if (!row || row.outcome_ref !== outcomeRef)
|
|
6
|
+
semanticFactInvalid("condition_rule_condition_unknown", `${rule.key}:${ref}`);
|
|
7
|
+
}
|
|
8
|
+
for (const ref of rule.exclusion_refs) {
|
|
9
|
+
const row = exclusionByRef.get(ref);
|
|
10
|
+
if (!row || row.outcome_ref !== outcomeRef)
|
|
11
|
+
semanticFactInvalid("condition_rule_exclusion_unknown", `${rule.key}:${ref}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function validateSemanticFactAxisValueCoverage(outcomeRef, axes, rows) {
|
|
15
|
+
for (const axis of axes.values())
|
|
16
|
+
for (const value of axis.values)
|
|
17
|
+
if (!rows.some((item) => item.axis_values.some((entry) => entry.axis_ref === axis.key && entry.value_ref === value.key)))
|
|
18
|
+
semanticFactInvalid("axis_value_uncovered", `${outcomeRef}:${axis.key}:${value.key}`);
|
|
19
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { isAtomicSemanticFactAtom, isCustomSemanticFactName, SEMANTIC_FACT_STANDARD_CONDITION_AXES, } from "./semantic-fact-catalog.js";
|
|
2
|
+
import { validateSemanticFactAxisValueCoverage, validateSemanticFactConditionRuleReferences, } from "./semantic-fact-policy-condition-references.js";
|
|
3
|
+
import { assertSameSemanticFactSet, canonicalSemanticFactCondition, requireSemanticFactBasis, requireSemanticFactSubset, semanticFactInvalid, uniqueNonemptySemanticFacts, uniqueSemanticFacts, validateSemanticFactCrossProduct, } from "./semantic-fact-policy-primitives.js";
|
|
4
|
+
export function validateSemanticFactAxisAndConditionClosure(manifest) {
|
|
5
|
+
validateAxisCatalog(manifest);
|
|
6
|
+
const conditionByRef = new Map(manifest.conditions.map((item) => [item.key, item]));
|
|
7
|
+
const exclusionByRef = new Map(manifest.condition_exclusions.map((item) => [item.key, item]));
|
|
8
|
+
for (const exclusion of manifest.condition_exclusions)
|
|
9
|
+
if (exclusion.disposition === "decision_required" ||
|
|
10
|
+
exclusion.disposition === "unavailable")
|
|
11
|
+
semanticFactInvalid("condition_exclusion_unresolved", `${exclusion.key}:${exclusion.disposition}`);
|
|
12
|
+
for (const outcomeRef of manifest.scope.outcome_refs)
|
|
13
|
+
validateOutcomeConditions(manifest, outcomeRef, conditionByRef, exclusionByRef);
|
|
14
|
+
}
|
|
15
|
+
function validateAxisCatalog(manifest) {
|
|
16
|
+
const standardRows = manifest.axis_dispositions.filter((item) => item.standard);
|
|
17
|
+
uniqueSemanticFacts(manifest.axis_dispositions.flatMap((item) => item.outcome_refs.map((outcomeRef) => `${outcomeRef}\0${item.axis}`)), "axis_outcome_identity");
|
|
18
|
+
for (const axis of manifest.axis_dispositions)
|
|
19
|
+
validateAxis(manifest, axis);
|
|
20
|
+
for (const outcomeRef of manifest.scope.outcome_refs)
|
|
21
|
+
for (const axis of SEMANTIC_FACT_STANDARD_CONDITION_AXES) {
|
|
22
|
+
const rows = standardRows.filter((item) => item.axis === axis && item.outcome_refs.includes(outcomeRef));
|
|
23
|
+
if (rows.length !== 1)
|
|
24
|
+
semanticFactInvalid("standard_axis_outcome_disposition_required", `${outcomeRef}:${axis}:${rows.length}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function validateAxis(manifest, axis) {
|
|
28
|
+
const isStandard = SEMANTIC_FACT_STANDARD_CONDITION_AXES.includes(axis.axis);
|
|
29
|
+
if (axis.standard !== isStandard)
|
|
30
|
+
semanticFactInvalid("axis_standard_flag_mismatch", axis.axis);
|
|
31
|
+
if (!axis.standard && !isCustomSemanticFactName(axis.axis))
|
|
32
|
+
semanticFactInvalid("custom_axis_name_required", axis.axis);
|
|
33
|
+
uniqueNonemptySemanticFacts(axis.outcome_refs, `axis_outcomes:${axis.key}`);
|
|
34
|
+
requireSemanticFactSubset(axis.outcome_refs, manifest.scope.outcome_refs, "axis_outcome_unknown", axis.key);
|
|
35
|
+
uniqueSemanticFacts(axis.values.map((item) => item.key), `axis_values:${axis.key}`);
|
|
36
|
+
for (const value of axis.values) {
|
|
37
|
+
if (!isAtomicSemanticFactAtom(value.key))
|
|
38
|
+
semanticFactInvalid("axis_value_not_atomic", `${axis.key}:${value.key}`);
|
|
39
|
+
requireSemanticFactBasis(value, `axis_value:${axis.key}:${value.key}`);
|
|
40
|
+
}
|
|
41
|
+
if (axis.disposition === "applicable" && !axis.values.length)
|
|
42
|
+
semanticFactInvalid("applicable_axis_values_required", axis.key);
|
|
43
|
+
if (axis.disposition !== "applicable" && axis.values.length)
|
|
44
|
+
semanticFactInvalid("inapplicable_axis_values_forbidden", `${axis.key}:${axis.disposition}`);
|
|
45
|
+
if (axis.disposition === "decision_required" ||
|
|
46
|
+
axis.disposition === "unavailable")
|
|
47
|
+
semanticFactInvalid("axis_unresolved", `${axis.key}:${axis.disposition}`);
|
|
48
|
+
requireSemanticFactBasis(axis, `axis:${axis.key}`);
|
|
49
|
+
}
|
|
50
|
+
function validateOutcomeConditions(manifest, outcomeRef, conditionByRef, exclusionByRef) {
|
|
51
|
+
const applicableAxes = manifest.axis_dispositions.filter((item) => item.disposition === "applicable" &&
|
|
52
|
+
item.outcome_refs.includes(outcomeRef));
|
|
53
|
+
const axisByRef = new Map(applicableAxes.map((item) => [item.key, item]));
|
|
54
|
+
const expectedAxes = [...axisByRef.keys()].sort();
|
|
55
|
+
const conditions = manifest.conditions.filter((item) => item.outcome_ref === outcomeRef);
|
|
56
|
+
const exclusions = manifest.condition_exclusions.filter((item) => item.outcome_ref === outcomeRef);
|
|
57
|
+
if (!conditions.length)
|
|
58
|
+
semanticFactInvalid("condition_required", outcomeRef);
|
|
59
|
+
validateConditionRows([...conditions, ...exclusions], axisByRef, expectedAxes);
|
|
60
|
+
uniqueSemanticFacts([...conditions, ...exclusions].map((item) => canonicalSemanticFactCondition(item.axis_values)), `condition_combination:${outcomeRef}`);
|
|
61
|
+
const rules = manifest.condition_rules.filter((item) => item.outcome_ref === outcomeRef);
|
|
62
|
+
if (!applicableAxes.length) {
|
|
63
|
+
if (rules.length)
|
|
64
|
+
semanticFactInvalid("condition_rules_without_axes", outcomeRef);
|
|
65
|
+
if (conditions.length !== 1 ||
|
|
66
|
+
conditions[0].axis_values.length ||
|
|
67
|
+
exclusions.length)
|
|
68
|
+
semanticFactInvalid("baseline_condition_required", outcomeRef);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (rules.length !== 1)
|
|
72
|
+
semanticFactInvalid("condition_rule_complete_expansion_required", `${outcomeRef}:${rules.length}`);
|
|
73
|
+
validateOutcomeConditionRule(rules[0], outcomeRef, axisByRef, expectedAxes, conditions, exclusions, conditionByRef, exclusionByRef);
|
|
74
|
+
}
|
|
75
|
+
function validateConditionRows(rows, axisByRef, expectedAxes) {
|
|
76
|
+
for (const row of rows) {
|
|
77
|
+
uniqueSemanticFacts(row.axis_values.map((item) => item.axis_ref), `condition_axis:${row.key}`);
|
|
78
|
+
assertSameSemanticFactSet(row.axis_values.map((item) => item.axis_ref), expectedAxes, `condition_axis_set:${row.key}`);
|
|
79
|
+
for (const axisValue of row.axis_values) {
|
|
80
|
+
const axis = axisByRef.get(axisValue.axis_ref);
|
|
81
|
+
if (!axis)
|
|
82
|
+
semanticFactInvalid("condition_axis_unknown", `${row.key}:${axisValue.axis_ref}`);
|
|
83
|
+
if (!axis.values.some((item) => item.key === axisValue.value_ref))
|
|
84
|
+
semanticFactInvalid("condition_axis_value_unknown", `${row.key}:${axisValue.axis_ref}:${axisValue.value_ref}`);
|
|
85
|
+
}
|
|
86
|
+
requireSemanticFactBasis(row, `condition:${row.key}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function validateOutcomeConditionRule(rule, outcomeRef, axisByRef, expectedAxes, conditions, exclusions, conditionByRef, exclusionByRef) {
|
|
90
|
+
assertSameSemanticFactSet(rule.axis_refs, expectedAxes, `condition_rule_axis_universe:${outcomeRef}`);
|
|
91
|
+
uniqueNonemptySemanticFacts(rule.axis_refs, `condition_rule_axes:${rule.key}`);
|
|
92
|
+
uniqueSemanticFacts(rule.condition_refs, `condition_rule_conditions:${rule.key}`);
|
|
93
|
+
uniqueSemanticFacts(rule.exclusion_refs, `condition_rule_exclusions:${rule.key}`);
|
|
94
|
+
requireSemanticFactBasis(rule, `condition_rule:${rule.key}`);
|
|
95
|
+
validateSemanticFactConditionRuleReferences(rule, outcomeRef, conditionByRef, exclusionByRef);
|
|
96
|
+
if (!rule.condition_refs.length && !rule.exclusion_refs.length)
|
|
97
|
+
semanticFactInvalid("condition_rule_cells_required", rule.key);
|
|
98
|
+
validateSemanticFactCrossProduct(rule, axisByRef, conditionByRef, exclusionByRef);
|
|
99
|
+
assertSameSemanticFactSet(rule.condition_refs, conditions.map((item) => item.key), `condition_rule_condition_closure:${outcomeRef}`);
|
|
100
|
+
assertSameSemanticFactSet(rule.exclusion_refs, exclusions.map((item) => item.key), `condition_rule_exclusion_closure:${outcomeRef}`);
|
|
101
|
+
validateSemanticFactAxisValueCoverage(outcomeRef, axisByRef, [
|
|
102
|
+
...conditions,
|
|
103
|
+
...exclusions,
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SemanticFactManifestIndexV1 } from "./semantic-fact-policy.js";
|
|
2
|
+
import type { SemanticFactManifestV1 } from "./semantic-fact-types.js";
|
|
3
|
+
export declare function validateSemanticFactClosure(manifest: SemanticFactManifestV1, units: SemanticFactManifestIndexV1["unit_by_ref"]): void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { assertSameSemanticFactSet, requireSemanticFactBasis, requireSemanticFactSubset, semanticFactCellIdentity, semanticFactInvalid, uniqueNonemptySemanticFacts, validateSemanticFactLocatedValue, validateSemanticFactQuantifier, } from "./semantic-fact-policy-primitives.js";
|
|
2
|
+
export function validateSemanticFactClosure(manifest, units) {
|
|
3
|
+
const propertyByRef = new Map(manifest.property_dispositions.map((item) => [item.key, item]));
|
|
4
|
+
const conditionByRef = new Map(manifest.conditions.map((item) => [item.key, item]));
|
|
5
|
+
const expectedCells = [];
|
|
6
|
+
for (const property of manifest.property_dispositions)
|
|
7
|
+
for (const unitRef of property.applicable_unit_refs) {
|
|
8
|
+
const unit = units.get(unitRef);
|
|
9
|
+
for (const conditionRef of property.condition_refs)
|
|
10
|
+
if (conditionByRef.get(conditionRef)?.outcome_ref === unit.outcome_ref)
|
|
11
|
+
expectedCells.push(semanticFactCellIdentity(unitRef, conditionRef, property.key));
|
|
12
|
+
}
|
|
13
|
+
assertSameSemanticFactSet(manifest.fact_cells.map((item) => semanticFactCellIdentity(item.unit_ref, item.condition_ref, item.property_ref)), expectedCells, "fact_cell_universe");
|
|
14
|
+
const factByRef = new Map(manifest.facts.map((item) => [item.key, item]));
|
|
15
|
+
const specifiedRefs = [];
|
|
16
|
+
for (const cell of manifest.fact_cells)
|
|
17
|
+
validateFactCell(cell, units, propertyByRef, conditionByRef, factByRef, specifiedRefs);
|
|
18
|
+
assertSameSemanticFactSet(specifiedRefs, manifest.facts.map((item) => item.key), "specified_fact_set");
|
|
19
|
+
for (const fact of manifest.facts)
|
|
20
|
+
validateFact(manifest, fact);
|
|
21
|
+
}
|
|
22
|
+
function validateFactCell(cell, units, propertyByRef, conditionByRef, factByRef, specifiedRefs) {
|
|
23
|
+
const unit = units.get(cell.unit_ref);
|
|
24
|
+
const property = propertyByRef.get(cell.property_ref);
|
|
25
|
+
const condition = conditionByRef.get(cell.condition_ref);
|
|
26
|
+
if (!unit || !property || !condition)
|
|
27
|
+
semanticFactInvalid("fact_cell_reference_unknown", `${cell.key}:${cell.unit_ref}:${cell.condition_ref}:${cell.property_ref}`);
|
|
28
|
+
if (cell.outcome_ref !== unit.outcome_ref ||
|
|
29
|
+
condition.outcome_ref !== unit.outcome_ref)
|
|
30
|
+
semanticFactInvalid("fact_cell_outcome_mismatch", cell.key);
|
|
31
|
+
if (cell.disposition === "decision_required" ||
|
|
32
|
+
cell.disposition === "unavailable")
|
|
33
|
+
semanticFactInvalid("fact_cell_unresolved", `${cell.key}:${cell.disposition}`);
|
|
34
|
+
if (cell.disposition === "specified") {
|
|
35
|
+
if (!cell.fact_ref)
|
|
36
|
+
semanticFactInvalid("specified_fact_cell_ref_required", cell.key);
|
|
37
|
+
specifiedRefs.push(cell.fact_ref);
|
|
38
|
+
const fact = factByRef.get(cell.fact_ref);
|
|
39
|
+
if (!fact)
|
|
40
|
+
semanticFactInvalid("fact_cell_fact_unknown", `${cell.key}:${cell.fact_ref}`);
|
|
41
|
+
if (fact.cell_ref !== cell.key ||
|
|
42
|
+
fact.outcome_ref !== cell.outcome_ref ||
|
|
43
|
+
fact.unit_ref !== cell.unit_ref ||
|
|
44
|
+
fact.condition_ref !== cell.condition_ref ||
|
|
45
|
+
fact.property_ref !== cell.property_ref ||
|
|
46
|
+
fact.family_ref !== unit.family_ref ||
|
|
47
|
+
fact.value_kind !== property.value_kind)
|
|
48
|
+
semanticFactInvalid("fact_cell_fact_identity_mismatch", cell.key);
|
|
49
|
+
}
|
|
50
|
+
else if (cell.fact_ref)
|
|
51
|
+
semanticFactInvalid("non_specified_fact_cell_ref_forbidden", cell.key);
|
|
52
|
+
requireSemanticFactBasis(cell, `fact_cell:${cell.key}`);
|
|
53
|
+
}
|
|
54
|
+
function validateFact(manifest, fact) {
|
|
55
|
+
uniqueNonemptySemanticFacts(fact.source_item_refs, `fact_source_items:${fact.key}`);
|
|
56
|
+
requireSemanticFactSubset(fact.source_item_refs, manifest.scope.source_item_refs, "fact_source_item_unknown", fact.key);
|
|
57
|
+
if (!fact.provenance.basis_refs.length)
|
|
58
|
+
semanticFactInvalid("fact_provenance_basis_required", fact.key);
|
|
59
|
+
if (fact.provenance.kind === "logically_derived" &&
|
|
60
|
+
!fact.provenance.derivation)
|
|
61
|
+
semanticFactInvalid("fact_derivation_required", fact.key);
|
|
62
|
+
if (fact.provenance.kind !== "logically_derived" &&
|
|
63
|
+
fact.provenance.derivation)
|
|
64
|
+
semanticFactInvalid("fact_derivation_forbidden", fact.key);
|
|
65
|
+
if (fact.observation_sensitivity === "protected" &&
|
|
66
|
+
fact.expected.representation !== "digest_only")
|
|
67
|
+
semanticFactInvalid("protected_fact_digest_only_required", fact.key);
|
|
68
|
+
if (fact.observation_sensitivity === "plain" &&
|
|
69
|
+
fact.expected.representation === "digest_only")
|
|
70
|
+
semanticFactInvalid("plain_fact_digest_only_forbidden", fact.key);
|
|
71
|
+
validateSemanticFactLocatedValue(manifest, fact.expected, `fact:${fact.key}:expected`);
|
|
72
|
+
validateSemanticFactQuantifier(manifest, fact);
|
|
73
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SemanticFactManifestCollectionName, SemanticFactManifestV1 } from "./semantic-fact-types.js";
|
|
2
|
+
export declare function validateSemanticFactLocatedValue(manifest: SemanticFactManifestV1, located: SemanticFactManifestV1["facts"][number]["expected"], label: string): void;
|
|
3
|
+
export declare function validateSemanticFactLocator(manifest: SemanticFactManifestV1, locator: {
|
|
4
|
+
material_ref: string;
|
|
5
|
+
kind: string;
|
|
6
|
+
value: string;
|
|
7
|
+
}, label: string): void;
|
|
8
|
+
export declare function validateSemanticFactQuantifier(manifest: SemanticFactManifestV1, fact: SemanticFactManifestV1["facts"][number]): void;
|
|
9
|
+
export declare function validateSemanticFactCrossProduct(rule: SemanticFactManifestV1["condition_rules"][number], axes: Map<string, SemanticFactManifestV1["axis_dispositions"][number]>, conditions: Map<string, SemanticFactManifestV1["conditions"][number]>, exclusions: Map<string, SemanticFactManifestV1["condition_exclusions"][number]>): void;
|
|
10
|
+
export declare function semanticFactCollections(manifest: SemanticFactManifestV1): Record<SemanticFactManifestCollectionName, Array<{
|
|
11
|
+
key: string;
|
|
12
|
+
}>>;
|
|
13
|
+
export declare function semanticFactCellIdentity(unitRef: string, conditionRef: string, propertyRef: string): string;
|
|
14
|
+
export declare function canonicalSemanticFactCondition(values: Array<{
|
|
15
|
+
axis_ref: string;
|
|
16
|
+
value_ref: string;
|
|
17
|
+
}>): string;
|
|
18
|
+
export declare function assertNoSemanticFactParentCycle(key: string, subjects: SemanticFactManifestV1["subjects"]): void;
|
|
19
|
+
export declare function requireSemanticFactBasis(value: {
|
|
20
|
+
basis_refs: string[];
|
|
21
|
+
}, label: string): void;
|
|
22
|
+
export declare function requireSemanticFactSubset(actual: string[], allowed: string[], code: string, label: string): void;
|
|
23
|
+
export declare function uniqueNonemptySemanticFacts(values: Array<string | number>, label: string): void;
|
|
24
|
+
export declare function uniqueSemanticFacts(values: Array<string | number>, label: string): void;
|
|
25
|
+
export declare function assertSameSemanticFactSet(actual: Array<string | number>, expected: Array<string | number>, label: string): void;
|
|
26
|
+
export declare function semanticFactInvalid(code: string, detail: string): never;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
2
|
+
export function validateSemanticFactLocatedValue(manifest, located, label) {
|
|
3
|
+
validateSemanticFactLocator(manifest, located.locator, label);
|
|
4
|
+
if (located.representation === "inline") {
|
|
5
|
+
const digest = sha256Hex(canonicalValueJson(located.value));
|
|
6
|
+
if (digest !== located.sha256)
|
|
7
|
+
semanticFactInvalid("inline_value_digest_mismatch", `${label}:${located.sha256}:${digest}`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function validateSemanticFactLocator(manifest, locator, label) {
|
|
11
|
+
const inputRefs = new Set(manifest.inputs.map((item) => item.key));
|
|
12
|
+
if (locator.kind === "manifest_pointer"
|
|
13
|
+
? locator.material_ref !== manifest.key
|
|
14
|
+
: !inputRefs.has(locator.material_ref))
|
|
15
|
+
semanticFactInvalid("locator_material_unknown", `${label}:${locator.material_ref}`);
|
|
16
|
+
}
|
|
17
|
+
export function validateSemanticFactQuantifier(manifest, fact) {
|
|
18
|
+
const quantifier = fact.quantifier;
|
|
19
|
+
const population = quantifier.population_ref
|
|
20
|
+
? (manifest.populations.find((item) => item.key === quantifier.population_ref) ?? null)
|
|
21
|
+
: null;
|
|
22
|
+
validateSemanticFactQuantifierPopulation(fact, population);
|
|
23
|
+
validateSemanticFactQuantifierNonnegativeBounds(fact);
|
|
24
|
+
validateSemanticFactQuantifierKindBounds(fact);
|
|
25
|
+
}
|
|
26
|
+
function validateSemanticFactQuantifierPopulation(fact, population) {
|
|
27
|
+
const quantifier = fact.quantifier;
|
|
28
|
+
if (quantifier.population_ref && !population)
|
|
29
|
+
semanticFactInvalid("quantifier_population_unknown", `${fact.key}:${quantifier.population_ref}`);
|
|
30
|
+
const populationKinds = new Set([
|
|
31
|
+
"all",
|
|
32
|
+
"any",
|
|
33
|
+
"none",
|
|
34
|
+
"exactly",
|
|
35
|
+
"at_least",
|
|
36
|
+
"at_most",
|
|
37
|
+
"range",
|
|
38
|
+
]);
|
|
39
|
+
if (populationKinds.has(quantifier.kind) && !quantifier.population_ref)
|
|
40
|
+
semanticFactInvalid("quantifier_population_required", fact.key);
|
|
41
|
+
if (population && population.outcome_ref !== fact.outcome_ref)
|
|
42
|
+
semanticFactInvalid("quantifier_population_outcome_mismatch", `${fact.key}:${population.key}`);
|
|
43
|
+
if (quantifier.kind === "one" && quantifier.population_ref)
|
|
44
|
+
semanticFactInvalid("quantifier_population_forbidden", fact.key);
|
|
45
|
+
}
|
|
46
|
+
function validateSemanticFactQuantifierNonnegativeBounds(fact) {
|
|
47
|
+
for (const [label, value] of [
|
|
48
|
+
["minimum", fact.quantifier.minimum],
|
|
49
|
+
["maximum", fact.quantifier.maximum],
|
|
50
|
+
])
|
|
51
|
+
if (value !== null && (!Number.isInteger(value) || value < 0))
|
|
52
|
+
semanticFactInvalid(`quantifier_${label}_invalid`, fact.key);
|
|
53
|
+
}
|
|
54
|
+
function validateSemanticFactQuantifierKindBounds(fact) {
|
|
55
|
+
const quantifier = fact.quantifier;
|
|
56
|
+
if (quantifier.kind === "exactly" &&
|
|
57
|
+
(quantifier.minimum === null ||
|
|
58
|
+
quantifier.maximum === null ||
|
|
59
|
+
quantifier.minimum !== quantifier.maximum))
|
|
60
|
+
semanticFactInvalid("quantifier_exactly_bounds_invalid", fact.key);
|
|
61
|
+
if (quantifier.kind === "at_least" && quantifier.minimum === null)
|
|
62
|
+
semanticFactInvalid("quantifier_minimum_required", fact.key);
|
|
63
|
+
if (quantifier.kind === "at_most" && quantifier.maximum === null)
|
|
64
|
+
semanticFactInvalid("quantifier_maximum_required", fact.key);
|
|
65
|
+
if (quantifier.kind === "range" &&
|
|
66
|
+
(quantifier.minimum === null ||
|
|
67
|
+
quantifier.maximum === null ||
|
|
68
|
+
quantifier.minimum > quantifier.maximum))
|
|
69
|
+
semanticFactInvalid("quantifier_range_invalid", fact.key);
|
|
70
|
+
if (["one", "all", "any", "none"].includes(quantifier.kind) &&
|
|
71
|
+
(quantifier.minimum !== null || quantifier.maximum !== null))
|
|
72
|
+
semanticFactInvalid("quantifier_bounds_forbidden", fact.key);
|
|
73
|
+
if (quantifier.kind === "at_least" && quantifier.maximum !== null)
|
|
74
|
+
semanticFactInvalid("quantifier_maximum_forbidden", fact.key);
|
|
75
|
+
if (quantifier.kind === "at_most" && quantifier.minimum !== null)
|
|
76
|
+
semanticFactInvalid("quantifier_minimum_forbidden", fact.key);
|
|
77
|
+
}
|
|
78
|
+
export function validateSemanticFactCrossProduct(rule, axes, conditions, exclusions) {
|
|
79
|
+
const values = rule.axis_refs.map((axisRef) => {
|
|
80
|
+
const axis = axes.get(axisRef);
|
|
81
|
+
if (!axis)
|
|
82
|
+
semanticFactInvalid("condition_rule_axis_unknown", `${rule.key}:${axisRef}`);
|
|
83
|
+
return axis.values.map((item) => `${axisRef}=${item.key}`);
|
|
84
|
+
});
|
|
85
|
+
const expected = semanticFactCartesian(values).map((row) => row.sort().join("|"));
|
|
86
|
+
const actual = [...rule.condition_refs, ...rule.exclusion_refs].map((ref) => {
|
|
87
|
+
const row = conditions.get(ref) ?? exclusions.get(ref);
|
|
88
|
+
if (!row)
|
|
89
|
+
semanticFactInvalid("condition_rule_cell_unknown", `${rule.key}:${ref}`);
|
|
90
|
+
return row.axis_values
|
|
91
|
+
.filter((item) => rule.axis_refs.includes(item.axis_ref))
|
|
92
|
+
.map((item) => `${item.axis_ref}=${item.value_ref}`)
|
|
93
|
+
.sort()
|
|
94
|
+
.join("|");
|
|
95
|
+
});
|
|
96
|
+
assertSameSemanticFactSet(actual, expected, `condition_rule_cross_product:${rule.key}`);
|
|
97
|
+
}
|
|
98
|
+
export function semanticFactCollections(manifest) {
|
|
99
|
+
return {
|
|
100
|
+
inputs: manifest.inputs,
|
|
101
|
+
inspector_census: manifest.inspector.census,
|
|
102
|
+
family_dispositions: manifest.family_dispositions,
|
|
103
|
+
subjects: manifest.subjects,
|
|
104
|
+
relations: manifest.relations,
|
|
105
|
+
populations: manifest.populations,
|
|
106
|
+
axis_dispositions: manifest.axis_dispositions,
|
|
107
|
+
condition_rules: manifest.condition_rules,
|
|
108
|
+
conditions: manifest.conditions,
|
|
109
|
+
condition_exclusions: manifest.condition_exclusions,
|
|
110
|
+
property_dispositions: manifest.property_dispositions,
|
|
111
|
+
fact_cells: manifest.fact_cells,
|
|
112
|
+
facts: manifest.facts,
|
|
113
|
+
proof_obligations: manifest.proof_obligations,
|
|
114
|
+
oracles: manifest.oracles,
|
|
115
|
+
environments: manifest.environments,
|
|
116
|
+
blockers: manifest.blockers,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export function semanticFactCellIdentity(unitRef, conditionRef, propertyRef) {
|
|
120
|
+
return `${unitRef}\0${conditionRef}\0${propertyRef}`;
|
|
121
|
+
}
|
|
122
|
+
export function canonicalSemanticFactCondition(values) {
|
|
123
|
+
return values
|
|
124
|
+
.map((item) => `${item.axis_ref}=${item.value_ref}`)
|
|
125
|
+
.sort()
|
|
126
|
+
.join("|");
|
|
127
|
+
}
|
|
128
|
+
function semanticFactCartesian(values) {
|
|
129
|
+
return values.reduce((rows, current) => rows.flatMap((row) => current.map((value) => [...row, value])), [[]]);
|
|
130
|
+
}
|
|
131
|
+
export function assertNoSemanticFactParentCycle(key, subjects) {
|
|
132
|
+
const byKey = new Map(subjects.map((item) => [item.key, item]));
|
|
133
|
+
const seen = new Set();
|
|
134
|
+
let current = key;
|
|
135
|
+
while (current) {
|
|
136
|
+
if (seen.has(current))
|
|
137
|
+
semanticFactInvalid("subject_parent_cycle", key);
|
|
138
|
+
seen.add(current);
|
|
139
|
+
current = byKey.get(current)?.parent_ref ?? null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export function requireSemanticFactBasis(value, label) {
|
|
143
|
+
uniqueNonemptySemanticFacts(value.basis_refs, `${label}:basis_refs`);
|
|
144
|
+
}
|
|
145
|
+
export function requireSemanticFactSubset(actual, allowed, code, label) {
|
|
146
|
+
const unknown = actual.filter((item) => !allowed.includes(item));
|
|
147
|
+
if (unknown.length)
|
|
148
|
+
semanticFactInvalid(code, `${label}:${unknown.join(",")}`);
|
|
149
|
+
}
|
|
150
|
+
export function uniqueNonemptySemanticFacts(values, label) {
|
|
151
|
+
if (!values.length)
|
|
152
|
+
semanticFactInvalid(`${label}_required`, "");
|
|
153
|
+
uniqueSemanticFacts(values, label);
|
|
154
|
+
}
|
|
155
|
+
export function uniqueSemanticFacts(values, label) {
|
|
156
|
+
if (new Set(values).size !== values.length)
|
|
157
|
+
semanticFactInvalid(`${label}_duplicate`, values.join(","));
|
|
158
|
+
}
|
|
159
|
+
export function assertSameSemanticFactSet(actual, expected, label) {
|
|
160
|
+
if (actual.length !== expected.length ||
|
|
161
|
+
new Set(actual).size !== actual.length ||
|
|
162
|
+
actual.some((item) => !expected.includes(item)))
|
|
163
|
+
semanticFactInvalid(`${label}_mismatch`, `expected=${[...expected].sort().join(",")};actual=${[...actual]
|
|
164
|
+
.sort()
|
|
165
|
+
.join(",")}`);
|
|
166
|
+
}
|
|
167
|
+
export function semanticFactInvalid(code, detail) {
|
|
168
|
+
throw new Error(`semantic_fact_manifest_invalid:${code}${detail ? `:${detail}` : ""}`);
|
|
169
|
+
}
|