project-tiny-context-harness 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +401 -395
- package/assets/README.md +628 -549
- package/assets/README.zh-CN.md +345 -339
- package/assets/agents/.gitkeep +1 -1
- package/assets/agents/AGENTS_CORE.md +70 -70
- 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 +137 -137
- 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 +88 -88
- package/assets/skills/context_surface_contract/SKILL.md +191 -191
- package/assets/skills/context_uiux_design/SKILL.md +172 -172
- package/assets/skills/design-resource-authoring/SKILL.md +85 -85
- package/assets/skills/design-resource-authoring/references/downstream-handoff.md +136 -136
- package/assets/skills/design-resource-authoring/references/open-design-provider.md +135 -135
- package/assets/skills/design-resource-authoring/references/resource-selection.md +173 -173
- 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 +100 -100
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +75 -73
- package/assets/skills/long-task-workflow/references/contract-authoring.md +119 -115
- package/assets/skills/long-task-workflow/references/evidence-design.md +77 -76
- package/assets/skills/long-task-workflow/references/source-authoring.md +102 -99
- package/assets/skills/source-plan-authoring/SKILL.md +14 -14
- package/assets/tools/validate_context.py +442 -442
- package/dist/commands/design-resource.js +2 -2
- package/dist/commands/index.js +26 -26
- package/dist/commands/long-task-authoring.js +83 -9
- package/dist/commands/long-task.js +15 -15
- package/dist/lib/context-graph-snapshot.js +7 -0
- package/dist/lib/design-resource-handoff-shape-evidence.js +3 -0
- package/dist/lib/design-resource-handoff-types.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-coverage.js +1 -0
- package/dist/lib/design-resource-handoff-validation-structure.js +2 -0
- package/dist/lib/execution-target-capabilities.d.ts +2 -0
- package/dist/lib/execution-target-capabilities.js +31 -0
- package/dist/lib/long-task-acceptance-shape.js +81 -20
- package/dist/lib/long-task-applicability-shape.d.ts +4 -0
- package/dist/lib/long-task-applicability-shape.js +54 -0
- package/dist/lib/long-task-assertions-v2.js +8 -0
- package/dist/lib/long-task-authoring-preflight-diagnostics.js +5 -0
- package/dist/lib/long-task-authoring-preflight-repair-order.js +3 -0
- package/dist/lib/long-task-authoring-preflight.js +12 -8
- package/dist/lib/long-task-authority-materials.js +46 -4
- package/dist/lib/long-task-authority-policy.d.ts +54 -0
- package/dist/lib/long-task-authority-policy.js +61 -0
- package/dist/lib/long-task-authority-types.d.ts +12 -2
- package/dist/lib/long-task-authority.js +9 -2
- package/dist/lib/long-task-check-shape.js +6 -1
- package/dist/lib/long-task-claim-definitions.js +27 -42
- package/dist/lib/long-task-claims.js +275 -146
- package/dist/lib/long-task-contract-types.d.ts +42 -8
- package/dist/lib/long-task-control-fields.d.ts +12 -0
- package/dist/lib/long-task-control-fields.js +66 -0
- package/dist/lib/long-task-control-types.d.ts +1 -0
- package/dist/lib/long-task-control-types.js +1 -0
- package/dist/lib/long-task-counterfactual-claim-policy.js +7 -0
- package/dist/lib/long-task-counterfactual-types.d.ts +12 -0
- package/dist/lib/long-task-delivery-compiler.js +1 -0
- package/dist/lib/long-task-delivery-preflight.js +24 -0
- package/dist/lib/long-task-delivery-shape.d.ts +1 -0
- package/dist/lib/long-task-delivery-shape.js +1 -0
- package/dist/lib/long-task-delivery-types.d.ts +1 -0
- package/dist/lib/long-task-delivery-types.js +1 -0
- package/dist/lib/long-task-delivery-validation.js +46 -0
- package/dist/lib/long-task-design-resource-handoff.js +48 -7
- package/dist/lib/long-task-evidence-capability-codec.js +37 -0
- package/dist/lib/long-task-evidence-capability-policy.js +80 -38
- package/dist/lib/long-task-evidence-capability-runtime.js +36 -0
- package/dist/lib/long-task-evidence-capability-types.d.ts +13 -1
- package/dist/lib/long-task-evidence-findings.js +3 -0
- package/dist/lib/long-task-evidence-sensitivity-policy.js +38 -0
- package/dist/lib/long-task-evidence-v2.js +7 -1
- package/dist/lib/long-task-final-integrity.d.ts +16 -0
- package/dist/lib/long-task-final-integrity.js +98 -0
- package/dist/lib/long-task-final-v2.js +17 -34
- package/dist/lib/long-task-outcome-parser.js +22 -4
- package/dist/lib/long-task-playwright-case-evidence.d.ts +2 -0
- package/dist/lib/long-task-playwright-case-evidence.js +10 -2
- package/dist/lib/long-task-playwright-evidence.d.ts +1 -0
- package/dist/lib/long-task-playwright-evidence.js +32 -0
- package/dist/lib/long-task-product-shape.d.ts +3 -1
- package/dist/lib/long-task-product-shape.js +100 -4
- package/dist/lib/long-task-requirement-shape.js +3 -0
- package/dist/lib/long-task-root-shape.js +12 -6
- package/dist/lib/long-task-runner-freeze.js +16 -0
- package/dist/lib/long-task-runtime-types.d.ts +11 -2
- package/dist/lib/long-task-semantic-assurance-policy.d.ts +4 -0
- package/dist/lib/long-task-semantic-assurance-policy.js +52 -0
- package/dist/lib/long-task-semantic-contract-types.d.ts +18 -1
- package/dist/lib/long-task-semantic-drift-migration.js +33 -2
- package/dist/lib/long-task-semantic-mutation.d.ts +7 -0
- package/dist/lib/long-task-semantic-mutation.js +81 -0
- package/dist/lib/long-task-shape-primitives.d.ts +1 -1
- package/dist/lib/long-task-shape-primitives.js +1 -0
- package/dist/lib/long-task-source-authority-types.d.ts +2 -0
- package/dist/lib/long-task-source-item-parser.d.ts +1 -1
- package/dist/lib/long-task-source-item-parser.js +150 -104
- package/dist/lib/long-task-source-markers.d.ts +12 -0
- package/dist/lib/long-task-source-markers.js +146 -0
- package/dist/lib/long-task-source-target-continuity.d.ts +1 -1
- package/dist/lib/long-task-source-target-continuity.js +80 -55
- package/dist/lib/long-task-source-target-index.js +7 -29
- package/dist/lib/long-task-status-v2.d.ts +1 -0
- package/dist/lib/long-task-status-v2.js +2 -2
- package/dist/lib/long-task-target-policy.js +20 -0
- package/dist/lib/long-task-ui-design-policy.js +27 -1
- package/dist/lib/long-task-ui-surface-shape.js +21 -1
- package/dist/lib/long-task-ui-surface-types.d.ts +7 -0
- package/dist/lib/long-task-verification-preview.d.ts +10 -0
- package/dist/lib/long-task-verifier-dependency-closure.d.ts +3 -0
- package/dist/lib/long-task-verifier-dependency-closure.js +179 -0
- package/dist/lib/long-task-verifier-v2.js +12 -2
- package/dist/lib/long-task-workspace.js +5 -1
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +99 -25
- package/migrations/README.md +15 -15
- package/package.json +84 -84
- package/source-mappings.yaml +25 -25
|
@@ -319,8 +319,8 @@ function nextAction(status) {
|
|
|
319
319
|
if (status.ready_outcomes.length) {
|
|
320
320
|
const outcome = status.ready_outcomes[0];
|
|
321
321
|
if (status.outcomes[outcome] === "progress_stale")
|
|
322
|
-
return `Targeted evidence for
|
|
323
|
-
return `
|
|
322
|
+
return `Targeted evidence for Outcome ${outcome} on the acceptance/verification frontier is stale. This is a freshness fact, not an implementation gate or immediate per-edit rerun instruction: coalesce relevant changes and refresh the cheapest reliable declared Check only before relying on this Progress. Implementation may continue, and Final Gate needs no targeted refresh because it reruns every Check.`;
|
|
323
|
+
return `Outcome ${outcome} is on the advisory acceptance/verification frontier. Implementation order remains Goal-owned; run its declared Check when the feedback value justifies the cost, or continue in-scope implementation before the complete Final Gate.`;
|
|
324
324
|
}
|
|
325
325
|
return "Create a clean candidate commit; Stop will run the authoritative Live Final Gate.";
|
|
326
326
|
}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
const FAMILY_CAPABILITY = {
|
|
2
|
+
browser: "browser-runtime",
|
|
3
|
+
native: "native-runtime",
|
|
4
|
+
desktop: "desktop-runtime",
|
|
5
|
+
service: "service-runtime",
|
|
6
|
+
process: "process-runtime",
|
|
7
|
+
external: "external-runtime",
|
|
8
|
+
};
|
|
1
9
|
export function validateExecutionTargets(contract, report) {
|
|
2
10
|
const targets = indexExecutionTargets(contract, report);
|
|
3
11
|
const requiredTargetRefs = contract.task.target_profile.required_target_refs;
|
|
@@ -12,6 +20,13 @@ function indexExecutionTargets(contract, report) {
|
|
|
12
20
|
if (targets.has(target.key))
|
|
13
21
|
issue(report, "execution_target_key_duplicate", target.key);
|
|
14
22
|
targets.set(target.key, target);
|
|
23
|
+
if (!target.capabilities.length)
|
|
24
|
+
issue(report, "execution_target_capability_required", target.key);
|
|
25
|
+
if (new Set(target.capabilities).size !== target.capabilities.length)
|
|
26
|
+
issue(report, "execution_target_capability_duplicate", target.key);
|
|
27
|
+
const familyCapability = FAMILY_CAPABILITY[target.runtime_family];
|
|
28
|
+
if (!target.capabilities.includes(familyCapability))
|
|
29
|
+
issue(report, "execution_target_family_capability_required", `${target.key}:${familyCapability}`);
|
|
15
30
|
}
|
|
16
31
|
if (![...targets.values()].some((target) => target.role === "product"))
|
|
17
32
|
issue(report, "product_execution_target_required", contract.task.id);
|
|
@@ -29,6 +44,11 @@ function validateRequiredTargetRefs(contract, targets, report) {
|
|
|
29
44
|
issue(report, "target_profile_required_target_ref_unknown", targetRef);
|
|
30
45
|
else if (target.role !== "product")
|
|
31
46
|
issue(report, "target_profile_required_target_must_be_product", targetRef);
|
|
47
|
+
else {
|
|
48
|
+
for (const capability of ["cold-start", "production-root"])
|
|
49
|
+
if (!target.capabilities.includes(capability))
|
|
50
|
+
issue(report, "required_target_capability_missing", `${targetRef}:${capability}`);
|
|
51
|
+
}
|
|
32
52
|
}
|
|
33
53
|
}
|
|
34
54
|
function validateCheckTarget(outcomeKey, check, targets, report) {
|
|
@@ -18,6 +18,7 @@ function validateDesignTargets({ outcome, binding, outcomeClaims, checks, carrie
|
|
|
18
18
|
unique(target.verification_method_bindings.map((item) => item.assertion_ref), "ui_design_target_verification_assertion_duplicate", label, report);
|
|
19
19
|
if (!target.verification_method_bindings.length)
|
|
20
20
|
issue(report, "ui_design_target_verification_methods_required", label);
|
|
21
|
+
const methodArtifactPaths = new Set();
|
|
21
22
|
for (const [name, values] of [
|
|
22
23
|
["source_paths", target.source_paths],
|
|
23
24
|
["condition_keys", target.condition_keys],
|
|
@@ -49,12 +50,37 @@ function validateDesignTargets({ outcome, binding, outcomeClaims, checks, carrie
|
|
|
49
50
|
issue(report, "ui_design_target_assertion_duplicate", `${label}:${methodBinding.assertion_ref}`);
|
|
50
51
|
if (methodBinding.assertion_ref !== target.conformance_assertion_ref)
|
|
51
52
|
designAssertionRefs.add(identity);
|
|
52
|
-
|
|
53
|
+
const methodAssertion = check.positive_assertions.find((item) => item.key === methodBinding.assertion_ref);
|
|
54
|
+
if (!methodAssertion)
|
|
53
55
|
issue(report, "ui_design_target_verification_assertion_unknown", `${label}:${methodBinding.method}:${methodBinding.assertion_ref}`);
|
|
56
|
+
else if (!methodAssertion.evidence_capabilities.includes("design_method"))
|
|
57
|
+
issue(report, "ui_design_target_verification_capability_required", `${label}:${methodBinding.method}:design_method`);
|
|
58
|
+
const conditionKeys = methodBinding.evidence_artifacts.map((item) => item.condition_key);
|
|
59
|
+
unique(conditionKeys, "ui_design_method_evidence_condition_duplicate", `${label}:${methodBinding.method}`, report);
|
|
60
|
+
if (!sameSet(conditionKeys, target.condition_keys))
|
|
61
|
+
issue(report, "ui_design_method_evidence_conditions_mismatch", `${label}:${methodBinding.method}`);
|
|
62
|
+
for (const artifact of methodBinding.evidence_artifacts) {
|
|
63
|
+
for (const [kind, artifactPath] of [
|
|
64
|
+
["record", artifact.path],
|
|
65
|
+
["observation", artifact.observation_path],
|
|
66
|
+
]) {
|
|
67
|
+
if (methodArtifactPaths.has(artifactPath))
|
|
68
|
+
issue(report, "ui_design_method_evidence_artifact_reused", `${label}:${kind}:${artifactPath}`);
|
|
69
|
+
methodArtifactPaths.add(artifactPath);
|
|
70
|
+
if (!check.artifact_globs.some((pattern) => matchesRepoPattern(artifactPath, pattern)))
|
|
71
|
+
issue(report, "ui_design_method_evidence_artifact_glob_missing", `${label}:${methodBinding.method}:${kind}:${artifactPath}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
54
74
|
}
|
|
55
75
|
validateDesignFiles(target, check, label, report);
|
|
56
76
|
}
|
|
57
77
|
}
|
|
78
|
+
function sameSet(left, right) {
|
|
79
|
+
const leftSet = [...new Set(left)].sort();
|
|
80
|
+
const rightSet = [...new Set(right)].sort();
|
|
81
|
+
return (leftSet.length === rightSet.length &&
|
|
82
|
+
leftSet.every((value, index) => value === rightSet[index]));
|
|
83
|
+
}
|
|
58
84
|
function validateDesignAssertion(target, check, label, report) {
|
|
59
85
|
const assertion = check.positive_assertions.find((item) => item.key === target.conformance_assertion_ref);
|
|
60
86
|
if (!assertion) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DESIGN_RESOURCE_VERIFICATION_METHODS } from "./design-resource-handoff-types.js";
|
|
2
|
+
import { EXECUTION_TARGET_CAPABILITIES } from "./execution-target-capabilities.js";
|
|
2
3
|
import { array, key, literal, object, repositoryFile, repositoryFiles, string, strings, } from "./long-task-shape-primitives.js";
|
|
3
4
|
export function parseSurfaceBindings(value, label) {
|
|
4
5
|
return array(value, label).map((item, index) => {
|
|
@@ -67,6 +68,7 @@ function parseAcceptanceBlockers(value, label) {
|
|
|
67
68
|
"refs",
|
|
68
69
|
"source_item_refs",
|
|
69
70
|
"verification_methods",
|
|
71
|
+
"required_capabilities",
|
|
70
72
|
"rationale",
|
|
71
73
|
]);
|
|
72
74
|
return {
|
|
@@ -75,6 +77,7 @@ function parseAcceptanceBlockers(value, label) {
|
|
|
75
77
|
refs: strings(row.refs, `${itemLabel}.refs`),
|
|
76
78
|
source_item_refs: strings(row.source_item_refs, `${itemLabel}.source_item_refs`),
|
|
77
79
|
verification_methods: array(row.verification_methods, `${itemLabel}.verification_methods`).map((method, methodIndex) => literal(method, DESIGN_RESOURCE_VERIFICATION_METHODS, `${itemLabel}.verification_methods[${methodIndex}]`)),
|
|
80
|
+
required_capabilities: array(row.required_capabilities, `${itemLabel}.required_capabilities`).map((capability, capabilityIndex) => literal(capability, EXECUTION_TARGET_CAPABILITIES, `${itemLabel}.required_capabilities[${capabilityIndex}]`)),
|
|
78
81
|
rationale: string(row.rationale, `${itemLabel}.rationale`),
|
|
79
82
|
};
|
|
80
83
|
});
|
|
@@ -82,10 +85,27 @@ function parseAcceptanceBlockers(value, label) {
|
|
|
82
85
|
function parseVerificationMethodBindings(value, label) {
|
|
83
86
|
return array(value, label).map((item, index) => {
|
|
84
87
|
const itemLabel = `${label}[${index}]`;
|
|
85
|
-
const row = object(item, itemLabel, [
|
|
88
|
+
const row = object(item, itemLabel, [
|
|
89
|
+
"method",
|
|
90
|
+
"assertion_ref",
|
|
91
|
+
"evidence_artifacts",
|
|
92
|
+
]);
|
|
86
93
|
return {
|
|
87
94
|
method: literal(row.method, DESIGN_RESOURCE_VERIFICATION_METHODS, `${itemLabel}.method`),
|
|
88
95
|
assertion_ref: key(row.assertion_ref, `${itemLabel}.assertion_ref`),
|
|
96
|
+
evidence_artifacts: array(row.evidence_artifacts, `${itemLabel}.evidence_artifacts`).map((artifact, artifactIndex) => {
|
|
97
|
+
const artifactLabel = `${itemLabel}.evidence_artifacts[${artifactIndex}]`;
|
|
98
|
+
const entry = object(artifact, artifactLabel, [
|
|
99
|
+
"condition_key",
|
|
100
|
+
"path",
|
|
101
|
+
"observation_path",
|
|
102
|
+
]);
|
|
103
|
+
return {
|
|
104
|
+
condition_key: key(entry.condition_key, `${artifactLabel}.condition_key`),
|
|
105
|
+
path: repositoryFile(entry.path, `${artifactLabel}.path`),
|
|
106
|
+
observation_path: repositoryFile(entry.observation_path, `${artifactLabel}.observation_path`),
|
|
107
|
+
};
|
|
108
|
+
}),
|
|
89
109
|
};
|
|
90
110
|
});
|
|
91
111
|
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { DesignResourceVerificationMethod } from "./design-resource-handoff-types.js";
|
|
2
|
+
import type { ExecutionTargetCapabilityV2 } from "./execution-target-capabilities.js";
|
|
2
3
|
export type DesignTargetInterpretationV2 = "exact_target" | "constraint";
|
|
3
4
|
export interface DeliveryDesignVerificationBindingV2 {
|
|
4
5
|
method: DesignResourceVerificationMethod;
|
|
5
6
|
assertion_ref: string;
|
|
7
|
+
evidence_artifacts: Array<{
|
|
8
|
+
condition_key: string;
|
|
9
|
+
path: string;
|
|
10
|
+
observation_path: string;
|
|
11
|
+
}>;
|
|
6
12
|
}
|
|
7
13
|
export interface DeliveryDesignTargetV2 {
|
|
8
14
|
key: string;
|
|
@@ -23,6 +29,7 @@ export interface DeliveryDesignAcceptanceBlockerV2 {
|
|
|
23
29
|
refs: string[];
|
|
24
30
|
source_item_refs: string[];
|
|
25
31
|
verification_methods: DesignResourceVerificationMethod[];
|
|
32
|
+
required_capabilities: ExecutionTargetCapabilityV2[];
|
|
26
33
|
rationale: string;
|
|
27
34
|
}
|
|
28
35
|
export interface DeliverySurfaceBindingV2 {
|
|
@@ -40,6 +40,16 @@ interface CounterfactualExecutionPreviewV1 {
|
|
|
40
40
|
type: "replace_file";
|
|
41
41
|
path: string;
|
|
42
42
|
fixture_path: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: "replace_json_value";
|
|
45
|
+
path: string;
|
|
46
|
+
pointer: string;
|
|
47
|
+
value: unknown;
|
|
48
|
+
} | {
|
|
49
|
+
type: "replace_text";
|
|
50
|
+
path: string;
|
|
51
|
+
match: string;
|
|
52
|
+
replacement: string;
|
|
43
53
|
};
|
|
44
54
|
runner: RunnerPreviewV1;
|
|
45
55
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { WorkspaceManifestV2 } from "./long-task-delivery-types.js";
|
|
2
|
+
export declare function freezeLocalVerifierDependencyClosure(repository: string, roots: Iterable<string>, manifest: WorkspaceManifestV2, runtimeOwnedPatterns?: readonly string[]): Promise<Record<string, string>>;
|
|
3
|
+
export declare function packageScriptVerifierRoots(script: string, packageFile: string, manifest: WorkspaceManifestV2): string[];
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { classifyRepositoryPatternOverlap, matchesRepoPattern, } from "./long-task-paths.js";
|
|
4
|
+
import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
|
|
5
|
+
import { sha256Hex } from "./strict-codec.js";
|
|
6
|
+
const SCRIPT_EXTENSIONS = new Set([
|
|
7
|
+
".js",
|
|
8
|
+
".jsx",
|
|
9
|
+
".mjs",
|
|
10
|
+
".cjs",
|
|
11
|
+
".ts",
|
|
12
|
+
".tsx",
|
|
13
|
+
".mts",
|
|
14
|
+
".cts",
|
|
15
|
+
]);
|
|
16
|
+
const RESOLUTION_EXTENSIONS = [
|
|
17
|
+
"",
|
|
18
|
+
".js",
|
|
19
|
+
".mjs",
|
|
20
|
+
".cjs",
|
|
21
|
+
".jsx",
|
|
22
|
+
".ts",
|
|
23
|
+
".mts",
|
|
24
|
+
".cts",
|
|
25
|
+
".tsx",
|
|
26
|
+
".json",
|
|
27
|
+
];
|
|
28
|
+
export async function freezeLocalVerifierDependencyClosure(repository, roots, manifest, runtimeOwnedPatterns = []) {
|
|
29
|
+
const available = new Set(manifest.files.map((file) => file.path));
|
|
30
|
+
const result = {};
|
|
31
|
+
const queued = [...new Set(roots)].filter((file) => SCRIPT_EXTENSIONS.has(path.posix.extname(file).toLowerCase()));
|
|
32
|
+
const visited = new Set();
|
|
33
|
+
while (queued.length) {
|
|
34
|
+
const relative = queued.shift();
|
|
35
|
+
if (visited.has(relative))
|
|
36
|
+
continue;
|
|
37
|
+
visited.add(relative);
|
|
38
|
+
if (!available.has(relative))
|
|
39
|
+
throw new Error(`verification_dependency_not_found:${relative}`);
|
|
40
|
+
const absolute = await assertProtectedRepositoryFile(repository, path.join(repository, ...relative.split("/")), "verification_dependency");
|
|
41
|
+
const bytes = await readFile(absolute);
|
|
42
|
+
result[relative] = sha256Hex(bytes);
|
|
43
|
+
const source = bytes.toString("utf8");
|
|
44
|
+
if (hasNonLiteralLoader(source))
|
|
45
|
+
throw new Error(`verification_dependency_dynamic_unresolved:${relative}`);
|
|
46
|
+
for (const specifier of staticSpecifiers(source)) {
|
|
47
|
+
if (!isLocalSpecifier(specifier))
|
|
48
|
+
continue;
|
|
49
|
+
const dependency = resolveLocalSpecifier(relative, specifier, available);
|
|
50
|
+
const unresolvedBase = localSpecifierBase(relative, specifier);
|
|
51
|
+
if ((dependency &&
|
|
52
|
+
runtimeOwnedPatterns.some((pattern) => matchesRepoPattern(dependency, pattern))) ||
|
|
53
|
+
(!dependency &&
|
|
54
|
+
unresolvedBase &&
|
|
55
|
+
runtimeOwnedPatterns.some((pattern) => runtimePatternOwns(unresolvedBase, pattern))))
|
|
56
|
+
continue;
|
|
57
|
+
if (!dependency)
|
|
58
|
+
throw new Error(`verification_dependency_not_found:${relative}:${specifier}`);
|
|
59
|
+
if (SCRIPT_EXTENSIONS.has(path.posix.extname(dependency).toLowerCase()))
|
|
60
|
+
queued.push(dependency);
|
|
61
|
+
else {
|
|
62
|
+
const dependencyFile = await assertProtectedRepositoryFile(repository, path.join(repository, ...dependency.split("/")), "verification_dependency");
|
|
63
|
+
result[dependency] = sha256Hex(await readFile(dependencyFile));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return sortRecord(result);
|
|
68
|
+
}
|
|
69
|
+
export function packageScriptVerifierRoots(script, packageFile, manifest) {
|
|
70
|
+
const tokens = staticPackageScriptTokens(script);
|
|
71
|
+
if (!tokens.length || !["node", "node.exe"].includes(tokens[0].toLowerCase()))
|
|
72
|
+
throw new Error("package_script_dependency_closure_unresolved:node_entry_required");
|
|
73
|
+
if (tokens.length < 2 || tokens[1].startsWith("-"))
|
|
74
|
+
throw new Error("package_script_dependency_closure_unresolved:static_entry_required");
|
|
75
|
+
const packageDirectory = path.posix.dirname(packageFile);
|
|
76
|
+
const candidate = path.posix.normalize(path.posix.join(packageDirectory === "." ? "" : packageDirectory, tokens[1]));
|
|
77
|
+
if (candidate === ".." ||
|
|
78
|
+
candidate.startsWith("../") ||
|
|
79
|
+
!manifest.files.some((file) => file.path === candidate) ||
|
|
80
|
+
!SCRIPT_EXTENSIONS.has(path.posix.extname(candidate).toLowerCase()))
|
|
81
|
+
throw new Error(`package_script_dependency_closure_unresolved:entry_not_found:${tokens[1]}`);
|
|
82
|
+
return [candidate];
|
|
83
|
+
}
|
|
84
|
+
function staticPackageScriptTokens(script) {
|
|
85
|
+
const tokens = [];
|
|
86
|
+
let current = "";
|
|
87
|
+
let quote = null;
|
|
88
|
+
for (let index = 0; index < script.length; index += 1) {
|
|
89
|
+
const character = script[index];
|
|
90
|
+
if (!quote && /\s/u.test(character)) {
|
|
91
|
+
if (current) {
|
|
92
|
+
tokens.push(current);
|
|
93
|
+
current = "";
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (character === "'" || character === '"') {
|
|
98
|
+
if (!quote) {
|
|
99
|
+
quote = character;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (quote === character) {
|
|
103
|
+
quote = null;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if ((!quote && /[&|;<>()]/u.test(character)) ||
|
|
108
|
+
character === "`" ||
|
|
109
|
+
character === "$" ||
|
|
110
|
+
character === "%" ||
|
|
111
|
+
character === "!")
|
|
112
|
+
throw new Error("package_script_dependency_closure_unresolved:dynamic_shell_syntax");
|
|
113
|
+
current += character;
|
|
114
|
+
}
|
|
115
|
+
if (quote)
|
|
116
|
+
throw new Error("package_script_dependency_closure_unresolved:unterminated_quote");
|
|
117
|
+
if (current)
|
|
118
|
+
tokens.push(current);
|
|
119
|
+
return tokens;
|
|
120
|
+
}
|
|
121
|
+
function staticSpecifiers(source) {
|
|
122
|
+
const result = [];
|
|
123
|
+
const patterns = [
|
|
124
|
+
/\b(?:import|export)\s+(?:[^"'`]*?\s+from\s+)?["']([^"']+)["']/gu,
|
|
125
|
+
/\bimport\s*\(\s*["']([^"']+)["']\s*\)/gu,
|
|
126
|
+
/\brequire\s*\(\s*["']([^"']+)["']\s*\)/gu,
|
|
127
|
+
/\brequire\.resolve\s*\(\s*["']([^"']+)["']\s*\)/gu,
|
|
128
|
+
/\bmodule\.require\s*\(\s*["']([^"']+)["']\s*\)/gu,
|
|
129
|
+
/\bnew\s+URL\s*\(\s*["']([^"']+)["']\s*,\s*import\.meta\.url\s*\)/gu,
|
|
130
|
+
];
|
|
131
|
+
for (const pattern of patterns)
|
|
132
|
+
for (const match of source.matchAll(pattern))
|
|
133
|
+
result.push(match[1]);
|
|
134
|
+
return [...new Set(result)];
|
|
135
|
+
}
|
|
136
|
+
function hasNonLiteralLoader(source) {
|
|
137
|
+
return (/\b(?:import|require)\s*\(\s*(?!["'])[^)]/u.test(source) ||
|
|
138
|
+
/\brequire\.resolve\s*\(\s*(?!["'])[^)]/u.test(source) ||
|
|
139
|
+
/\bmodule\.require\s*\(\s*(?!["'])[^)]/u.test(source) ||
|
|
140
|
+
/\bnew\s+URL\s*\(\s*(?!["'])[^,]+,\s*import\.meta\.url\s*\)/u.test(source) ||
|
|
141
|
+
/\bcreateRequire\b/u.test(source));
|
|
142
|
+
}
|
|
143
|
+
function isLocalSpecifier(specifier) {
|
|
144
|
+
return (specifier.startsWith("./") ||
|
|
145
|
+
specifier.startsWith("../") ||
|
|
146
|
+
specifier.startsWith("/"));
|
|
147
|
+
}
|
|
148
|
+
function resolveLocalSpecifier(importer, specifier, available) {
|
|
149
|
+
const base = localSpecifierBase(importer, specifier);
|
|
150
|
+
if (!base)
|
|
151
|
+
return null;
|
|
152
|
+
if (base.startsWith("../") || base === "..")
|
|
153
|
+
return null;
|
|
154
|
+
for (const extension of RESOLUTION_EXTENSIONS) {
|
|
155
|
+
const candidate = `${base}${extension}`;
|
|
156
|
+
if (available.has(candidate))
|
|
157
|
+
return candidate;
|
|
158
|
+
}
|
|
159
|
+
for (const extension of RESOLUTION_EXTENSIONS.slice(1)) {
|
|
160
|
+
const candidate = `${base}/index${extension}`;
|
|
161
|
+
if (available.has(candidate))
|
|
162
|
+
return candidate;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
function localSpecifierBase(importer, specifier) {
|
|
167
|
+
const base = specifier.startsWith("/")
|
|
168
|
+
? specifier.slice(1)
|
|
169
|
+
: path.posix.normalize(path.posix.join(path.posix.dirname(importer), specifier));
|
|
170
|
+
return base === ".." || base.startsWith("../") ? null : base;
|
|
171
|
+
}
|
|
172
|
+
function runtimePatternOwns(base, pattern) {
|
|
173
|
+
return (matchesRepoPattern(base, pattern) ||
|
|
174
|
+
classifyRepositoryPatternOverlap(`${base}/**`, pattern).status ===
|
|
175
|
+
"proven_overlap");
|
|
176
|
+
}
|
|
177
|
+
function sortRecord(value) {
|
|
178
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
|
|
179
|
+
}
|
|
@@ -109,8 +109,17 @@ export async function runDeliveryChecks(compiled, snapshot, checks, includeCount
|
|
|
109
109
|
}
|
|
110
110
|
const counterfactualFindings = [];
|
|
111
111
|
if (includeCounterfactuals) {
|
|
112
|
+
// Counterfactual sensitivity is a closure condition for an otherwise
|
|
113
|
+
// passing proof. A failed or externally blocked owning Check already
|
|
114
|
+
// blocks acceptance; rerunning its oracle in a mutated sandbox can only
|
|
115
|
+
// obscure that primary status (for example, by turning a typed external
|
|
116
|
+
// blocker into an integrity failure).
|
|
117
|
+
const passingCheckIds = new Set(mainCheckResults
|
|
118
|
+
.filter((result) => result.status === "passed")
|
|
119
|
+
.map((result) => result.internal_id));
|
|
112
120
|
const selectedGlobalCheckKeys = new Set(checks
|
|
113
|
-
.filter((check) => check.outcome_key === null
|
|
121
|
+
.filter((check) => check.outcome_key === null &&
|
|
122
|
+
passingCheckIds.has(check.internal_id))
|
|
114
123
|
.map((check) => check.key));
|
|
115
124
|
if (selectedGlobalCheckKeys.size)
|
|
116
125
|
counterfactualFindings.push(...(await evaluateGlobalCounterfactuals(compiled, snapshot.root, selectedGlobalCheckKeys, snapshot.manifest)));
|
|
@@ -119,7 +128,8 @@ export async function runDeliveryChecks(compiled, snapshot, checks, includeCount
|
|
|
119
128
|
.filter((key) => Boolean(key)));
|
|
120
129
|
for (const outcome of compiled.outcomes.filter((item) => outcomeKeys.has(item.key))) {
|
|
121
130
|
const selectedKeys = new Set(checks
|
|
122
|
-
.filter((check) => check.outcome_key === outcome.key
|
|
131
|
+
.filter((check) => check.outcome_key === outcome.key &&
|
|
132
|
+
passingCheckIds.has(check.internal_id))
|
|
123
133
|
.map((check) => check.key));
|
|
124
134
|
counterfactualFindings.push(...(await evaluateOutcomeCounterfactuals({
|
|
125
135
|
...outcome,
|
|
@@ -4,7 +4,11 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
6
6
|
export async function repositoryRoot(start) {
|
|
7
|
-
|
|
7
|
+
const resolved = path.resolve(start);
|
|
8
|
+
const gitEntry = await stat(path.join(resolved, ".git")).catch(() => null);
|
|
9
|
+
if (gitEntry?.isDirectory() || gitEntry?.isFile())
|
|
10
|
+
return resolved;
|
|
11
|
+
return path.resolve(await gitOutput(resolved, ["rev-parse", "--show-toplevel"]));
|
|
8
12
|
}
|
|
9
13
|
export async function gitCommonDir(root) {
|
|
10
14
|
return path.resolve(await gitOutput(root, [
|