project-tiny-context-harness 0.8.12 → 0.8.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -19
- package/assets/README.md +81 -40
- package/assets/README.zh-CN.md +44 -22
- package/assets/agents/AGENTS_CORE.md +8 -4
- package/assets/skills/context_development_engineer/SKILL.md +9 -1
- package/assets/skills/design-resource-authoring/SKILL.md +7 -5
- package/assets/skills/design-resource-authoring/references/downstream-handoff.md +2 -2
- package/assets/skills/design-resource-authoring/references/open-design-provider.md +22 -2
- package/assets/skills/design-resource-authoring/references/recovery-and-writeback.md +199 -0
- package/assets/skills/design-resource-authoring/references/resource-selection.md +2 -2
- package/assets/skills/long-task-workflow/SKILL.md +6 -6
- package/assets/skills/long-task-workflow/references/contract-authoring.md +27 -22
- package/assets/skills/long-task-workflow/references/evidence-design.md +49 -32
- package/assets/skills/long-task-workflow/references/source-authoring.md +2 -2
- package/dist/commands/design-resource-recovery.d.ts +1 -0
- package/dist/commands/design-resource-recovery.js +152 -0
- package/dist/commands/design-resource.js +20 -1
- package/dist/commands/index.js +1 -1
- package/dist/lib/design-resource-reconciliation-codec.d.ts +2 -0
- package/dist/lib/design-resource-reconciliation-codec.js +174 -0
- package/dist/lib/design-resource-reconciliation-types.d.ts +62 -0
- package/dist/lib/design-resource-reconciliation-types.js +1 -0
- package/dist/lib/design-resource-reconciliation.d.ts +3 -0
- package/dist/lib/design-resource-reconciliation.js +224 -0
- package/dist/lib/design-resource-recovery-authority-policy.d.ts +8 -0
- package/dist/lib/design-resource-recovery-authority-policy.js +123 -0
- package/dist/lib/design-resource-recovery-catalog-resources.d.ts +6 -0
- package/dist/lib/design-resource-recovery-catalog-resources.js +121 -0
- package/dist/lib/design-resource-recovery-catalog-shape.d.ts +3 -0
- package/dist/lib/design-resource-recovery-catalog-shape.js +104 -0
- package/dist/lib/design-resource-recovery-catalog.d.ts +9 -0
- package/dist/lib/design-resource-recovery-catalog.js +103 -0
- package/dist/lib/design-resource-recovery-cleanup.d.ts +2 -0
- package/dist/lib/design-resource-recovery-cleanup.js +11 -0
- package/dist/lib/design-resource-recovery-codec-primitives.d.ts +19 -0
- package/dist/lib/design-resource-recovery-codec-primitives.js +92 -0
- package/dist/lib/design-resource-recovery-codec.d.ts +4 -0
- package/dist/lib/design-resource-recovery-codec.js +93 -0
- package/dist/lib/design-resource-recovery-current.d.ts +9 -0
- package/dist/lib/design-resource-recovery-current.js +41 -0
- package/dist/lib/design-resource-recovery-delta-policy.d.ts +5 -0
- package/dist/lib/design-resource-recovery-delta-policy.js +108 -0
- package/dist/lib/design-resource-recovery-files.d.ts +37 -0
- package/dist/lib/design-resource-recovery-files.js +204 -0
- package/dist/lib/design-resource-recovery-final-disposition-shape.d.ts +2 -0
- package/dist/lib/design-resource-recovery-final-disposition-shape.js +47 -0
- package/dist/lib/design-resource-recovery-patch-types.d.ts +54 -0
- package/dist/lib/design-resource-recovery-patch-types.js +1 -0
- package/dist/lib/design-resource-recovery-replay.d.ts +7 -0
- package/dist/lib/design-resource-recovery-replay.js +112 -0
- package/dist/lib/design-resource-recovery-repository-bindings.d.ts +9 -0
- package/dist/lib/design-resource-recovery-repository-bindings.js +49 -0
- package/dist/lib/design-resource-recovery-schema.d.ts +5 -0
- package/dist/lib/design-resource-recovery-schema.js +5 -0
- package/dist/lib/design-resource-recovery-shape.d.ts +10 -0
- package/dist/lib/design-resource-recovery-shape.js +184 -0
- package/dist/lib/design-resource-recovery-source-authority.d.ts +5 -0
- package/dist/lib/design-resource-recovery-source-authority.js +219 -0
- package/dist/lib/design-resource-recovery-text.d.ts +17 -0
- package/dist/lib/design-resource-recovery-text.js +186 -0
- package/dist/lib/design-resource-recovery-types.d.ts +197 -0
- package/dist/lib/design-resource-recovery-types.js +1 -0
- package/dist/lib/design-resource-recovery-writeback-policy.d.ts +5 -0
- package/dist/lib/design-resource-recovery-writeback-policy.js +326 -0
- package/dist/lib/design-resource-recovery-writeback-shape.d.ts +6 -0
- package/dist/lib/design-resource-recovery-writeback-shape.js +155 -0
- package/dist/lib/design-resource-recovery.d.ts +44 -0
- package/dist/lib/design-resource-recovery.js +216 -0
- package/dist/lib/long-task-acceptance-shape.js +8 -2
- package/dist/lib/long-task-activation-validation.js +13 -3
- package/dist/lib/long-task-admitted-observation-records.d.ts +25 -0
- package/dist/lib/long-task-admitted-observation-records.js +40 -0
- package/dist/lib/long-task-admitted-observation.d.ts +93 -0
- package/dist/lib/long-task-admitted-observation.js +201 -0
- package/dist/lib/long-task-artifacts.d.ts +1 -0
- package/dist/lib/long-task-artifacts.js +1 -0
- package/dist/lib/long-task-authority-material-diff.js +1 -0
- package/dist/lib/long-task-authority-policy.d.ts +2 -0
- package/dist/lib/long-task-authority-policy.js +2 -0
- package/dist/lib/long-task-authority-revision-details.js +3 -1
- package/dist/lib/long-task-authority.js +2 -1
- package/dist/lib/long-task-check-execution-policy.js +5 -0
- package/dist/lib/long-task-check-runner.d.ts +2 -2
- package/dist/lib/long-task-check-runner.js +440 -23
- package/dist/lib/long-task-claims.d.ts +18 -0
- package/dist/lib/long-task-claims.js +35 -0
- package/dist/lib/long-task-compact-structure-targets.js +1 -0
- package/dist/lib/long-task-conformance-policy.js +3 -1
- package/dist/lib/long-task-counterfactual-claim-policy.d.ts +3 -4
- package/dist/lib/long-task-counterfactual-claim-policy.js +11 -3
- package/dist/lib/long-task-counterfactual-sandbox.js +1 -0
- package/dist/lib/long-task-counterfactual-types.d.ts +2 -0
- package/dist/lib/long-task-delivery-compiler.js +2 -0
- package/dist/lib/long-task-evidence-capability-policy.d.ts +3 -2
- package/dist/lib/long-task-evidence-capability-policy.js +114 -8
- package/dist/lib/long-task-evidence-capability-runtime.d.ts +5 -2
- package/dist/lib/long-task-evidence-capability-runtime.js +149 -5
- package/dist/lib/long-task-evidence-sensitivity-policy.d.ts +9 -0
- package/dist/lib/long-task-evidence-sensitivity-policy.js +31 -1
- package/dist/lib/long-task-evidence-v2.d.ts +4 -4
- package/dist/lib/long-task-evidence-v2.js +321 -43
- package/dist/lib/long-task-exact-comparison.d.ts +16 -0
- package/dist/lib/long-task-exact-comparison.js +28 -0
- package/dist/lib/long-task-execution-observation.d.ts +24 -0
- package/dist/lib/long-task-execution-observation.js +332 -0
- package/dist/lib/long-task-explain-acceptance-link.d.ts +4 -0
- package/dist/lib/long-task-explain-acceptance-link.js +4 -0
- package/dist/lib/long-task-explain-claim-links.d.ts +2 -0
- package/dist/lib/long-task-explain-claim-links.js +2 -0
- package/dist/lib/long-task-explain-source-links.d.ts +6 -0
- package/dist/lib/long-task-final-v2.js +7 -4
- package/dist/lib/long-task-json-pointer-observation.d.ts +59 -0
- package/dist/lib/long-task-json-pointer-observation.js +184 -0
- package/dist/lib/long-task-observation-artifact.d.ts +8 -0
- package/dist/lib/long-task-observation-artifact.js +58 -0
- package/dist/lib/long-task-observation-authority.d.ts +17 -0
- package/dist/lib/long-task-observation-authority.js +348 -0
- package/dist/lib/long-task-paths.js +1 -1
- package/dist/lib/long-task-process-observation.d.ts +6 -0
- package/dist/lib/long-task-process-observation.js +117 -0
- package/dist/lib/long-task-process-runtime-closure.d.ts +13 -0
- package/dist/lib/long-task-process-runtime-closure.js +175 -0
- package/dist/lib/long-task-progress.js +2 -0
- package/dist/lib/long-task-protected-files.d.ts +1 -1
- package/dist/lib/long-task-protected-files.js +1 -30
- package/dist/lib/long-task-risk.js +8 -3
- package/dist/lib/long-task-root-shape.js +10 -1
- package/dist/lib/long-task-runner-environment.d.ts +1 -1
- package/dist/lib/long-task-runner-environment.js +4 -4
- package/dist/lib/long-task-runner-freeze.d.ts +4 -2
- package/dist/lib/long-task-runner-freeze.js +128 -28
- package/dist/lib/long-task-runtime-types.d.ts +111 -0
- package/dist/lib/long-task-scoped-binding.d.ts +13 -0
- package/dist/lib/long-task-scoped-binding.js +11 -0
- package/dist/lib/long-task-semantic-contract-types.d.ts +1 -0
- package/dist/lib/long-task-semantic-fact-contract-closure.js +3 -3
- package/dist/lib/long-task-semantic-fact-evidence.d.ts +3 -3
- package/dist/lib/long-task-semantic-fact-evidence.js +75 -58
- package/dist/lib/long-task-source-claim-validation.js +4 -1
- package/dist/lib/long-task-source-target-continuity.d.ts +2 -2
- package/dist/lib/long-task-source-target-continuity.js +35 -2
- package/dist/lib/long-task-source-target-index.d.ts +3 -0
- package/dist/lib/long-task-source-target-index.js +19 -0
- package/dist/lib/long-task-stage-policy.js +10 -4
- package/dist/lib/long-task-static-observation-freeze.d.ts +83 -0
- package/dist/lib/long-task-static-observation-freeze.js +428 -0
- package/dist/lib/long-task-status-projection.js +5 -1
- package/dist/lib/long-task-target-policy.d.ts +1 -1
- package/dist/lib/long-task-target-policy.js +3 -0
- package/dist/lib/long-task-verifier-dependency-closure.js +7 -3
- package/dist/lib/long-task-verifier-v2.js +70 -9
- package/dist/lib/long-task-workspace.d.ts +1 -1
- package/dist/lib/long-task-workspace.js +1 -12
- package/dist/lib/repository-path-safety.d.ts +14 -0
- package/dist/lib/repository-path-safety.js +126 -0
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +53 -8
- package/migrations/README.md +77 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { outcomeResultExternallyBlocked } from "./long-task-claims.js";
|
|
1
2
|
import { progressRecordFresh } from "./long-task-progress.js";
|
|
2
3
|
export function projectDeliveryStatus(input) {
|
|
3
4
|
const outcomes = projectOutcomes(input);
|
|
@@ -46,7 +47,8 @@ function projectOutcomes(input) {
|
|
|
46
47
|
});
|
|
47
48
|
outcomes[outcome.key] = states.includes("progress_failing")
|
|
48
49
|
? "progress_failing"
|
|
49
|
-
:
|
|
50
|
+
: outcomeResultExternallyBlocked(input.compiled, outcome.key) ||
|
|
51
|
+
states.includes("blocked_external")
|
|
50
52
|
? "blocked_external"
|
|
51
53
|
: states.includes("progress_stale")
|
|
52
54
|
? "progress_stale"
|
|
@@ -99,7 +101,9 @@ function projectFindings(input) {
|
|
|
99
101
|
function readyOutcomes(compiled, outcomes, stages) {
|
|
100
102
|
return compiled.outcomes
|
|
101
103
|
.filter((outcome) => stages[outcome.stage] !== "locked" &&
|
|
104
|
+
stages[outcome.stage] !== "blocked_external" &&
|
|
102
105
|
stages[outcome.stage] !== "progress_passing" &&
|
|
106
|
+
outcomes[outcome.key] !== "blocked_external" &&
|
|
103
107
|
outcomes[outcome.key] !== "progress_passing" &&
|
|
104
108
|
outcome.depends_on.every((dependency) => outcomes[dependency] === "progress_passing"))
|
|
105
109
|
.map((outcome) => outcome.key);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CompiledClaimsV2 } from "./long-task-claims.js";
|
|
2
2
|
import type { DeliveryContractV2 } from "./long-task-delivery-types.js";
|
|
3
3
|
type Reporter = (message: string) => void;
|
|
4
4
|
export declare function validateExecutionTargets(contract: DeliveryContractV2, report?: Reporter): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { outcomeResultExternallyBlocked, } from "./long-task-claims.js";
|
|
1
2
|
const FAMILY_CAPABILITY = {
|
|
2
3
|
browser: "browser-runtime",
|
|
3
4
|
native: "native-runtime",
|
|
@@ -94,6 +95,8 @@ function validateCriticalPathTargets(contract, requiredTargetRefs, report) {
|
|
|
94
95
|
const outcome = contract.outcomes.find((candidate) => candidate.key === outcomeKey);
|
|
95
96
|
if (!outcome)
|
|
96
97
|
continue;
|
|
98
|
+
if (outcomeResultExternallyBlocked(contract, outcome.key))
|
|
99
|
+
continue;
|
|
97
100
|
for (const targetRef of requiredTargetRefs)
|
|
98
101
|
if (!outcome.acceptance.checks.some((check) => provesSuccess(check, targetRef)))
|
|
99
102
|
issue(report, "critical_path_required_target_proof_missing", `${outcome.key}:${targetRef}`);
|
|
@@ -167,11 +167,15 @@ function localSpecifierBase(importer, specifier) {
|
|
|
167
167
|
const base = specifier.startsWith("/")
|
|
168
168
|
? specifier.slice(1)
|
|
169
169
|
: path.posix.normalize(path.posix.join(path.posix.dirname(importer), specifier));
|
|
170
|
-
|
|
170
|
+
const canonicalBase = base.replace(/\/+$/u, "") || ".";
|
|
171
|
+
return canonicalBase === ".." || canonicalBase.startsWith("../")
|
|
172
|
+
? null
|
|
173
|
+
: canonicalBase;
|
|
171
174
|
}
|
|
172
175
|
function runtimePatternOwns(base, pattern) {
|
|
173
|
-
|
|
174
|
-
|
|
176
|
+
const directoryPattern = base === "." ? "**" : `${base}/**`;
|
|
177
|
+
return ((base !== "." && matchesRepoPattern(base, pattern)) ||
|
|
178
|
+
classifyRepositoryPatternOverlap(directoryPattern, pattern).status ===
|
|
175
179
|
"proven_overlap");
|
|
176
180
|
}
|
|
177
181
|
function sortRecord(value) {
|
|
@@ -2,6 +2,7 @@ import { deliveryCompileFreshness } from "./long-task-freshness.js";
|
|
|
2
2
|
import { assertVerifierAuthorityCurrent } from "./long-task-freshness.js";
|
|
3
3
|
import { enrichCheckResultFindings, enrichFinding, } from "./long-task-finding-context.js";
|
|
4
4
|
import { executeCheckRunner } from "./long-task-check-runner.js";
|
|
5
|
+
import { prepareExecutionObservationUniverse } from "./long-task-execution-observation.js";
|
|
5
6
|
import { evaluateCheckEvidence, evaluateGlobalCounterfactuals, evaluateOutcomeCounterfactuals, } from "./long-task-evidence-v2.js";
|
|
6
7
|
import { matchesRepoPattern } from "./long-task-paths.js";
|
|
7
8
|
import { activeAuthorityIdentityMatches, loadActiveLongTaskAuthority, writeProgressRecord, } from "./long-task-state.js";
|
|
@@ -85,6 +86,12 @@ async function resolved(workdir) {
|
|
|
85
86
|
return (await import("node:path")).default.resolve(workdir);
|
|
86
87
|
}
|
|
87
88
|
export async function runDeliveryChecks(compiled, snapshot, checks, includeCounterfactuals, finalGate = false) {
|
|
89
|
+
const observationAuthorityPaths = [
|
|
90
|
+
compiled.contract_file,
|
|
91
|
+
...Object.keys(compiled.contract_files),
|
|
92
|
+
...Object.keys(compiled.source_hashes),
|
|
93
|
+
...compiled.context_snapshot.files,
|
|
94
|
+
];
|
|
88
95
|
const findings = await preRunFindings(compiled, snapshot.manifest);
|
|
89
96
|
if (finalGate)
|
|
90
97
|
findings.push(...finalPathFindings(compiled, snapshot.manifest));
|
|
@@ -95,17 +102,46 @@ export async function runDeliveryChecks(compiled, snapshot, checks, includeCount
|
|
|
95
102
|
findings: findings.map((finding) => enrichFinding(compiled, finding)),
|
|
96
103
|
};
|
|
97
104
|
const rawExecutions = new Map();
|
|
105
|
+
const completeChecks = allCompiledChecks(compiled);
|
|
106
|
+
const selectedExecutionGroups = rawExecutionGroups(checks);
|
|
107
|
+
const completeExecutionGroups = selectedExecutionGroups.map((group) => completeChecks.filter((check) => check.raw_execution_identity === group[0].raw_execution_identity));
|
|
108
|
+
const preparedExecutionGroups = await prepareExecutionObservationUniverse({
|
|
109
|
+
groups: completeExecutionGroups,
|
|
110
|
+
snapshot_root: snapshot.root,
|
|
111
|
+
workspace_manifest: snapshot.manifest,
|
|
112
|
+
protected_authority_paths: observationAuthorityPaths,
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
const pending = [];
|
|
116
|
+
for (const [index, group] of selectedExecutionGroups.entries()) {
|
|
117
|
+
const prepared = preparedExecutionGroups[index];
|
|
118
|
+
if (!prepared)
|
|
119
|
+
throw new Error("raw_execution_group_prepare_missing");
|
|
120
|
+
pending.push({
|
|
121
|
+
raw_execution_identity: group[0].raw_execution_identity,
|
|
122
|
+
raw: await executeCheckRunner(group[0], prepared.execution_root, prepared.runner_context),
|
|
123
|
+
prepared,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const finalized = await Promise.all(pending.map(async (entry) => ({
|
|
127
|
+
raw_execution_identity: entry.raw_execution_identity,
|
|
128
|
+
raw: await entry.prepared.finalize(entry.raw),
|
|
129
|
+
})));
|
|
130
|
+
for (const entry of finalized)
|
|
131
|
+
rawExecutions.set(entry.raw_execution_identity, entry.raw);
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
await Promise.all(preparedExecutionGroups.map((prepared) => prepared.dispose()));
|
|
135
|
+
}
|
|
98
136
|
const mainCheckResults = [];
|
|
99
137
|
for (const check of checks) {
|
|
100
|
-
|
|
101
|
-
if (!raw)
|
|
102
|
-
|
|
103
|
-
rawExecutions.set(check.raw_execution_identity, raw);
|
|
104
|
-
}
|
|
138
|
+
const raw = rawExecutions.get(check.raw_execution_identity);
|
|
139
|
+
if (!raw)
|
|
140
|
+
throw new Error("raw_execution_group_result_missing");
|
|
105
141
|
const outcome = check.outcome_key
|
|
106
142
|
? compiled.outcomes.find((item) => item.key === check.outcome_key)
|
|
107
143
|
: undefined;
|
|
108
|
-
mainCheckResults.push(await evaluateCheckEvidence(check, raw, snapshot.root, outcome));
|
|
144
|
+
mainCheckResults.push(await evaluateCheckEvidence(check, raw, snapshot.root, outcome, observationAuthorityPaths));
|
|
109
145
|
}
|
|
110
146
|
const counterfactualFindings = [];
|
|
111
147
|
if (includeCounterfactuals) {
|
|
@@ -122,7 +158,7 @@ export async function runDeliveryChecks(compiled, snapshot, checks, includeCount
|
|
|
122
158
|
passingCheckIds.has(check.internal_id))
|
|
123
159
|
.map((check) => check.key));
|
|
124
160
|
if (selectedGlobalCheckKeys.size)
|
|
125
|
-
counterfactualFindings.push(...(await evaluateGlobalCounterfactuals(compiled, snapshot.root, selectedGlobalCheckKeys, snapshot.manifest)));
|
|
161
|
+
counterfactualFindings.push(...(await evaluateGlobalCounterfactuals(compiled, snapshot.root, selectedGlobalCheckKeys, snapshot.manifest, mainCheckResults, rawExecutions, completeChecks)));
|
|
126
162
|
const outcomeKeys = new Set(checks
|
|
127
163
|
.map((check) => check.outcome_key)
|
|
128
164
|
.filter((key) => Boolean(key)));
|
|
@@ -137,7 +173,7 @@ export async function runDeliveryChecks(compiled, snapshot, checks, includeCount
|
|
|
137
173
|
...outcome.acceptance,
|
|
138
174
|
counterfactual_controls: outcome.acceptance.counterfactual_controls.filter((control) => selectedKeys.has(control.check_key)),
|
|
139
175
|
},
|
|
140
|
-
}, snapshot.root, snapshot.manifest,
|
|
176
|
+
}, snapshot.root, snapshot.manifest, observationAuthorityPaths, mainCheckResults, rawExecutions, completeChecks)));
|
|
141
177
|
}
|
|
142
178
|
}
|
|
143
179
|
const unassignedCounterfactuals = counterfactualFindings.filter((finding) => !mainCheckResults.some((result) => findingBelongsToCheck(finding, result)));
|
|
@@ -154,6 +190,17 @@ export async function runDeliveryChecks(compiled, snapshot, checks, includeCount
|
|
|
154
190
|
findings: findings.map((finding) => enrichFinding(compiled, finding)),
|
|
155
191
|
};
|
|
156
192
|
}
|
|
193
|
+
function rawExecutionGroups(checks) {
|
|
194
|
+
const groups = new Map();
|
|
195
|
+
for (const check of checks) {
|
|
196
|
+
const group = groups.get(check.raw_execution_identity);
|
|
197
|
+
if (group)
|
|
198
|
+
group.push(check);
|
|
199
|
+
else
|
|
200
|
+
groups.set(check.raw_execution_identity, [check]);
|
|
201
|
+
}
|
|
202
|
+
return [...groups.values()];
|
|
203
|
+
}
|
|
157
204
|
function applyCounterfactualFindings(checkResults, findings, unassignedFinding) {
|
|
158
205
|
return checkResults.map((result, index) => {
|
|
159
206
|
const projected = findings.filter((finding) => findingBelongsToCheck(finding, result));
|
|
@@ -200,7 +247,9 @@ function finalPathFindings(compiled, manifest) {
|
|
|
200
247
|
for (const outcome of compiled.outcomes)
|
|
201
248
|
for (const binding of outcome.technical.bindings)
|
|
202
249
|
if (binding.existence === "planned" &&
|
|
203
|
-
!plannedBindingRequiredPatterns(binding).every((pattern) => manifest.files.some((file) => matchesRepoPattern(file.path, pattern)))
|
|
250
|
+
!plannedBindingRequiredPatterns(binding).every((pattern) => manifest.files.some((file) => matchesRepoPattern(file.path, pattern))) &&
|
|
251
|
+
!outcome.acceptance.checks.some((check) => (check.observation_authorities ?? []).some((authority) => authority.authority === "package_static_json_exact" &&
|
|
252
|
+
authority.carrier_refs.some((carrier) => carrier.binding_ref === `${outcome.key}.${binding.key}`))))
|
|
204
253
|
findings.push({
|
|
205
254
|
code: "binding_missing",
|
|
206
255
|
outcome_key: outcome.key,
|
|
@@ -212,6 +261,18 @@ function finalPathFindings(compiled, manifest) {
|
|
|
212
261
|
expected: binding.carrier_paths,
|
|
213
262
|
next_action: "Implement the declared binding and rerun verification.",
|
|
214
263
|
});
|
|
264
|
+
for (const check of allCompiledChecks(compiled))
|
|
265
|
+
for (const runtimePath of check.process_runtime_closure
|
|
266
|
+
?.allowed_runtime_files ?? [])
|
|
267
|
+
if (!manifest.files.some((file) => file.path === runtimePath))
|
|
268
|
+
findings.push({
|
|
269
|
+
code: "process_runtime_input_missing",
|
|
270
|
+
outcome_key: check.outcome_key,
|
|
271
|
+
check_key: check.key,
|
|
272
|
+
message: `Declared process runtime input did not exist in the Final Gate candidate: ${runtimePath}`,
|
|
273
|
+
expected: runtimePath,
|
|
274
|
+
next_action: "Materialize the Source-backed product root and every declared production runtime input, then rerun Final Gate.",
|
|
275
|
+
});
|
|
215
276
|
return findings;
|
|
216
277
|
}
|
|
217
278
|
function plannedBindingRequiredPatterns(binding) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { resolveInsideRepository } from "./repository-path-safety.js";
|
|
1
2
|
import type { WorkspaceFingerprintV2, WorkspaceManifestV2 } from "./long-task-delivery-types.js";
|
|
2
3
|
export interface WorkspaceSnapshotV2 {
|
|
3
4
|
root: string;
|
|
@@ -22,5 +23,4 @@ export declare function gitPath(root: string, pathSpec: string): Promise<string>
|
|
|
22
23
|
export declare function gitConfigGet(root: string, name: string): Promise<string | null>;
|
|
23
24
|
export declare function gitConfigSet(root: string, name: string, value: string): Promise<void>;
|
|
24
25
|
export declare function gitConfigUnset(root: string, name: string): Promise<void>;
|
|
25
|
-
export declare function resolveInsideRepository(rootInput: string, relativeInput: string, label: string): string;
|
|
26
26
|
export declare function repoRelative(rootInput: string, fileInput: string): string;
|
|
@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { copyFile, mkdir, mkdtemp, readFile, readdir, rm, stat, symlink, } from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
export { resolveInsideRepository } from "./repository-path-safety.js";
|
|
5
6
|
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
6
7
|
export async function repositoryRoot(start) {
|
|
7
8
|
const resolved = path.resolve(start);
|
|
@@ -247,18 +248,6 @@ export async function gitConfigUnset(root, name) {
|
|
|
247
248
|
throw error;
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
|
-
export function resolveInsideRepository(rootInput, relativeInput, label) {
|
|
251
|
-
if (!relativeInput || path.isAbsolute(relativeInput))
|
|
252
|
-
throw new Error(`unsafe_path:${label}:${relativeInput}`);
|
|
253
|
-
const normalized = relativeInput.replace(/\\/gu, "/");
|
|
254
|
-
if (normalized.split("/").includes(".."))
|
|
255
|
-
throw new Error(`unsafe_path:${label}:${relativeInput}`);
|
|
256
|
-
const root = path.resolve(rootInput);
|
|
257
|
-
const resolved = path.resolve(root, ...normalized.split("/"));
|
|
258
|
-
if (resolved !== root && !resolved.startsWith(`${root}${path.sep}`))
|
|
259
|
-
throw new Error(`unsafe_path:${label}:${relativeInput}`);
|
|
260
|
-
return resolved;
|
|
261
|
-
}
|
|
262
251
|
export function repoRelative(rootInput, fileInput) {
|
|
263
252
|
const value = path
|
|
264
253
|
.relative(path.resolve(rootInput), path.resolve(fileInput))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Stats } from "node:fs";
|
|
2
|
+
export interface SafeRepositoryPath {
|
|
3
|
+
repository: string;
|
|
4
|
+
absolute: string;
|
|
5
|
+
relative: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveInsideRepository(rootInput: string, relativeInput: string, label: string): string;
|
|
8
|
+
export declare function assertProtectedRepositoryFile(repositoryInput: string, fileInput: string, label: string): Promise<string>;
|
|
9
|
+
export declare function assertSafeRepositoryFilePath(repositoryInput: string, relativeInput: string, label: string, options?: {
|
|
10
|
+
destinationMayBeAbsent?: boolean;
|
|
11
|
+
}): Promise<SafeRepositoryPath & {
|
|
12
|
+
status: Stats | null;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function ensureSafeRepositoryDirectory(repositoryInput: string, relativeInput: string, label: string): Promise<SafeRepositoryPath>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { lstat, mkdir, realpath } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function resolveInsideRepository(rootInput, relativeInput, label) {
|
|
4
|
+
if (!relativeInput || path.isAbsolute(relativeInput))
|
|
5
|
+
throw new Error(`unsafe_path:${label}:${relativeInput}`);
|
|
6
|
+
const normalized = relativeInput.replace(/\\/gu, "/");
|
|
7
|
+
if (normalized.split("/").some((segment) => segment === "..") ||
|
|
8
|
+
normalized.startsWith("//") ||
|
|
9
|
+
/^[A-Za-z]:/u.test(normalized))
|
|
10
|
+
throw new Error(`unsafe_path:${label}:${relativeInput}`);
|
|
11
|
+
const root = path.resolve(rootInput);
|
|
12
|
+
const resolved = path.resolve(root, ...normalized.split("/"));
|
|
13
|
+
if (!inside(root, resolved))
|
|
14
|
+
throw new Error(`unsafe_path:${label}:${relativeInput}`);
|
|
15
|
+
return resolved;
|
|
16
|
+
}
|
|
17
|
+
export async function assertProtectedRepositoryFile(repositoryInput, fileInput, label) {
|
|
18
|
+
const repositoryPath = path.resolve(repositoryInput);
|
|
19
|
+
const repository = await realpath(repositoryPath);
|
|
20
|
+
const candidate = path.resolve(fileInput);
|
|
21
|
+
assertInside(repositoryPath, candidate, label, fileInput);
|
|
22
|
+
await assertExistingParents(repositoryPath, repository, candidate, label);
|
|
23
|
+
const info = await lstatOrNull(candidate);
|
|
24
|
+
if (!info)
|
|
25
|
+
throw new Error(`protected_input_not_found:${label}:${display(fileInput)}`);
|
|
26
|
+
assertRegularNoFollow(info, label, fileInput);
|
|
27
|
+
const resolved = await realpath(candidate);
|
|
28
|
+
assertInside(repository, resolved, label, fileInput);
|
|
29
|
+
return resolved;
|
|
30
|
+
}
|
|
31
|
+
export async function assertSafeRepositoryFilePath(repositoryInput, relativeInput, label, options = {}) {
|
|
32
|
+
const repositoryPath = path.resolve(repositoryInput);
|
|
33
|
+
const repository = await realpath(repositoryPath);
|
|
34
|
+
const absolute = resolveInsideRepository(repositoryPath, relativeInput, label);
|
|
35
|
+
await assertExistingParents(repositoryPath, repository, absolute, label);
|
|
36
|
+
const status = await lstatOrNull(absolute);
|
|
37
|
+
if (!status && !options.destinationMayBeAbsent)
|
|
38
|
+
throw new Error(`protected_input_not_found:${label}:${display(relativeInput)}`);
|
|
39
|
+
if (status) {
|
|
40
|
+
assertRegularNoFollow(status, label, relativeInput);
|
|
41
|
+
assertInside(repository, await realpath(absolute), label, relativeInput);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
repository,
|
|
45
|
+
absolute,
|
|
46
|
+
relative: repositoryRelative(repositoryPath, absolute, label),
|
|
47
|
+
status,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export async function ensureSafeRepositoryDirectory(repositoryInput, relativeInput, label) {
|
|
51
|
+
const repositoryPath = path.resolve(repositoryInput);
|
|
52
|
+
const repository = await realpath(repositoryPath);
|
|
53
|
+
const absolute = resolveInsideRepository(repositoryPath, relativeInput, label);
|
|
54
|
+
const relative = repositoryRelative(repositoryPath, absolute, label);
|
|
55
|
+
let cursor = repositoryPath;
|
|
56
|
+
for (const segment of relative.split(path.sep)) {
|
|
57
|
+
cursor = path.join(cursor, segment);
|
|
58
|
+
let status = await lstatOrNull(cursor);
|
|
59
|
+
if (!status) {
|
|
60
|
+
await mkdir(cursor).catch((error) => {
|
|
61
|
+
if (error.code !== "EEXIST")
|
|
62
|
+
throw error;
|
|
63
|
+
});
|
|
64
|
+
status = await lstatOrNull(cursor);
|
|
65
|
+
}
|
|
66
|
+
if (!status || status.isSymbolicLink() || !status.isDirectory())
|
|
67
|
+
throw new Error(`unsafe_repository_directory:${label}:${display(relativeInput)}`);
|
|
68
|
+
assertInside(repository, await realpath(cursor), label, relativeInput);
|
|
69
|
+
}
|
|
70
|
+
return { repository, absolute, relative: relative.replace(/\\/gu, "/") };
|
|
71
|
+
}
|
|
72
|
+
async function assertExistingParents(lexicalRepository, canonicalRepository, candidate, label) {
|
|
73
|
+
const relative = repositoryRelative(lexicalRepository, path.dirname(candidate), label);
|
|
74
|
+
if (!relative)
|
|
75
|
+
return;
|
|
76
|
+
let cursor = lexicalRepository;
|
|
77
|
+
for (const segment of relative.split(path.sep)) {
|
|
78
|
+
cursor = path.join(cursor, segment);
|
|
79
|
+
const status = await lstatOrNull(cursor);
|
|
80
|
+
if (!status)
|
|
81
|
+
throw new Error(`protected_input_parent_not_found:${label}:${display(cursor)}`);
|
|
82
|
+
if (status.isSymbolicLink())
|
|
83
|
+
throw new Error(`protected_input_parent_symlink_not_allowed:${label}:${display(cursor)}`);
|
|
84
|
+
if (!status.isDirectory())
|
|
85
|
+
throw new Error(`protected_input_parent_not_directory:${label}:${display(cursor)}`);
|
|
86
|
+
assertInside(canonicalRepository, await realpath(cursor), label, cursor);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function assertRegularNoFollow(info, label, original) {
|
|
90
|
+
if (info.isSymbolicLink())
|
|
91
|
+
throw new Error(`protected_input_symlink_not_allowed:${label}:${display(original)}`);
|
|
92
|
+
if (!info.isFile())
|
|
93
|
+
throw new Error(`protected_input_not_regular_file:${label}:${display(original)}`);
|
|
94
|
+
if (typeof info.nlink === "number" && info.nlink > 1)
|
|
95
|
+
throw new Error(`protected_input_hardlink_not_allowed:${label}:${display(original)}`);
|
|
96
|
+
}
|
|
97
|
+
function repositoryRelative(repository, candidate, label) {
|
|
98
|
+
const relative = path.relative(repository, candidate);
|
|
99
|
+
if (!inside(repository, candidate))
|
|
100
|
+
throw new Error(`protected_input_realpath_outside_repository:${label}:${display(candidate)}`);
|
|
101
|
+
return relative;
|
|
102
|
+
}
|
|
103
|
+
function assertInside(repository, candidate, label, original) {
|
|
104
|
+
if (!inside(repository, candidate))
|
|
105
|
+
throw new Error(`protected_input_realpath_outside_repository:${label}:${display(original)}`);
|
|
106
|
+
}
|
|
107
|
+
function inside(repository, candidate) {
|
|
108
|
+
const relative = path.relative(repository, candidate);
|
|
109
|
+
return (relative === "" ||
|
|
110
|
+
(relative !== ".." &&
|
|
111
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
112
|
+
!path.isAbsolute(relative)));
|
|
113
|
+
}
|
|
114
|
+
async function lstatOrNull(target) {
|
|
115
|
+
try {
|
|
116
|
+
return await lstat(target);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (error.code === "ENOENT")
|
|
120
|
+
return null;
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function display(file) {
|
|
125
|
+
return file.replace(/\\/gu, "/");
|
|
126
|
+
}
|
|
@@ -169,15 +169,39 @@
|
|
|
169
169
|
],
|
|
170
170
|
"properties": {
|
|
171
171
|
"claims": { "type": "integer", "minimum": 0, "maximum": 1000000 },
|
|
172
|
-
"claim_projections": {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
172
|
+
"claim_projections": {
|
|
173
|
+
"type": "integer",
|
|
174
|
+
"minimum": 0,
|
|
175
|
+
"maximum": 2000000
|
|
176
|
+
},
|
|
177
|
+
"selector_members": {
|
|
178
|
+
"type": "integer",
|
|
179
|
+
"minimum": 0,
|
|
180
|
+
"maximum": 5000000
|
|
181
|
+
},
|
|
182
|
+
"structure_families": {
|
|
183
|
+
"type": "integer",
|
|
184
|
+
"minimum": 0,
|
|
185
|
+
"maximum": 1000000
|
|
186
|
+
},
|
|
187
|
+
"structure_references": {
|
|
188
|
+
"type": "integer",
|
|
189
|
+
"minimum": 0,
|
|
190
|
+
"maximum": 10000000
|
|
191
|
+
},
|
|
192
|
+
"structure_arguments": {
|
|
193
|
+
"type": "integer",
|
|
194
|
+
"minimum": 0,
|
|
195
|
+
"maximum": 50000000
|
|
196
|
+
},
|
|
177
197
|
"facts": { "type": "integer", "minimum": 0, "maximum": 1000000 },
|
|
178
198
|
"obligations": { "type": "integer", "minimum": 0, "maximum": 2000000 },
|
|
179
199
|
"assertions": { "type": "integer", "minimum": 0, "maximum": 5000000 },
|
|
180
|
-
"canonical_bytes": {
|
|
200
|
+
"canonical_bytes": {
|
|
201
|
+
"type": "integer",
|
|
202
|
+
"minimum": 0,
|
|
203
|
+
"maximum": 268435456
|
|
204
|
+
}
|
|
181
205
|
}
|
|
182
206
|
},
|
|
183
207
|
"longTaskCompactCarrier": {
|
|
@@ -229,9 +253,18 @@
|
|
|
229
253
|
"items": {
|
|
230
254
|
"type": "object",
|
|
231
255
|
"additionalProperties": false,
|
|
232
|
-
"required": [
|
|
256
|
+
"required": [
|
|
257
|
+
"key",
|
|
258
|
+
"digest",
|
|
259
|
+
"boundary",
|
|
260
|
+
"parameter_count",
|
|
261
|
+
"template"
|
|
262
|
+
],
|
|
233
263
|
"properties": {
|
|
234
|
-
"key": {
|
|
264
|
+
"key": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"pattern": "^structure\\.[a-f0-9]{24}$"
|
|
267
|
+
},
|
|
235
268
|
"digest": { "$ref": "#/$defs/sha256" },
|
|
236
269
|
"boundary": { "$ref": "#/$defs/semanticRef" },
|
|
237
270
|
"parameter_count": { "type": "integer", "minimum": 0 },
|
|
@@ -464,6 +497,10 @@
|
|
|
464
497
|
]
|
|
465
498
|
},
|
|
466
499
|
"root_entrypoint": { "$ref": "#/$defs/nonEmpty" },
|
|
500
|
+
"root_argv": {
|
|
501
|
+
"type": "array",
|
|
502
|
+
"items": { "type": "string", "pattern": "^[^\\u0000]*$" }
|
|
503
|
+
},
|
|
467
504
|
"capabilities": {
|
|
468
505
|
"type": "array",
|
|
469
506
|
"minItems": 1,
|
|
@@ -2011,6 +2048,10 @@
|
|
|
2011
2048
|
"check_key": { "$ref": "#/$defs/key" },
|
|
2012
2049
|
"expected_assertion_failures": { "$ref": "#/$defs/keys" },
|
|
2013
2050
|
"preserved_assertions": { "$ref": "#/$defs/keys", "default": [] },
|
|
2051
|
+
"allowed_fanout_assertions": {
|
|
2052
|
+
"$ref": "#/$defs/keys",
|
|
2053
|
+
"default": []
|
|
2054
|
+
},
|
|
2014
2055
|
"mutation": {
|
|
2015
2056
|
"oneOf": [
|
|
2016
2057
|
{
|
|
@@ -2091,6 +2132,10 @@
|
|
|
2091
2132
|
"uniqueItems": true
|
|
2092
2133
|
},
|
|
2093
2134
|
"preserved_assertions": { "$ref": "#/$defs/keys", "default": [] },
|
|
2135
|
+
"allowed_fanout_assertions": {
|
|
2136
|
+
"$ref": "#/$defs/keys",
|
|
2137
|
+
"default": []
|
|
2138
|
+
},
|
|
2094
2139
|
"mutation": {
|
|
2095
2140
|
"oneOf": [
|
|
2096
2141
|
{
|
package/migrations/README.md
CHANGED
|
@@ -13,3 +13,80 @@ V2 file predates explicit Stage, target-profile/root-runtime, journey-scenario,
|
|
|
13
13
|
success/degradation, evidence-capability or external-impact authority. Those
|
|
14
14
|
meanings must be re-authored from Source; migration never infers them from old
|
|
15
15
|
Progress or Receipts.
|
|
16
|
+
|
|
17
|
+
## Admitted-observation/runtime-TCB revision
|
|
18
|
+
|
|
19
|
+
The next Long-Task compatibility boundary intentionally does not provide a
|
|
20
|
+
mechanical Contract rewrite. Machine evidence that depends on a custom Oracle,
|
|
21
|
+
a project-submitted runtime/interaction/state/verdict row, a runner-created or
|
|
22
|
+
runner-modified carrier, an indirect wrapper, an unsupported target family or
|
|
23
|
+
a Counterfactual with no package-admitted actual must be re-authored explicitly.
|
|
24
|
+
Migration must never silently:
|
|
25
|
+
|
|
26
|
+
- replace a custom Oracle with a package Oracle;
|
|
27
|
+
- turn machine proof into `external_confirmation`;
|
|
28
|
+
- replace a verifier wrapper with a product root entrypoint;
|
|
29
|
+
- infer a static structure carrier from `Binding`/`input_paths` alone; or
|
|
30
|
+
- infer a process runtime dependency from `input_paths`, runner or verification membership;
|
|
31
|
+
- invent a Source-backed execution target or production Binding for an old root; or
|
|
32
|
+
- preserve a known false-acceptance path as compatibility behavior.
|
|
33
|
+
|
|
34
|
+
Greenfield authoring may declare an exact process root, argv repository file
|
|
35
|
+
or production carrier as `planned`. Its exact Source/Binding declaration is
|
|
36
|
+
stable through Preflight/Compile and later materialization, but Final Gate
|
|
37
|
+
requires every compiled closure member to exist. A glob, a manifest sibling or
|
|
38
|
+
an undeclared repository-file argv token cannot substitute; migration does not
|
|
39
|
+
invent these exact declarations or parse language dependencies.
|
|
40
|
+
|
|
41
|
+
Preflight and doctor should report the narrow failing obligation and an
|
|
42
|
+
actionable manual route through diagnostics such as
|
|
43
|
+
`machine_observer_not_admitted`,
|
|
44
|
+
`semantic_fact_machine_observer_not_admitted`,
|
|
45
|
+
`unsupported_observer_requires_external_confirmation`,
|
|
46
|
+
`custom_oracle_machine_completion_forbidden`,
|
|
47
|
+
`static_observation_not_in_pre_run_snapshot`,
|
|
48
|
+
`static_observation_changed_by_runner`,
|
|
49
|
+
`process_observer_direct_root_required`,
|
|
50
|
+
`process_observer_root_invocation_required`,
|
|
51
|
+
`process_observer_root_argv_mismatch`,
|
|
52
|
+
`process_observation_input_changed_by_runner`,
|
|
53
|
+
`process_runtime_input_expected_authority_forbidden`,
|
|
54
|
+
`process_runtime_input_verification_role_forbidden`,
|
|
55
|
+
`process_runtime_input_evidence_role_forbidden`,
|
|
56
|
+
`process_root_source_binding_required`,
|
|
57
|
+
`process_root_production_binding_required`,
|
|
58
|
+
`process_root_source_identity_mismatch`,
|
|
59
|
+
`process_runtime_carrier_exact_path_required`,
|
|
60
|
+
`process_runtime_input_missing`,
|
|
61
|
+
`process_runtime_closure_identity_mismatch`,
|
|
62
|
+
`legacy_target_runtime_non_authoritative`,
|
|
63
|
+
`counterfactual_admitted_observation_required`,
|
|
64
|
+
`counterfactual_runtime_reachability_unproven` and
|
|
65
|
+
`project_submitted_verdict_disagrees_with_harness`. The user or Agent must then
|
|
66
|
+
choose an admitted static/direct-process observation or the existing blocking
|
|
67
|
+
External Confirmation. That semantic choice is `manual_required`, not ordinary
|
|
68
|
+
`sync` or a migration-registry inference; release guidance must therefore use
|
|
69
|
+
the `manual-required` update mode for affected consumers.
|
|
70
|
+
|
|
71
|
+
When the whole affected Outcome is unsupported, re-author it explicitly as an
|
|
72
|
+
external-only route: set `success_path_required: false`, remove machine Checks,
|
|
73
|
+
bind every ordinary/global and Semantic Fact Claim through exact
|
|
74
|
+
`impact_claims`, keep every Semantic Fact proof's explicit `confirmation_ref`,
|
|
75
|
+
and ensure a `blocks_target: true` confirmation impacts each
|
|
76
|
+
Stage Gate result Claim. A non-blocking confirmation or one missing gate-result
|
|
77
|
+
lineage cannot replace a Stage Gate, and migration never invents these choices.
|
|
78
|
+
The valid route ends as `blocked_external`, not either machine-accepted status.
|
|
79
|
+
|
|
80
|
+
Changing verifier identity, Compile admission policy or the observation TCB
|
|
81
|
+
invalidates the prior Active Authority. The Contract must be recompiled or
|
|
82
|
+
adopted through an explicit Authority Revision; old Progress, evidence and
|
|
83
|
+
Receipt are not reusable on the new boundary. Public
|
|
84
|
+
`long-task-check-result-v3` remains the compatibility payload, but its
|
|
85
|
+
project-authored actual/pass/verdict/runtime fields are non-authoritative. The
|
|
86
|
+
Harness-owned process adapter captures the separate
|
|
87
|
+
`ty-context-product-observation-v1` envelope from bounded stdout inside an
|
|
88
|
+
isolated frozen runtime-closure snapshot. It exposes no output-path, challenge
|
|
89
|
+
or protocol environment variable to the child; any execution nonce remains
|
|
90
|
+
internal host-attestation data. No v4 is introduced by this revision; a future
|
|
91
|
+
v4 requires a distinct need to carry a live browser/native session handle
|
|
92
|
+
across processes.
|