project-tiny-context-harness 0.8.13 → 0.8.17
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 +53 -28
- package/assets/README.md +52 -25
- package/assets/README.zh-CN.md +55 -26
- package/assets/agents/AGENTS_CORE.md +2 -2
- package/assets/agents/long-task-implementation.toml +1 -1
- package/assets/context_templates/screen-contract.md +16 -0
- package/assets/skills/context_development_engineer/SKILL.md +16 -30
- package/assets/skills/context_development_engineer/references/engineering-design-reasoning.md +93 -0
- package/assets/skills/context_surface_contract/SKILL.md +13 -2
- package/assets/skills/context_uiux_design/SKILL.md +7 -3
- package/assets/skills/context_uiux_design/references/task-uiux-analysis.md +82 -0
- package/assets/skills/design-resource-authoring/SKILL.md +14 -8
- package/assets/skills/design-resource-authoring/references/downstream-handoff.md +18 -6
- package/assets/skills/design-resource-authoring/references/formal-selected-web-app-handoff.md +26 -8
- package/assets/skills/design-resource-authoring/references/implementation-feasibility.md +111 -0
- package/assets/skills/design-resource-authoring/references/open-design-provider.md +45 -7
- package/assets/skills/design-resource-authoring/references/recovery-and-writeback.md +7 -1
- package/assets/skills/design-resource-authoring/references/resource-selection.md +115 -3
- package/assets/skills/long-task-workflow/SKILL.md +9 -7
- package/assets/skills/long-task-workflow/agents/openai.yaml +1 -1
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +1 -1
- package/dist/commands/design-resource.js +9 -2
- package/dist/commands/long-task-revision.js +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/lib/design-resource-handoff-bundle.js +5 -0
- package/dist/lib/design-resource-handoff-input-types.d.ts +1 -1
- package/dist/lib/design-resource-handoff-manifest-projection.js +1 -0
- package/dist/lib/design-resource-handoff-set-integrity.js +1 -0
- package/dist/lib/design-resource-handoff-shape.js +6 -2
- package/dist/lib/design-resource-handoff-types.d.ts +5 -0
- package/dist/lib/design-resource-handoff-validation.js +12 -0
- package/dist/lib/design-resource-implementation-feasibility-model.d.ts +35 -0
- package/dist/lib/design-resource-implementation-feasibility-model.js +62 -0
- package/dist/lib/design-resource-implementation-feasibility-shape-sections.d.ts +43 -0
- package/dist/lib/design-resource-implementation-feasibility-shape-sections.js +142 -0
- package/dist/lib/design-resource-implementation-feasibility-shape.d.ts +4 -0
- package/dist/lib/design-resource-implementation-feasibility-shape.js +154 -0
- package/dist/lib/design-resource-implementation-feasibility-source-decision-projection.d.ts +24 -0
- package/dist/lib/design-resource-implementation-feasibility-source-decision-projection.js +106 -0
- package/dist/lib/design-resource-implementation-feasibility-source-decision.d.ts +21 -0
- package/dist/lib/design-resource-implementation-feasibility-source-decision.js +75 -0
- package/dist/lib/design-resource-implementation-feasibility-types.d.ts +135 -0
- package/dist/lib/design-resource-implementation-feasibility-types.js +37 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-cells.d.ts +5 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-cells.js +125 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-document.d.ts +4 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-document.js +167 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-facts.d.ts +3 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-facts.js +76 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-realizations.d.ts +8 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-realizations.js +76 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-support.d.ts +10 -0
- package/dist/lib/design-resource-implementation-feasibility-validation-support.js +75 -0
- package/dist/lib/design-resource-implementation-feasibility-validation.d.ts +3 -0
- package/dist/lib/design-resource-implementation-feasibility-validation.js +138 -0
- package/dist/lib/design-resource-symbolic-fact-shape.js +3 -1
- package/dist/lib/design-resource-symbolic-fact-types.d.ts +5 -0
- package/dist/lib/design-resource-symbolic-fact-validation.js +13 -0
- package/dist/lib/long-task-activation-validation.js +77 -7
- package/dist/lib/long-task-authority-revision-brief.js +3 -1
- package/dist/lib/long-task-authority-revision-summary.js +6 -2
- package/dist/lib/long-task-authority-revision-types.d.ts +2 -0
- package/dist/lib/long-task-check-runner.js +3 -296
- package/dist/lib/long-task-command-process.d.ts +9 -0
- package/dist/lib/long-task-command-process.js +175 -0
- package/dist/lib/long-task-delivery-validation.d.ts +5 -2
- package/dist/lib/long-task-delivery-validation.js +4 -4
- package/dist/lib/long-task-design-feasibility-binding-owners.d.ts +6 -0
- package/dist/lib/long-task-design-feasibility-binding-owners.js +52 -0
- package/dist/lib/long-task-design-feasibility-binding.d.ts +13 -0
- package/dist/lib/long-task-design-feasibility-binding.js +114 -0
- package/dist/lib/long-task-design-feasibility-source-closure.d.ts +11 -0
- package/dist/lib/long-task-design-feasibility-source-closure.js +78 -0
- package/dist/lib/long-task-design-resource-handoff.d.ts +3 -3
- package/dist/lib/long-task-design-resource-handoff.js +39 -3
- package/dist/lib/long-task-design-resource-method-binding.js +16 -4
- package/dist/lib/long-task-freshness.js +1 -1
- package/dist/lib/long-task-git.d.ts +26 -0
- package/dist/lib/long-task-git.js +130 -0
- package/dist/lib/long-task-paths.js +1 -1
- package/dist/lib/long-task-process-runtime-closure.js +74 -42
- package/dist/lib/long-task-process-table.d.ts +12 -0
- package/dist/lib/long-task-process-table.js +119 -0
- package/dist/lib/long-task-process-tree.d.ts +9 -0
- package/dist/lib/long-task-process-tree.js +138 -0
- package/dist/lib/long-task-protected-files.d.ts +1 -1
- package/dist/lib/long-task-protected-files.js +1 -1
- package/dist/lib/long-task-ui-surface-policy.d.ts +2 -1
- package/dist/lib/long-task-ui-surface-policy.js +7 -4
- package/dist/lib/long-task-ui-surface-validation.d.ts +3 -1
- package/dist/lib/long-task-ui-surface-validation.js +2 -2
- package/dist/lib/long-task-verifier-authority.js +2 -0
- package/dist/lib/long-task-workspace-manifest.d.ts +8 -0
- package/dist/lib/long-task-workspace-manifest.js +173 -0
- package/dist/lib/long-task-workspace-snapshot.d.ts +8 -0
- package/dist/lib/long-task-workspace-snapshot.js +176 -0
- package/dist/lib/long-task-workspace.d.ts +4 -25
- package/dist/lib/long-task-workspace.js +3 -432
- package/dist/lib/repository-path-safety.d.ts +2 -0
- package/dist/lib/repository-path-safety.js +36 -0
- package/dist/long-task-hook.js +12 -3
- package/dist/public-types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { DESIGN_RESOURCE_FEASIBILITY_DECISION_SCHEMA, deriveDesignResourceFeasibilityConditionScopeSha256, parseDesignResourceFeasibilityDecisionProjections, } from "./design-resource-implementation-feasibility-source-decision.js";
|
|
2
|
+
import { createV1ImplementationFeasibilityTargetModel, createV2ImplementationFeasibilityTargetModel, } from "./design-resource-implementation-feasibility-model.js";
|
|
3
|
+
import { matchingFeasibilityBindingKeys, validateFeasibilityBindingOwnerRoots, } from "./long-task-design-feasibility-binding-owners.js";
|
|
4
|
+
import { validateFeasibilityBlockers, validateSelectedPlannedOwnerClaims, validateTechnicalDecisionClaim, } from "./long-task-design-feasibility-source-closure.js";
|
|
5
|
+
import { invalid } from "./long-task-design-resource-method-binding.js";
|
|
6
|
+
export function validateLongTaskDesignFeasibilityBindings(contract, contractTarget, preflight, sourceItems, options = {}) {
|
|
7
|
+
const document = preflight.technical_feasibility_documents.find((item) => item.target_ref === contractTarget.target.key);
|
|
8
|
+
if (!document) {
|
|
9
|
+
if (!contractTarget.binding.component_binding_refs.length)
|
|
10
|
+
invalid("componentless_surface_requires_blocker_only_feasibility", contractTarget.target.key);
|
|
11
|
+
return {
|
|
12
|
+
consumed_component_binding_refs: [
|
|
13
|
+
...contractTarget.binding.component_binding_refs,
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const model = feasibilityTargetModel(preflight, contractTarget.target.key);
|
|
18
|
+
const decisions = compiledDecisionSources(document, sourceItems);
|
|
19
|
+
const outcome = contract.outcomes.find((item) => item.key === contractTarget.outcome_key);
|
|
20
|
+
const bindingsByKey = new Map(outcome.technical.bindings.map((binding) => [binding.key, binding]));
|
|
21
|
+
const componentBindings = contractTarget.binding.component_binding_refs.map((bindingRef) => {
|
|
22
|
+
const binding = bindingsByKey.get(bindingRef);
|
|
23
|
+
if (!binding)
|
|
24
|
+
invalid("feasibility_component_binding_unknown", `${contractTarget.target.key}:${bindingRef}`);
|
|
25
|
+
return binding;
|
|
26
|
+
});
|
|
27
|
+
const routeBinding = bindingsByKey.get(contractTarget.binding.route_binding_ref);
|
|
28
|
+
if (!routeBinding)
|
|
29
|
+
invalid("feasibility_route_binding_unknown", `${contractTarget.target.key}:${contractTarget.binding.route_binding_ref}`);
|
|
30
|
+
const hasCandidateCells = document.component_family_cells.some((cell) => cell.feasible_realizations.length > 0);
|
|
31
|
+
const allCellsBlockerOnly = document.component_family_cells.length > 0 &&
|
|
32
|
+
document.component_family_cells.every((cell) => cell.feasible_realizations.length === 0 && cell.blocker_refs.length > 0);
|
|
33
|
+
if (!componentBindings.length && hasCandidateCells)
|
|
34
|
+
invalid("feasibility_component_binding_required", contractTarget.target.key);
|
|
35
|
+
if (!componentBindings.length && !allCellsBlockerOnly)
|
|
36
|
+
invalid("componentless_surface_requires_blocker_only_feasibility", contractTarget.target.key);
|
|
37
|
+
validateFeasibilityBindingOwnerRoots(document, componentBindings, routeBinding);
|
|
38
|
+
const consumedComponentBindings = new Set();
|
|
39
|
+
for (const cell of document.component_family_cells) {
|
|
40
|
+
const blockerOnly = cell.feasible_realizations.length === 0 && cell.blocker_refs.length > 0;
|
|
41
|
+
if (blockerOnly)
|
|
42
|
+
continue;
|
|
43
|
+
const matching = cell.feasible_realizations
|
|
44
|
+
.map((realization) => ({
|
|
45
|
+
realization,
|
|
46
|
+
bindingKeys: matchingFeasibilityBindingKeys(realization, componentBindings),
|
|
47
|
+
}))
|
|
48
|
+
.filter((candidate) => candidate.bindingKeys.size > 0);
|
|
49
|
+
const required = cell.required_realization.realization_ref;
|
|
50
|
+
if (required !== null &&
|
|
51
|
+
(matching.length !== 1 || matching[0].realization.key !== required))
|
|
52
|
+
invalid("feasibility_required_realization_binding_mismatch", `${cell.key}:${required}:${matching.map((item) => item.realization.key).join(",")}`);
|
|
53
|
+
if (required === null && matching.length === 0)
|
|
54
|
+
invalid("feasibility_realization_binding_missing", cell.key);
|
|
55
|
+
if (required === null && matching.length > 1)
|
|
56
|
+
invalid("feasibility_realization_binding_ambiguous", `${cell.key}:${matching.map((item) => item.realization.key).join(",")}`);
|
|
57
|
+
const selected = matching.length === 1 ? matching[0] : null;
|
|
58
|
+
if (selected)
|
|
59
|
+
for (const bindingKey of selected.bindingKeys)
|
|
60
|
+
consumedComponentBindings.add(bindingKey);
|
|
61
|
+
if (required !== null)
|
|
62
|
+
validateTechnicalDecisionClaim(contract, contractTarget, decisions, cell.required_realization.technical_authority_source_refs, {
|
|
63
|
+
schema_version: DESIGN_RESOURCE_FEASIBILITY_DECISION_SCHEMA,
|
|
64
|
+
mode: "required_realization",
|
|
65
|
+
target_ref: cell.target_ref,
|
|
66
|
+
component_family_ref: cell.component_family_ref,
|
|
67
|
+
condition_scope_sha256: deriveDesignResourceFeasibilityConditionScopeSha256(document, model, cell.condition_profile_ref),
|
|
68
|
+
realization_ref: required,
|
|
69
|
+
}, `required_realization:${cell.key}`);
|
|
70
|
+
if (selected)
|
|
71
|
+
validateSelectedPlannedOwnerClaims(contract, contractTarget, document, model, decisions, selected.realization, componentBindings, cell);
|
|
72
|
+
}
|
|
73
|
+
if (!options.deferComponentBindingAttribution)
|
|
74
|
+
validateComponentBindingAttribution(contractTarget, consumedComponentBindings);
|
|
75
|
+
validateFeasibilityBlockers(contract, contractTarget, document, model, decisions);
|
|
76
|
+
return {
|
|
77
|
+
consumed_component_binding_refs: [...consumedComponentBindings].sort(),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function validateComponentBindingAttribution(contractTarget, consumedComponentBindings) {
|
|
81
|
+
for (const bindingRef of contractTarget.binding.component_binding_refs)
|
|
82
|
+
if (!consumedComponentBindings.has(bindingRef))
|
|
83
|
+
invalid("feasibility_component_binding_unattributed", `${contractTarget.target.key}:${bindingRef}`);
|
|
84
|
+
}
|
|
85
|
+
function feasibilityTargetModel(preflight, targetRef) {
|
|
86
|
+
if ("preflight_schema_version" in preflight) {
|
|
87
|
+
const target = preflight.handoff.targets.find((candidate) => candidate.key === targetRef);
|
|
88
|
+
return createV2ImplementationFeasibilityTargetModel(target, preflight.manifest);
|
|
89
|
+
}
|
|
90
|
+
const target = preflight.handoff.targets.find((candidate) => candidate.key === targetRef);
|
|
91
|
+
return createV1ImplementationFeasibilityTargetModel(preflight.handoff, target);
|
|
92
|
+
}
|
|
93
|
+
function compiledDecisionSources(document, sourceItems) {
|
|
94
|
+
const index = new Map();
|
|
95
|
+
for (const record of document.source_records) {
|
|
96
|
+
if (record.locator.kind !== "source_item")
|
|
97
|
+
continue;
|
|
98
|
+
const item = sourceItems.find((candidate) => candidate.source_path === record.path &&
|
|
99
|
+
candidate.key === record.locator.value);
|
|
100
|
+
if (!item)
|
|
101
|
+
invalid("feasibility_source_item_not_declared", `${record.key}:${record.path}#${record.locator.value}`);
|
|
102
|
+
if (item.text_sha256 !== record.locator.text_sha256)
|
|
103
|
+
invalid("feasibility_source_item_digest_mismatch", `${record.key}:${record.locator.text_sha256}:${item.text_sha256}`);
|
|
104
|
+
index.set(record.key, {
|
|
105
|
+
source_record_ref: record.key,
|
|
106
|
+
source_path: record.path,
|
|
107
|
+
source_item_key: item.key,
|
|
108
|
+
source_item_kind: item.kind,
|
|
109
|
+
text_sha256: item.text_sha256,
|
|
110
|
+
projections: parseDesignResourceFeasibilityDecisionProjections(record.path, item.key, item.normalized_text),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return index;
|
|
114
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DesignResourceImplementationFeasibilityTargetModel } from "./design-resource-implementation-feasibility-model.js";
|
|
2
|
+
import { type DesignResourceFeasibilityDecisionProjection, type DesignResourceFeasibilityDecisionSourceIndex } from "./design-resource-implementation-feasibility-source-decision.js";
|
|
3
|
+
import type { DesignResourceImplementationFeasibilityV1 } from "./design-resource-implementation-feasibility-types.js";
|
|
4
|
+
import type { DeliveryBindingV2, DeliveryContractV2 } from "./long-task-delivery-types.js";
|
|
5
|
+
import type { ContractDesignTarget } from "./long-task-design-resource-handoff.js";
|
|
6
|
+
type FeasibilityCell = DesignResourceImplementationFeasibilityV1["component_family_cells"][number];
|
|
7
|
+
type FeasibleRealization = FeasibilityCell["feasible_realizations"][number];
|
|
8
|
+
export declare function validateSelectedPlannedOwnerClaims(contract: DeliveryContractV2, contractTarget: ContractDesignTarget, document: DesignResourceImplementationFeasibilityV1, model: DesignResourceImplementationFeasibilityTargetModel, decisions: DesignResourceFeasibilityDecisionSourceIndex, realization: FeasibleRealization, bindings: DeliveryBindingV2[], cell: FeasibilityCell): void;
|
|
9
|
+
export declare function validateTechnicalDecisionClaim(contract: DeliveryContractV2, contractTarget: ContractDesignTarget, decisions: DesignResourceFeasibilityDecisionSourceIndex, sourceRefs: string[], expectation: DesignResourceFeasibilityDecisionProjection, label: string): void;
|
|
10
|
+
export declare function validateFeasibilityBlockers(contract: DeliveryContractV2, contractTarget: ContractDesignTarget, document: DesignResourceImplementationFeasibilityV1, model: DesignResourceImplementationFeasibilityTargetModel, decisions: DesignResourceFeasibilityDecisionSourceIndex): void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DESIGN_RESOURCE_FEASIBILITY_DECISION_SCHEMA, deriveDesignResourceFeasibilityConditionScopeSha256, requireExactFeasibilityDecisionProjection, } from "./design-resource-implementation-feasibility-source-decision.js";
|
|
2
|
+
import { invalid } from "./long-task-design-resource-method-binding.js";
|
|
3
|
+
export function validateSelectedPlannedOwnerClaims(contract, contractTarget, document, model, decisions, realization, bindings, cell) {
|
|
4
|
+
for (const owner of realization.owner_candidates) {
|
|
5
|
+
if (owner.kind !== "planned_logical_owner")
|
|
6
|
+
continue;
|
|
7
|
+
if (!bindings.some((binding) => binding.existence === "planned" &&
|
|
8
|
+
(binding.key === owner.locator || binding.target === owner.locator)))
|
|
9
|
+
continue;
|
|
10
|
+
validateTechnicalDecisionClaim(contract, contractTarget, decisions, owner.authorization_source_refs, {
|
|
11
|
+
schema_version: DESIGN_RESOURCE_FEASIBILITY_DECISION_SCHEMA,
|
|
12
|
+
mode: "planned_owner_authorization",
|
|
13
|
+
target_ref: cell.target_ref,
|
|
14
|
+
component_family_ref: cell.component_family_ref,
|
|
15
|
+
condition_scope_sha256: deriveDesignResourceFeasibilityConditionScopeSha256(document, model, cell.condition_profile_ref),
|
|
16
|
+
owner_locator: owner.locator,
|
|
17
|
+
}, `planned_owner:${cell.key}:${owner.locator}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function validateTechnicalDecisionClaim(contract, contractTarget, decisions, sourceRefs, expectation, label) {
|
|
21
|
+
const source = requireDecisionSource(sourceRefs, decisions, expectation, label, ["technical_obligation"], true);
|
|
22
|
+
const claim = exactSourceClaim(contract, source, label);
|
|
23
|
+
const validOutcomeClaim = claim.disposition.type === "claim" &&
|
|
24
|
+
claim.disposition.refs.some((ref) => ref.startsWith(`${contractTarget.outcome_key}.`));
|
|
25
|
+
if (!validOutcomeClaim && claim.disposition.type !== "global_constraint")
|
|
26
|
+
invalid("feasibility_technical_decision_claim_disposition_invalid", `${label}:${claim.key}:${claim.disposition.type}`);
|
|
27
|
+
}
|
|
28
|
+
export function validateFeasibilityBlockers(contract, contractTarget, document, model, decisions) {
|
|
29
|
+
for (const blocker of document.blockers) {
|
|
30
|
+
const source = requireDecisionSource(blocker.source_record_refs, decisions, {
|
|
31
|
+
schema_version: DESIGN_RESOURCE_FEASIBILITY_DECISION_SCHEMA,
|
|
32
|
+
mode: "feasibility_blocker",
|
|
33
|
+
target_ref: blocker.target_ref,
|
|
34
|
+
component_family_ref: blocker.component_family_ref,
|
|
35
|
+
condition_scope_sha256: deriveDesignResourceFeasibilityConditionScopeSha256(document, model, blocker.condition_profile_ref),
|
|
36
|
+
blocker_ref: blocker.key,
|
|
37
|
+
substrate_observation_refs: blocker.substrate_observation_refs,
|
|
38
|
+
}, `blocker:${blocker.key}`, ["decision", "external_confirmation"], false);
|
|
39
|
+
const claim = exactSourceClaim(contract, source, `blocker:${blocker.key}`);
|
|
40
|
+
if (source.source_item_kind === "decision") {
|
|
41
|
+
if (claim.disposition.type !== "decision_required")
|
|
42
|
+
invalid("feasibility_blocker_decision_required", `${blocker.key}:${claim.key}:${claim.disposition.type}`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (claim.disposition.type !== "external_confirmation")
|
|
46
|
+
invalid("feasibility_blocker_external_confirmation_required", `${blocker.key}:${claim.key}:${claim.disposition.type}`);
|
|
47
|
+
const impactedTargetClaims = new Set(contractTarget.target.claim_refs.map((claimRef) => `${contractTarget.outcome_key}.${claimRef}`));
|
|
48
|
+
for (const confirmationRef of claim.disposition.refs) {
|
|
49
|
+
const confirmation = contract.global.acceptance.external_confirmations.find((candidate) => candidate.key === confirmationRef);
|
|
50
|
+
if (!confirmation)
|
|
51
|
+
invalid("feasibility_blocker_confirmation_unknown", `${blocker.key}:${confirmationRef}`);
|
|
52
|
+
if (!confirmation.blocks_target)
|
|
53
|
+
invalid("feasibility_blocker_confirmation_not_blocking", `${blocker.key}:${confirmationRef}`);
|
|
54
|
+
if (!confirmation.impact_claims.some((claimRef) => impactedTargetClaims.has(claimRef)))
|
|
55
|
+
invalid("feasibility_blocker_confirmation_target_claim_missing", `${blocker.key}:${confirmationRef}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function requireDecisionSource(sourceRefs, decisions, expectation, label, allowedItemKinds, allReferencesMustBeSourceItems) {
|
|
60
|
+
try {
|
|
61
|
+
return requireExactFeasibilityDecisionProjection(sourceRefs, decisions, expectation, label, { allReferencesMustBeSourceItems, allowedItemKinds });
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
invalid("feasibility_source_decision_invalid", `${label}:${message(error)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function exactSourceClaim(contract, source, label) {
|
|
68
|
+
if (!contract.task.source_paths.includes(source.source_path))
|
|
69
|
+
invalid("feasibility_authority_source_not_declared", `${label}:${source.source_path}`);
|
|
70
|
+
const sourceRef = `${source.source_path}#${source.source_item_key}`;
|
|
71
|
+
const matching = contract.source_claims.filter((claim) => claim.source_ref === sourceRef);
|
|
72
|
+
if (matching.length !== 1)
|
|
73
|
+
invalid("feasibility_authority_claim_count", `${label}:${sourceRef}:${matching.length}`);
|
|
74
|
+
return matching[0];
|
|
75
|
+
}
|
|
76
|
+
function message(error) {
|
|
77
|
+
return error instanceof Error ? error.message : String(error);
|
|
78
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DesignResourceHandoffPreflightV1, DesignResourceHandoffTargetV1 } from "./design-resource-handoff-types.js";
|
|
2
2
|
import type { DesignResourceHandoffPreflightV2 } from "./design-resource-symbolic-fact-types.js";
|
|
3
|
-
import type { DeliveryContractV2 } from "./long-task-delivery-types.js";
|
|
3
|
+
import type { CompiledSourceItemV2, DeliveryContractV2 } from "./long-task-delivery-types.js";
|
|
4
4
|
export interface ContractDesignTarget {
|
|
5
5
|
outcome_key: string;
|
|
6
6
|
binding: DeliveryContractV2["outcomes"][number]["product"]["surface_bindings"][number];
|
|
@@ -14,5 +14,5 @@ export interface LongTaskDesignHandoffConsumer {
|
|
|
14
14
|
consume(preflight: DesignResourceHandoffPreflightV1 | DesignResourceHandoffPreflightV2): void;
|
|
15
15
|
finish(): void;
|
|
16
16
|
}
|
|
17
|
-
export declare function validateLongTaskDesignResourceHandoffs(contract: DeliveryContractV2, repository: string): Promise<void>;
|
|
18
|
-
export declare function createLongTaskDesignHandoffConsumer(contract: DeliveryContractV2): LongTaskDesignHandoffConsumer;
|
|
17
|
+
export declare function validateLongTaskDesignResourceHandoffs(contract: DeliveryContractV2, repository: string, sourceItems: CompiledSourceItemV2[]): Promise<void>;
|
|
18
|
+
export declare function createLongTaskDesignHandoffConsumer(contract: DeliveryContractV2, sourceItems?: CompiledSourceItemV2[]): LongTaskDesignHandoffConsumer;
|
|
@@ -6,8 +6,9 @@ import { preflightParsedDesignResourceHandoffAny } from "./design-resource-hando
|
|
|
6
6
|
import { assertSameSet, designSourceItemClaims, invalid, validateTargetIdentity, validateSymbolicTargetIdentity, validateSymbolicVerificationMethodBindings, validateVerificationMethodBindings, } from "./long-task-design-resource-method-binding.js";
|
|
7
7
|
import { validateLongTaskDesignTargetCapabilities } from "./long-task-design-target-capabilities.js";
|
|
8
8
|
import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import { validateLongTaskDesignFeasibilityBindings } from "./long-task-design-feasibility-binding.js";
|
|
10
|
+
export async function validateLongTaskDesignResourceHandoffs(contract, repository, sourceItems) {
|
|
11
|
+
const consumer = createLongTaskDesignHandoffConsumer(contract, sourceItems);
|
|
11
12
|
for (const sourcePath of contract.task.source_paths) {
|
|
12
13
|
if (!sourcePath.toLowerCase().endsWith(".md"))
|
|
13
14
|
continue;
|
|
@@ -19,7 +20,7 @@ export async function validateLongTaskDesignResourceHandoffs(contract, repositor
|
|
|
19
20
|
}
|
|
20
21
|
consumer.finish();
|
|
21
22
|
}
|
|
22
|
-
export function createLongTaskDesignHandoffConsumer(contract) {
|
|
23
|
+
export function createLongTaskDesignHandoffConsumer(contract, sourceItems = []) {
|
|
23
24
|
const contractTargets = contract.outcomes.flatMap((outcome) => outcome.product.surface_bindings.flatMap((binding) => binding.design_targets.map((target) => ({
|
|
24
25
|
outcome_key: outcome.key,
|
|
25
26
|
binding,
|
|
@@ -29,6 +30,7 @@ export function createLongTaskDesignHandoffConsumer(contract) {
|
|
|
29
30
|
const indexedTargetKeys = new Set();
|
|
30
31
|
const consumed = new Set();
|
|
31
32
|
const validationErrors = new Map();
|
|
33
|
+
const feasibilityBindingRefsBySurface = new Map();
|
|
32
34
|
let duplicate = null;
|
|
33
35
|
const unbound = [];
|
|
34
36
|
const handoffSetIntegrity = createDesignResourceHandoffSetIntegrity(invalid);
|
|
@@ -54,6 +56,14 @@ export function createLongTaskDesignHandoffConsumer(contract) {
|
|
|
54
56
|
else
|
|
55
57
|
resourcePaths.set(resource.path, identity);
|
|
56
58
|
}
|
|
59
|
+
for (const input of preflight.handoff.technical_feasibility_inputs) {
|
|
60
|
+
const identity = JSON.stringify(input);
|
|
61
|
+
const previous = resourcePaths.get(input.path);
|
|
62
|
+
if (previous && previous !== identity)
|
|
63
|
+
recordCrossVersionIssue("handoff_set_feasibility_path_conflict", input.path);
|
|
64
|
+
else
|
|
65
|
+
resourcePaths.set(input.path, identity);
|
|
66
|
+
}
|
|
57
67
|
if (!("preflight_schema_version" in preflight))
|
|
58
68
|
handoffSetIntegrity.consume(preflight);
|
|
59
69
|
for (const target of preflight.handoff.targets) {
|
|
@@ -75,6 +85,8 @@ export function createLongTaskDesignHandoffConsumer(contract) {
|
|
|
75
85
|
target,
|
|
76
86
|
};
|
|
77
87
|
validateSymbolicTargetIdentity(contractTarget, indexedTarget);
|
|
88
|
+
const feasibility = validateLongTaskDesignFeasibilityBindings(contract, contractTarget, preflight, sourceItems, { deferComponentBindingAttribution: true });
|
|
89
|
+
recordFeasibilityBindingAttribution(feasibilityBindingRefsBySurface, contractTarget, feasibility.consumed_component_binding_refs);
|
|
78
90
|
validateSymbolicCoverageClaims(contract, contractTarget, indexedTarget);
|
|
79
91
|
validateBlockerBindings(contract, contractTarget, indexedTarget);
|
|
80
92
|
}
|
|
@@ -84,6 +96,8 @@ export function createLongTaskDesignHandoffConsumer(contract) {
|
|
|
84
96
|
target: target,
|
|
85
97
|
};
|
|
86
98
|
validateTargetIdentity(contractTarget, indexedTarget);
|
|
99
|
+
const feasibility = validateLongTaskDesignFeasibilityBindings(contract, contractTarget, preflight, sourceItems, { deferComponentBindingAttribution: true });
|
|
100
|
+
recordFeasibilityBindingAttribution(feasibilityBindingRefsBySurface, contractTarget, feasibility.consumed_component_binding_refs);
|
|
87
101
|
validateLongTaskDesignTargetCapabilities(contract, contractTarget, indexedTarget);
|
|
88
102
|
validateCoverageClaims(contract, contractTarget, indexedTarget);
|
|
89
103
|
validateBlockerBindings(contract, contractTarget, indexedTarget);
|
|
@@ -106,11 +120,33 @@ export function createLongTaskDesignHandoffConsumer(contract) {
|
|
|
106
120
|
if (validationErrors.has(contractTarget.target.key))
|
|
107
121
|
throw validationErrors.get(contractTarget.target.key);
|
|
108
122
|
}
|
|
123
|
+
validateSurfaceFeasibilityBindingAttribution(contractTargets, feasibilityBindingRefsBySurface);
|
|
109
124
|
if (unbound.length)
|
|
110
125
|
invalid("handoff_target_unbound", unbound[0]);
|
|
111
126
|
},
|
|
112
127
|
};
|
|
113
128
|
}
|
|
129
|
+
function recordFeasibilityBindingAttribution(bySurface, contractTarget, bindingRefs) {
|
|
130
|
+
const key = surfaceBindingIdentity(contractTarget);
|
|
131
|
+
const consumed = bySurface.get(key) ?? new Set();
|
|
132
|
+
for (const bindingRef of bindingRefs)
|
|
133
|
+
consumed.add(bindingRef);
|
|
134
|
+
bySurface.set(key, consumed);
|
|
135
|
+
}
|
|
136
|
+
function validateSurfaceFeasibilityBindingAttribution(contractTargets, bySurface) {
|
|
137
|
+
const surfaces = new Map();
|
|
138
|
+
for (const contractTarget of contractTargets)
|
|
139
|
+
surfaces.set(surfaceBindingIdentity(contractTarget), contractTarget);
|
|
140
|
+
for (const [surfaceKey, contractTarget] of surfaces) {
|
|
141
|
+
const consumed = bySurface.get(surfaceKey) ?? new Set();
|
|
142
|
+
for (const bindingRef of contractTarget.binding.component_binding_refs)
|
|
143
|
+
if (!consumed.has(bindingRef))
|
|
144
|
+
invalid("feasibility_component_binding_unattributed", `${contractTarget.binding.key}:${bindingRef}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function surfaceBindingIdentity(contractTarget) {
|
|
148
|
+
return `${contractTarget.outcome_key}\0${contractTarget.binding.key}`;
|
|
149
|
+
}
|
|
114
150
|
function validateSymbolicCoverageClaims(contract, contractTarget, indexed) {
|
|
115
151
|
const claims = new Map(contract.source_claims.map((item) => [item.key, item]));
|
|
116
152
|
const rows = indexed.preflight.handoff.coverage.filter((row) => row.target_ref === contractTarget.target.key);
|
|
@@ -10,7 +10,10 @@ export function validateTargetIdentity(contractTarget, indexed) {
|
|
|
10
10
|
assertSameSet(target.condition_keys, handoffTarget.condition_refs, "target_conditions_mismatch", target.key);
|
|
11
11
|
const resourcePaths = handoffTarget.resource_refs.map((ref) => indexed.preflight.handoff.resources.find((item) => item.key === ref)
|
|
12
12
|
.path);
|
|
13
|
-
|
|
13
|
+
const feasibilityPaths = indexed.preflight.handoff.technical_feasibility_inputs
|
|
14
|
+
.filter((item) => item.target_ref === handoffTarget.key)
|
|
15
|
+
.map((item) => item.path);
|
|
16
|
+
assertSameSet(target.source_paths, [indexed.preflight.handoff_path, ...resourcePaths, ...feasibilityPaths], "target_source_paths_mismatch", target.key);
|
|
14
17
|
}
|
|
15
18
|
export function validateSymbolicTargetIdentity(contractTarget, indexed) {
|
|
16
19
|
const { target } = contractTarget;
|
|
@@ -25,7 +28,10 @@ export function validateSymbolicTargetIdentity(contractTarget, indexed) {
|
|
|
25
28
|
invalid("v2_ground_method_bindings_forbidden", target.key);
|
|
26
29
|
const resourcePaths = handoffTarget.resource_refs.map((ref) => indexed.preflight.handoff.resources.find((item) => item.key === ref)
|
|
27
30
|
.path);
|
|
28
|
-
|
|
31
|
+
const feasibilityPaths = indexed.preflight.handoff.technical_feasibility_inputs
|
|
32
|
+
.filter((item) => item.target_ref === handoffTarget.key)
|
|
33
|
+
.map((item) => item.path);
|
|
34
|
+
assertSameSet(target.source_paths, [indexed.preflight.handoff_path, ...resourcePaths, ...feasibilityPaths], "target_source_paths_mismatch", target.key);
|
|
29
35
|
}
|
|
30
36
|
export function validateSymbolicVerificationMethodBindings(contract, contractTarget, indexed, claimsBySourceItem) {
|
|
31
37
|
const target = contractTarget.target;
|
|
@@ -49,6 +55,7 @@ export function validateSymbolicVerificationMethodBindings(contract, contractTar
|
|
|
49
55
|
const assertion = check.positive_assertions.find((item) => item.key === binding.assertion_ref);
|
|
50
56
|
if (!assertion)
|
|
51
57
|
invalid("v2_verification_method_assertion_unknown", `${target.key}:${binding.method}:${binding.assertion_ref}`);
|
|
58
|
+
const assertedClaims = claimsAssertedByRootOrMethod(target, check, assertion.claims);
|
|
52
59
|
for (const capability of requiredCapabilities(binding.method))
|
|
53
60
|
if (!assertion.evidence_capabilities.includes(capability))
|
|
54
61
|
invalid("v2_verification_method_capability_required", `${target.key}:${binding.method}:${capability}`);
|
|
@@ -60,7 +67,7 @@ export function validateSymbolicVerificationMethodBindings(contract, contractTar
|
|
|
60
67
|
const rule = preflight.manifest.fact_rules.find((item) => item.key === expectation.fact_rule_ref);
|
|
61
68
|
for (const sourceItemRef of rule.source_item_refs)
|
|
62
69
|
for (const claimRef of claimsBySourceItem.get(sourceItemRef) ?? [])
|
|
63
|
-
if (!
|
|
70
|
+
if (!assertedClaims.has(claimRef))
|
|
64
71
|
invalid("v2_verification_method_claim_not_asserted", `${target.key}:${binding.method}:${sourceItemRef}:${claimRef}`);
|
|
65
72
|
boundObligationRefs.push(expectation.obligation_ref);
|
|
66
73
|
}
|
|
@@ -145,12 +152,13 @@ export function validateVerificationMethodBindings(target, check, facts, proofs,
|
|
|
145
152
|
const assertion = check.positive_assertions.find((item) => item.key === binding.assertion_ref);
|
|
146
153
|
if (!assertion)
|
|
147
154
|
invalid("verification_method_assertion_unknown", `${target.key}:${binding.method}:${binding.assertion_ref}`);
|
|
155
|
+
const assertedClaims = claimsAssertedByRootOrMethod(target, check, assertion.claims);
|
|
148
156
|
const sourceItems = new Set(facts
|
|
149
157
|
.filter((fact) => proofs.some((proof) => proof.fact_ref === fact.key && proof.method === binding.method))
|
|
150
158
|
.flatMap((fact) => fact.source_item_refs));
|
|
151
159
|
for (const sourceItemRef of sourceItems)
|
|
152
160
|
for (const claimRef of claimsBySourceItem.get(sourceItemRef) ?? [])
|
|
153
|
-
if (!
|
|
161
|
+
if (!assertedClaims.has(claimRef))
|
|
154
162
|
invalid("verification_method_claim_not_asserted", `${target.key}:${binding.method}:${sourceItemRef}:${claimRef}`);
|
|
155
163
|
for (const capability of requiredCapabilities(binding.method))
|
|
156
164
|
if (!assertion.evidence_capabilities.includes(capability))
|
|
@@ -173,6 +181,10 @@ export function validateVerificationMethodBindings(target, check, facts, proofs,
|
|
|
173
181
|
}
|
|
174
182
|
assertSameSet(boundFactRefs, facts.map((fact) => fact.key), "design_method_fact_refs_mismatch", target.key);
|
|
175
183
|
}
|
|
184
|
+
function claimsAssertedByRootOrMethod(target, check, methodClaims) {
|
|
185
|
+
const rootAssertion = check.positive_assertions.find((item) => item.key === target.conformance_assertion_ref);
|
|
186
|
+
return new Set([...(rootAssertion?.claims ?? []), ...methodClaims]);
|
|
187
|
+
}
|
|
176
188
|
function designFactExpectation(indexed, factRef, proofRef) {
|
|
177
189
|
const handoff = indexed.preflight.handoff;
|
|
178
190
|
const fact = handoff.facts.find((item) => item.key === factRef);
|
|
@@ -8,7 +8,7 @@ import { parseDeliveryContractBundle } from "./long-task-delivery-parser.js";
|
|
|
8
8
|
export async function deliveryCompileFreshness(compiled) {
|
|
9
9
|
const findings = [];
|
|
10
10
|
try {
|
|
11
|
-
const currentContract = await parseDeliveryContractBundle(compiled.workdir);
|
|
11
|
+
const currentContract = await parseDeliveryContractBundle(compiled.workdir, compiled.repository_root, { validate_structure: false });
|
|
12
12
|
if (sha256Hex(canonicalValueJson(currentContract.contract)) !==
|
|
13
13
|
compiled.contract_sha256)
|
|
14
14
|
findings.push(`contract_changed_after_compile:${compiled.contract_file}`);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class GitCommandError extends Error {
|
|
2
|
+
readonly exitCode: number | null;
|
|
3
|
+
readonly signal: NodeJS.Signals | null;
|
|
4
|
+
readonly stdoutBytes: number;
|
|
5
|
+
readonly stderrBytes: number;
|
|
6
|
+
constructor(exitCode: number | null, signal: NodeJS.Signals | null, argv: string[], stdout: Buffer, stderr: Buffer);
|
|
7
|
+
}
|
|
8
|
+
export declare function repositoryRoot(start: string): Promise<string>;
|
|
9
|
+
export declare function gitCommonDir(root: string): Promise<string>;
|
|
10
|
+
export declare function currentGitState(root: string): Promise<{
|
|
11
|
+
head: string;
|
|
12
|
+
tree: string;
|
|
13
|
+
dirty: string[];
|
|
14
|
+
}>;
|
|
15
|
+
export declare function currentGitTree(root: string): Promise<string>;
|
|
16
|
+
export declare function gitPath(root: string, pathSpec: string): Promise<string>;
|
|
17
|
+
export declare function gitConfigGet(root: string, name: string): Promise<string | null>;
|
|
18
|
+
export declare function gitEffectiveConfigGet(root: string, name: string): Promise<string | null>;
|
|
19
|
+
export declare function gitConfigSet(root: string, name: string, value: string): Promise<void>;
|
|
20
|
+
export declare function gitConfigUnset(root: string, name: string): Promise<void>;
|
|
21
|
+
export declare function repoRelative(rootInput: string, fileInput: string): string;
|
|
22
|
+
export declare function gitOutput(root: string, argv: string[]): Promise<string>;
|
|
23
|
+
export declare function gitVoid(root: string, argv: string[]): Promise<void>;
|
|
24
|
+
export declare function gitBuffer(root: string, argv: string[]): Promise<Buffer>;
|
|
25
|
+
export declare function gitBufferInput(root: string, argv: string[], input?: Buffer): Promise<Buffer>;
|
|
26
|
+
export declare function splitGitZero(value: Buffer): string[];
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export class GitCommandError extends Error {
|
|
5
|
+
exitCode;
|
|
6
|
+
signal;
|
|
7
|
+
stdoutBytes;
|
|
8
|
+
stderrBytes;
|
|
9
|
+
constructor(exitCode, signal, argv, stdout, stderr) {
|
|
10
|
+
const stderrText = stderr.toString("utf8").trim();
|
|
11
|
+
super(`git_exit:${exitCode ?? "null"}:${argv.join(" ")}:signal=${signal ?? "none"}:stdout_bytes=${stdout.length}:stderr=${stderrText}`);
|
|
12
|
+
this.name = "GitCommandError";
|
|
13
|
+
this.exitCode = exitCode;
|
|
14
|
+
this.signal = signal;
|
|
15
|
+
this.stdoutBytes = stdout.length;
|
|
16
|
+
this.stderrBytes = stderr.length;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function repositoryRoot(start) {
|
|
20
|
+
const resolved = path.resolve(start);
|
|
21
|
+
const gitEntry = await stat(path.join(resolved, ".git")).catch(() => null);
|
|
22
|
+
if (gitEntry?.isDirectory() || gitEntry?.isFile())
|
|
23
|
+
return resolved;
|
|
24
|
+
return path.resolve(await gitOutput(resolved, ["rev-parse", "--show-toplevel"]));
|
|
25
|
+
}
|
|
26
|
+
export async function gitCommonDir(root) {
|
|
27
|
+
return path.resolve(await gitOutput(root, [
|
|
28
|
+
"rev-parse",
|
|
29
|
+
"--path-format=absolute",
|
|
30
|
+
"--git-common-dir",
|
|
31
|
+
]));
|
|
32
|
+
}
|
|
33
|
+
export async function currentGitState(root) {
|
|
34
|
+
const [head, tree, raw] = await Promise.all([
|
|
35
|
+
gitOutput(root, ["rev-parse", "HEAD"]),
|
|
36
|
+
gitOutput(root, ["rev-parse", "HEAD^{tree}"]),
|
|
37
|
+
gitOutput(root, ["status", "--short", "--untracked-files=all"]),
|
|
38
|
+
]);
|
|
39
|
+
return {
|
|
40
|
+
head,
|
|
41
|
+
tree,
|
|
42
|
+
dirty: raw ? raw.split(/\r?\n/u).filter(Boolean) : [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export async function currentGitTree(root) {
|
|
46
|
+
return gitOutput(root, ["rev-parse", "HEAD^{tree}"]);
|
|
47
|
+
}
|
|
48
|
+
export async function gitPath(root, pathSpec) {
|
|
49
|
+
return path.resolve(await gitOutput(root, [
|
|
50
|
+
"rev-parse",
|
|
51
|
+
"--path-format=absolute",
|
|
52
|
+
"--git-path",
|
|
53
|
+
pathSpec,
|
|
54
|
+
]));
|
|
55
|
+
}
|
|
56
|
+
export async function gitConfigGet(root, name) {
|
|
57
|
+
try {
|
|
58
|
+
return await gitOutput(root, ["config", "--local", "--get", name]);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (error instanceof GitCommandError && error.exitCode === 1)
|
|
62
|
+
return null;
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function gitEffectiveConfigGet(root, name) {
|
|
67
|
+
try {
|
|
68
|
+
return await gitOutput(root, ["config", "--get", name]);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error instanceof GitCommandError && error.exitCode === 1)
|
|
72
|
+
return null;
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export async function gitConfigSet(root, name, value) {
|
|
77
|
+
await gitVoid(root, ["config", "--local", name, value]);
|
|
78
|
+
}
|
|
79
|
+
export async function gitConfigUnset(root, name) {
|
|
80
|
+
try {
|
|
81
|
+
await gitVoid(root, ["config", "--local", "--unset-all", name]);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (!(error instanceof GitCommandError) || error.exitCode !== 5)
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function repoRelative(rootInput, fileInput) {
|
|
89
|
+
const value = path
|
|
90
|
+
.relative(path.resolve(rootInput), path.resolve(fileInput))
|
|
91
|
+
.replace(/\\/gu, "/");
|
|
92
|
+
if (value.startsWith("../") || path.isAbsolute(value))
|
|
93
|
+
throw new Error(`path_outside_repository:${fileInput}`);
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
export async function gitOutput(root, argv) {
|
|
97
|
+
return (await gitBuffer(root, argv)).toString("utf8").trim();
|
|
98
|
+
}
|
|
99
|
+
export async function gitVoid(root, argv) {
|
|
100
|
+
await gitBuffer(root, argv);
|
|
101
|
+
}
|
|
102
|
+
export async function gitBuffer(root, argv) {
|
|
103
|
+
return gitBufferInput(root, argv);
|
|
104
|
+
}
|
|
105
|
+
export async function gitBufferInput(root, argv, input) {
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
const child = spawn("git", argv, {
|
|
108
|
+
cwd: root,
|
|
109
|
+
shell: false,
|
|
110
|
+
windowsHide: true,
|
|
111
|
+
});
|
|
112
|
+
const stdout = [];
|
|
113
|
+
const stderr = [];
|
|
114
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
115
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
116
|
+
child.on("error", reject);
|
|
117
|
+
child.stdin.end(input);
|
|
118
|
+
child.on("close", (code, signal) => {
|
|
119
|
+
const stdoutBytes = Buffer.concat(stdout);
|
|
120
|
+
const stderrBytes = Buffer.concat(stderr);
|
|
121
|
+
if (code === 0)
|
|
122
|
+
resolve(stdoutBytes);
|
|
123
|
+
else
|
|
124
|
+
reject(new GitCommandError(code, signal, argv, stdoutBytes, stderrBytes));
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
export function splitGitZero(value) {
|
|
129
|
+
return value.toString("utf8").split("\0").filter(Boolean);
|
|
130
|
+
}
|
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { resolveInsideRepository } from "./long-task-workspace.js";
|
|
3
3
|
export function parseRepositoryPattern(value, label = "repository_pattern") {
|
|
4
4
|
const normalized = canonicalRepositoryPath(value, label, "pattern");
|
|
5
|
-
if (/[\[\]{}
|
|
5
|
+
if (/[\[\]{}]/u.test(normalized))
|
|
6
6
|
throw new Error(`unsupported_repository_pattern_syntax:${label}:${value}`);
|
|
7
7
|
const segments = normalized.split("/").map((segment) => {
|
|
8
8
|
if (segment === "**")
|