@peterxiaoyang/superspec 0.1.5 → 0.1.6
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/adapters/codex/agents/code-reviewer.toml +2 -2
- package/adapters/codex/agents/executor.toml +13 -0
- package/adapters/codex/agents/test-runner.toml +13 -0
- package/adapters/codex/agents/verifier.toml +2 -2
- package/adapters/codex/install-map.json +20 -0
- package/dist/src/apply_worker_chain.d.ts +57 -0
- package/dist/src/apply_worker_chain.js +1188 -0
- package/dist/src/cli_args.d.ts +8 -0
- package/dist/src/cli_args.js +121 -5
- package/dist/src/core.d.ts +1 -0
- package/dist/src/core.js +1 -0
- package/dist/src/evidence.js +152 -0
- package/dist/src/gates.d.ts +1 -0
- package/dist/src/gates.js +272 -2
- package/dist/src/install_engine.d.ts +17 -0
- package/dist/src/install_engine.js +125 -2
- package/dist/src/packet_measure.js +27 -5
- package/dist/src/packet_render.js +974 -3
- package/dist/src/packet_schema.d.ts +2 -1
- package/dist/src/tasks.d.ts +10 -0
- package/dist/src/tasks.js +86 -0
- package/dist/src/util.d.ts +1 -1
- package/dist/src/util.js +3 -0
- package/package.json +1 -1
- package/schemas/install-manifest.schema.json +17 -0
- package/templates/workflow/prompts/code-reviewer.md +7 -1
- package/templates/workflow/prompts/executor.md +32 -0
- package/templates/workflow/prompts/test-runner.md +33 -0
- package/templates/workflow/prompts/verifier.md +7 -1
- package/templates/workflow/skills/superspec-apply/SKILL.md +39 -1
|
@@ -2,14 +2,16 @@ import { existsSync, readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { load_config, sidecar_business_invariants_path, sidecar_discovery_path, sidecar_test_contract_path } from "./paths.js";
|
|
4
4
|
import {} from "./cli_args.js";
|
|
5
|
-
import { CLAIM_ADJUDICATION_DECISIONS, FINDING_ADJUDICATION_DECISIONS, GuardError, MAIN_ADJUDICATION_REQUIRED_FIELDS, MAIN_ADJUDICATION_DECISIONS, REQUEST_CHANGES_ROUTES, REVIEW_EVIDENCE_REQUIRED_FIELDS, ROLE_EVIDENCE_FIELDS, SOURCE_GUIDANCE_REQUIRED_FIELDS, VERIFY_EVIDENCE_REQUIRED_FIELDS, allow, block, isObject, renderList, runtime, } from "./util.js";
|
|
5
|
+
import { CLAIM_ADJUDICATION_DECISIONS, FINDING_ADJUDICATION_DECISIONS, fingerprint_obj, GuardError, MAIN_ADJUDICATION_REQUIRED_FIELDS, MAIN_ADJUDICATION_DECISIONS, REQUEST_CHANGES_ROUTES, REVIEW_EVIDENCE_REQUIRED_FIELDS, ROLE_EVIDENCE_FIELDS, SOURCE_GUIDANCE_REQUIRED_FIELDS, VERIFY_EVIDENCE_REQUIRED_FIELDS, allow, block, deepEqual, isObject, reason, renderList, runtime, safe_within, sha256_text, } from "./util.js";
|
|
6
6
|
import { artifact_status_map, gate_route_phase, get_change_root, get_repo_root, normalize_gate, openspec_status, openspec_status_shape_reasons, } from "./openspec.js";
|
|
7
7
|
import { build_finding_ledger, enumerate_review_targets, render_finding_ledger, REVIEW_TARGETS_BY_GATE, review_round_number, } from "./disclosure.js";
|
|
8
|
-
import { evidence_schema_guard, check_apply_ready, check_archive_ready, check_review_complete, check_superspec_gate, check_task_complete, check_task_edit, check_task_reopen, } from "./gates.js";
|
|
8
|
+
import { evidence_schema_guard, check_apply_ready, check_archive_ready, check_review_complete, check_superspec_gate, check_task_complete, check_task_edit, check_task_reopen, apply_worker_chain_lifecycle_reasons, } from "./gates.js";
|
|
9
9
|
import { final_verification_evidences, index_evidence, live_pass, live_user_confirmations } from "./evidence.js";
|
|
10
10
|
import { file_blob_sha, dirty_worktree_paths } from "./git.js";
|
|
11
11
|
import { preset_upgrade_reasons, preset_upgrade_required_from_context } from "./archive.js";
|
|
12
12
|
import { state_corrupt_reasons, state_stale_reasons } from "./state.js";
|
|
13
|
+
import { parse_tasks, resolve_test_contract_command, splitList, test_contract_invariant_refs_by_test } from "./tasks.js";
|
|
14
|
+
import { APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS, APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS, APPLY_TEST_RUNNER_REPORT_REQUIRED_FIELDS, APPLY_VERIFIER_REPORT_REQUIRED_FIELDS, apply_worker_implementation_fingerprint, apply_worker_executor_input_ref_digest, apply_worker_protected_path_refs, compute_apply_worker_freshness, fingerprint_digest, fingerprint_matches, pinned_artifact_ref_reasons as shared_pinned_artifact_ref_reasons, pre_edit_evidence_ref_reasons, read_pinned_artifact_json, worker_input_ref_digest, worker_test_run_reasons, } from "./apply_worker_chain.js";
|
|
13
15
|
function load_packet_context(change) {
|
|
14
16
|
if (typeof runtime.load_context === "function") {
|
|
15
17
|
const [status, repoRoot, changeRoot, evidences] = runtime.load_context(change);
|
|
@@ -576,6 +578,941 @@ function review_packet(ctx, gateRaw, role, round, requestedKind) {
|
|
|
576
578
|
packet.must_read_verbatim_decisions = decisionSelectors;
|
|
577
579
|
return packet;
|
|
578
580
|
}
|
|
581
|
+
function decision_status(decision) {
|
|
582
|
+
return decision.allowed ? "allowed" : "blocked";
|
|
583
|
+
}
|
|
584
|
+
function safe_decision_status(ctx, gate, taskId) {
|
|
585
|
+
try {
|
|
586
|
+
return decision_status(evaluate_workflow_decision(ctx, gate, taskId));
|
|
587
|
+
}
|
|
588
|
+
catch {
|
|
589
|
+
return "blocked";
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function decision_reasons(decision) {
|
|
593
|
+
return Array.isArray(decision.block_reasons) ? decision.block_reasons : [];
|
|
594
|
+
}
|
|
595
|
+
function apply_worker_report_policy() {
|
|
596
|
+
return {
|
|
597
|
+
max_inline_report_chars: 12000,
|
|
598
|
+
artifact_ref_policy: "raw transcripts, diffs, logs, and long outputs must be returned as refs",
|
|
599
|
+
truncation_policy: "fail_closed",
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function without_audit_metadata(value) {
|
|
603
|
+
if (Array.isArray(value))
|
|
604
|
+
return value.map(without_audit_metadata);
|
|
605
|
+
if (!isObject(value))
|
|
606
|
+
return value;
|
|
607
|
+
const out = {};
|
|
608
|
+
for (const [key, child] of Object.entries(value)) {
|
|
609
|
+
if (key === "computed_at")
|
|
610
|
+
continue;
|
|
611
|
+
out[key] = without_audit_metadata(child);
|
|
612
|
+
}
|
|
613
|
+
return out;
|
|
614
|
+
}
|
|
615
|
+
function apply_worker_stop_conditions(kind) {
|
|
616
|
+
if (kind === "apply_test") {
|
|
617
|
+
return [
|
|
618
|
+
"Stop after reporting the bounded test command result only.",
|
|
619
|
+
"Do not change implementation files.",
|
|
620
|
+
"Return raw logs as pinned artifact refs when output is long.",
|
|
621
|
+
];
|
|
622
|
+
}
|
|
623
|
+
if (kind === "apply_executor") {
|
|
624
|
+
return [
|
|
625
|
+
"Stay inside declared_task_write_scope.",
|
|
626
|
+
"Stop after implementation report; do not mark tasks complete.",
|
|
627
|
+
"Do not write OpenSpec artifacts or .superspec evidence directly.",
|
|
628
|
+
];
|
|
629
|
+
}
|
|
630
|
+
if (kind === "apply_code_review") {
|
|
631
|
+
return [
|
|
632
|
+
"Review only the task-local executor output and current diff.",
|
|
633
|
+
"Stop after implementation code-review report.",
|
|
634
|
+
"Do not write GREEN evidence or task completion state.",
|
|
635
|
+
];
|
|
636
|
+
}
|
|
637
|
+
return [
|
|
638
|
+
"Verify the accepted worker chain and GREEN evidence only.",
|
|
639
|
+
"Stop after verifier report.",
|
|
640
|
+
"Do not mark tasks complete or enter change-level review.",
|
|
641
|
+
];
|
|
642
|
+
}
|
|
643
|
+
function apply_worker_packet_fingerprint(packet) {
|
|
644
|
+
const { generated_at, guard_fingerprint, block_reasons, apply_worker_chain_refs, apply_worker_chain_active_refs, ...stable } = packet;
|
|
645
|
+
const blockerCodes = Array.isArray(block_reasons) ? block_reasons.map((item) => item.code).sort() : [];
|
|
646
|
+
return fingerprint_obj(without_audit_metadata({ ...stable, blocker_codes: blockerCodes }));
|
|
647
|
+
}
|
|
648
|
+
function finalize_apply_worker_packet(packet) {
|
|
649
|
+
packet.guard_fingerprint = apply_worker_packet_fingerprint(packet);
|
|
650
|
+
return packet;
|
|
651
|
+
}
|
|
652
|
+
function apply_packet_common(ctx, packetKind, taskId, workerChainContext, blockers) {
|
|
653
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
654
|
+
const packet = {
|
|
655
|
+
packet_kind: packetKind,
|
|
656
|
+
change: ctx.change,
|
|
657
|
+
task_id: taskId,
|
|
658
|
+
generated_at: new Date().toISOString(),
|
|
659
|
+
worker_chain_context: workerChainContext,
|
|
660
|
+
worker_state: blockers.length === 0 ? "ready" : "blocked",
|
|
661
|
+
apply_ready: decision_status(applyReady),
|
|
662
|
+
openspec_context_file_refs: collect_change_refs(ctx.changeRoot, [
|
|
663
|
+
"proposal.md",
|
|
664
|
+
"design.md",
|
|
665
|
+
"tasks.md",
|
|
666
|
+
".superspec/artifacts/business-invariants.md",
|
|
667
|
+
".superspec/artifacts/test-contract.md",
|
|
668
|
+
]),
|
|
669
|
+
openspec_dynamic_instructions: openspec_cli_surfaces_for_gate(ctx.change, "task_edit"),
|
|
670
|
+
task_refs: collect_change_refs(ctx.changeRoot, ["tasks.md"]),
|
|
671
|
+
test_contract_refs: collect_change_refs(ctx.changeRoot, [".superspec/artifacts/test-contract.md"]),
|
|
672
|
+
common_worker_report_policy: apply_worker_report_policy(),
|
|
673
|
+
stop_conditions: apply_worker_stop_conditions(packetKind),
|
|
674
|
+
};
|
|
675
|
+
if (blockers.length > 0) {
|
|
676
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
677
|
+
packet.block_reasons = blockers;
|
|
678
|
+
}
|
|
679
|
+
return packet;
|
|
680
|
+
}
|
|
681
|
+
function task_lookup_blockers(ctx, taskId) {
|
|
682
|
+
const task = parse_tasks(ctx.changeRoot)[taskId];
|
|
683
|
+
if (!task)
|
|
684
|
+
return { task: null, blockers: [reason("unknown_task", `task ${taskId} not found`)] };
|
|
685
|
+
return { task, blockers: [] };
|
|
686
|
+
}
|
|
687
|
+
function active_apply_worker_chain_id(ctx, taskId) {
|
|
688
|
+
const active = active_apply_worker_chain(ctx, taskId).active;
|
|
689
|
+
return active ? String(active.apply_worker_chain_id) : null;
|
|
690
|
+
}
|
|
691
|
+
function terminal_apply_worker_chain_valid(ctx, ev, taskId, chainId) {
|
|
692
|
+
const state = String(ev.chain_state ?? "");
|
|
693
|
+
if (state === "closed") {
|
|
694
|
+
return shared_pinned_artifact_ref_reasons(ctx.changeRoot, ev.executor_report_ref, "executor_report_ref", { kind: "worker_report", role: "executor", taskId, chainId }).length === 0
|
|
695
|
+
&& shared_pinned_artifact_ref_reasons(ctx.changeRoot, ev.task_code_review_report_ref, "task_code_review_report_ref", { kind: "worker_report", role: "code-reviewer", taskId, chainId }).length === 0
|
|
696
|
+
&& shared_pinned_artifact_ref_reasons(ctx.changeRoot, ev.verifier_report_ref, "verifier_report_ref", { kind: "worker_report", role: "verifier", taskId, chainId }).length === 0;
|
|
697
|
+
}
|
|
698
|
+
if (state === "abandoned") {
|
|
699
|
+
if ("restored_implementation_fingerprint" in ev && fingerprint_digest(ev.restored_implementation_fingerprint))
|
|
700
|
+
return true;
|
|
701
|
+
if ("serial_takeover_baseline_ref" in ev) {
|
|
702
|
+
return shared_pinned_artifact_ref_reasons(ctx.changeRoot, ev.serial_takeover_baseline_ref, "serial_takeover_baseline_ref", { kind: "status_report", role: "verifier", taskId, chainId }).length === 0
|
|
703
|
+
&& Array.isArray(ev.successor_green_evidence_refs)
|
|
704
|
+
&& ev.successor_green_evidence_refs.length > 0
|
|
705
|
+
&& ev.successor_green_evidence_refs.every((item) => typeof item === "string" && item.length > 0);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
function apply_worker_chain_packet_state(ctx, taskId) {
|
|
711
|
+
const chains = ctx.evidences
|
|
712
|
+
.filter((ev) => isObject(ev) && !ev._invalid && ev.status === "pass" && ev.gate === "task_complete" && ev.kind === "apply_worker_chain" && ev.task_id === taskId)
|
|
713
|
+
.filter((ev) => typeof ev.apply_worker_chain_id === "string" && ev.apply_worker_chain_id);
|
|
714
|
+
const blockers = [];
|
|
715
|
+
const activeByChain = new Map();
|
|
716
|
+
const terminalsByChain = new Map();
|
|
717
|
+
for (const ev of chains) {
|
|
718
|
+
const chainId = String(ev.apply_worker_chain_id ?? "");
|
|
719
|
+
if (String(ev.chain_state ?? "") === "active") {
|
|
720
|
+
const bucket = activeByChain.get(chainId) ?? [];
|
|
721
|
+
bucket.push(ev);
|
|
722
|
+
activeByChain.set(chainId, bucket);
|
|
723
|
+
}
|
|
724
|
+
if (String(ev.chain_state ?? "") === "closed" || String(ev.chain_state ?? "") === "abandoned") {
|
|
725
|
+
const bucket = terminalsByChain.get(chainId) ?? [];
|
|
726
|
+
bucket.push(ev);
|
|
727
|
+
terminalsByChain.set(chainId, bucket);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const validTerminalChainIds = new Set(chains
|
|
731
|
+
.filter((ev) => String(ev.chain_state ?? "") === "closed" || String(ev.chain_state ?? "") === "abandoned")
|
|
732
|
+
.filter((ev) => terminal_apply_worker_chain_valid(ctx, ev, taskId, String(ev.apply_worker_chain_id ?? "")))
|
|
733
|
+
.map((ev) => String(ev.apply_worker_chain_id ?? ""))
|
|
734
|
+
.filter(Boolean));
|
|
735
|
+
const duplicateActives = [...activeByChain.entries()]
|
|
736
|
+
.filter(([chainId]) => !validTerminalChainIds.has(chainId))
|
|
737
|
+
.map(([, items]) => items)
|
|
738
|
+
.filter((items) => items.length > 1)
|
|
739
|
+
.flatMap((items) => items.map((ev) => String(ev.evidence_id ?? ev.apply_worker_chain_id ?? "")))
|
|
740
|
+
.filter(Boolean)
|
|
741
|
+
.sort();
|
|
742
|
+
const activeCandidates = [...activeByChain.entries()]
|
|
743
|
+
.filter(([chainId]) => !validTerminalChainIds.has(chainId))
|
|
744
|
+
.map(([, items]) => items[0])
|
|
745
|
+
.filter(Boolean);
|
|
746
|
+
blockers.push(...apply_worker_chain_lifecycle_reasons(ctx.repoRoot, ctx.changeRoot, ctx.evidences, taskId).filter((item) => (item.code === "apply_worker_chain_active_conflict"
|
|
747
|
+
|| item.code === "apply_worker_chain_terminal_conflict"
|
|
748
|
+
|| item.code === "apply_worker_chain_terminal_invalid"
|
|
749
|
+
|| item.code === "apply_worker_chain_missing_active")));
|
|
750
|
+
const parallelActives = activeCandidates.length > 1
|
|
751
|
+
? activeCandidates.map((ev) => String(ev.evidence_id ?? ev.apply_worker_chain_id ?? "")).filter(Boolean).sort()
|
|
752
|
+
: [];
|
|
753
|
+
const activeConflicts = [...new Set([...duplicateActives, ...parallelActives])].sort();
|
|
754
|
+
if (activeConflicts.length > 0) {
|
|
755
|
+
blockers.push(reason("apply_worker_chain_active_conflict", `task ${taskId} has conflicting active apply worker chain markers: ${renderList(activeConflicts)}`, activeConflicts));
|
|
756
|
+
}
|
|
757
|
+
const duplicateTerminals = [...terminalsByChain.values()]
|
|
758
|
+
.filter((items) => items.length > 1)
|
|
759
|
+
.flatMap((items) => items.map((ev) => String(ev.evidence_id ?? ev.apply_worker_chain_id ?? "")))
|
|
760
|
+
.filter(Boolean)
|
|
761
|
+
.sort();
|
|
762
|
+
if (duplicateTerminals.length > 0) {
|
|
763
|
+
blockers.push(reason("apply_worker_chain_terminal_conflict", `task ${taskId} has duplicate apply worker chain terminal markers: ${renderList(duplicateTerminals)}`, duplicateTerminals));
|
|
764
|
+
}
|
|
765
|
+
const invalidTerminals = chains
|
|
766
|
+
.filter((ev) => String(ev.chain_state ?? "") === "closed" || String(ev.chain_state ?? "") === "abandoned")
|
|
767
|
+
.filter((ev) => !terminal_apply_worker_chain_valid(ctx, ev, taskId, String(ev.apply_worker_chain_id ?? "")))
|
|
768
|
+
.map((ev) => String(ev.evidence_id ?? ev.apply_worker_chain_id ?? ""))
|
|
769
|
+
.filter(Boolean)
|
|
770
|
+
.sort();
|
|
771
|
+
if (invalidTerminals.length > 0) {
|
|
772
|
+
blockers.push(reason("apply_worker_chain_terminal_invalid", `task ${taskId} has invalid apply worker chain terminal markers: ${renderList(invalidTerminals)}`, invalidTerminals));
|
|
773
|
+
}
|
|
774
|
+
const active = activeCandidates[0] ?? null;
|
|
775
|
+
return { active: blockers.length === 0 ? active : null, blockers };
|
|
776
|
+
}
|
|
777
|
+
function active_apply_worker_chain(ctx, taskId) {
|
|
778
|
+
return apply_worker_chain_packet_state(ctx, taskId);
|
|
779
|
+
}
|
|
780
|
+
function require_active_apply_worker_chain(ctx, taskId, blockers) {
|
|
781
|
+
const state = active_apply_worker_chain(ctx, taskId);
|
|
782
|
+
blockers.push(...state.blockers);
|
|
783
|
+
const active = state.active;
|
|
784
|
+
const chainId = active ? String(active.apply_worker_chain_id) : null;
|
|
785
|
+
if (!chainId) {
|
|
786
|
+
blockers.push(reason("missing_apply_worker_chain_active", `task ${taskId} requires an active apply_worker_chain marker before downstream worker packet generation`));
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
blockers.push(...pre_edit_evidence_ref_reasons(ctx.evidences, active, taskId, "apply_worker_chain_active_invalid"));
|
|
790
|
+
}
|
|
791
|
+
return chainId;
|
|
792
|
+
}
|
|
793
|
+
function generated_apply_worker_chain_id(ctx, taskId, writeScope = []) {
|
|
794
|
+
const digest = sha256_text(`${ctx.change}\n${taskId}\n${writeScope.join("\n")}`).slice("sha256:".length, "sha256:".length + 16);
|
|
795
|
+
const base = `CHAIN-${taskId}-${digest}`;
|
|
796
|
+
const used = new Set(ctx.evidences
|
|
797
|
+
.filter((ev) => isObject(ev) && ev.kind === "apply_worker_chain" && ev.task_id === taskId && typeof ev.apply_worker_chain_id === "string")
|
|
798
|
+
.map((ev) => String(ev.apply_worker_chain_id)));
|
|
799
|
+
if (!used.has(base))
|
|
800
|
+
return base;
|
|
801
|
+
for (let idx = 2; idx < 1000; idx += 1) {
|
|
802
|
+
const candidate = `${base}-R${idx}`;
|
|
803
|
+
if (!used.has(candidate))
|
|
804
|
+
return candidate;
|
|
805
|
+
}
|
|
806
|
+
return `${base}-R${sha256_text([...used].sort().join("\n")).slice("sha256:".length, "sha256:".length + 8)}`;
|
|
807
|
+
}
|
|
808
|
+
function implementation_fingerprint(ctx, declaredTaskWriteScope = []) {
|
|
809
|
+
return apply_worker_implementation_fingerprint(ctx.repoRoot, ctx.changeRoot, [], { declaredTaskWriteScope });
|
|
810
|
+
}
|
|
811
|
+
function read_json_ref(ctx, ref) {
|
|
812
|
+
const target = safe_within(ctx.changeRoot, ref);
|
|
813
|
+
if (target === null)
|
|
814
|
+
return { value: null, blockers: [reason("ref_path_unsafe", `ref escapes change root: ${ref}`, [ref])] };
|
|
815
|
+
if (!existsSync(target) || !statSync(target).isFile())
|
|
816
|
+
return { value: null, blockers: [reason("ref_not_readable", `ref is not readable: ${ref}`, [ref])] };
|
|
817
|
+
if (statSync(target).size <= 0)
|
|
818
|
+
return { value: null, blockers: [reason("ref_empty", `ref is empty: ${ref}`, [ref])] };
|
|
819
|
+
try {
|
|
820
|
+
const value = JSON.parse(readFileSync(target, "utf8"));
|
|
821
|
+
return isObject(value) ? { value, blockers: [] } : { value: null, blockers: [reason("ref_type_mismatch", `ref must contain a JSON object: ${ref}`, [ref])] };
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
return { value: null, blockers: [reason("ref_type_mismatch", `ref must contain valid JSON: ${ref}`, [ref])] };
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function validate_pinned_artifact_ref_object(ctx, item, label, expected, code = "pinned_artifact_ref_invalid") {
|
|
828
|
+
return shared_pinned_artifact_ref_reasons(ctx.changeRoot, item, label, {
|
|
829
|
+
kind: expected.kind ?? "worker_report",
|
|
830
|
+
role: expected.role,
|
|
831
|
+
taskId: expected.taskId,
|
|
832
|
+
chainId: expected.chainId,
|
|
833
|
+
}, code);
|
|
834
|
+
}
|
|
835
|
+
function validate_pinned_artifact_ref(ctx, ref, expected) {
|
|
836
|
+
const loaded = read_json_ref(ctx, ref);
|
|
837
|
+
if (loaded.blockers.length > 0 || !loaded.value)
|
|
838
|
+
return { ref: null, blockers: loaded.blockers };
|
|
839
|
+
const item = loaded.value;
|
|
840
|
+
const blockers = validate_pinned_artifact_ref_object(ctx, item, ref, expected);
|
|
841
|
+
return { ref: blockers.length === 0 ? item : null, blockers };
|
|
842
|
+
}
|
|
843
|
+
function validate_worker_report_refs(ctx, refs, expected) {
|
|
844
|
+
if (refs.length === 0)
|
|
845
|
+
return { refs: [], blockers: [reason(expected.missingCode, expected.missingMessage)] };
|
|
846
|
+
const resolved = [];
|
|
847
|
+
const blockers = [];
|
|
848
|
+
for (const ref of refs) {
|
|
849
|
+
const result = validate_pinned_artifact_ref(ctx, ref, { role: expected.role, taskId: expected.taskId, chainId: expected.chainId });
|
|
850
|
+
blockers.push(...result.blockers);
|
|
851
|
+
if (result.ref)
|
|
852
|
+
resolved.push(result.ref);
|
|
853
|
+
}
|
|
854
|
+
return { refs: resolved, blockers };
|
|
855
|
+
}
|
|
856
|
+
function worker_report_origin_blockers(refItem, expectedOrigin, label) {
|
|
857
|
+
if (!isObject(refItem) || typeof expectedOrigin !== "string" || !expectedOrigin.startsWith("sha256:"))
|
|
858
|
+
return [];
|
|
859
|
+
return refItem.origin_packet_fingerprint === expectedOrigin
|
|
860
|
+
? []
|
|
861
|
+
: [reason("worker_report_origin_mismatch", `${label} origin_packet_fingerprint must match active apply_worker_chain executor_packet_fingerprint`, [])];
|
|
862
|
+
}
|
|
863
|
+
function worker_report_input_blockers(refItem, refs, label) {
|
|
864
|
+
if (!isObject(refItem))
|
|
865
|
+
return [];
|
|
866
|
+
const expected = worker_input_ref_digest(refs);
|
|
867
|
+
return refItem.input_ref_digest === expected
|
|
868
|
+
? []
|
|
869
|
+
: [reason("worker_report_input_ref_mismatch", `${label} input_ref_digest must match upstream refs`, [])];
|
|
870
|
+
}
|
|
871
|
+
function worker_report_input_digest_blockers(refItem, expectedDigest, label) {
|
|
872
|
+
if (!isObject(refItem))
|
|
873
|
+
return [];
|
|
874
|
+
return refItem.input_ref_digest === expectedDigest
|
|
875
|
+
? []
|
|
876
|
+
: [reason("worker_report_input_ref_mismatch", `${label} input_ref_digest must match active executor packet inputs`, [])];
|
|
877
|
+
}
|
|
878
|
+
function code_review_executor_binding_blockers(ctx, codeReviewRef, activeChain, taskId, chainId) {
|
|
879
|
+
const blockers = [];
|
|
880
|
+
const report = read_pinned_artifact_json(ctx.changeRoot, codeReviewRef);
|
|
881
|
+
if (!report)
|
|
882
|
+
return [reason("worker_report_content_invalid", "task_code_review_report_ref content must be readable before spawning GREEN test-runner", [])];
|
|
883
|
+
const executorReportRef = report.executor_report_ref;
|
|
884
|
+
blockers.push(...validate_pinned_artifact_ref_object(ctx, executorReportRef, "task_code_review_report_ref.executor_report_ref", {
|
|
885
|
+
role: "executor",
|
|
886
|
+
taskId,
|
|
887
|
+
chainId,
|
|
888
|
+
}));
|
|
889
|
+
if (isObject(executorReportRef)) {
|
|
890
|
+
blockers.push(...worker_report_origin_blockers(executorReportRef, activeChain?.executor_packet_fingerprint, "task_code_review_report_ref.executor_report_ref"));
|
|
891
|
+
if (activeChain) {
|
|
892
|
+
blockers.push(...worker_report_input_digest_blockers(executorReportRef, apply_worker_executor_input_ref_digest(ctx.evidences, activeChain), "task_code_review_report_ref.executor_report_ref"));
|
|
893
|
+
}
|
|
894
|
+
blockers.push(...worker_report_input_blockers(codeReviewRef, [executorReportRef], "task_code_review_report_ref"));
|
|
895
|
+
}
|
|
896
|
+
return blockers;
|
|
897
|
+
}
|
|
898
|
+
function validate_executor_worker_test_run_refs(ctx, ev, ref, taskId, chainId) {
|
|
899
|
+
return worker_test_run_reasons(ctx.changeRoot, ev, taskId, chainId, "pinned_evidence_ref_invalid")
|
|
900
|
+
.map((item) => ({ ...item, refs: [ref] }));
|
|
901
|
+
}
|
|
902
|
+
function validate_test_run_evidence_refs(ctx, refs, expected) {
|
|
903
|
+
if (refs.length === 0)
|
|
904
|
+
return { refs: [], blockers: [reason(expected.missingCode, expected.missingMessage)] };
|
|
905
|
+
const resolved = [];
|
|
906
|
+
const blockers = [];
|
|
907
|
+
for (const ref of refs) {
|
|
908
|
+
let evidenceId = ref;
|
|
909
|
+
const maybePath = safe_within(ctx.changeRoot, ref);
|
|
910
|
+
if (maybePath !== null && existsSync(maybePath) && statSync(maybePath).isFile()) {
|
|
911
|
+
const loaded = read_json_ref(ctx, ref);
|
|
912
|
+
blockers.push(...loaded.blockers);
|
|
913
|
+
if (loaded.value) {
|
|
914
|
+
if (loaded.value.kind !== "test_run")
|
|
915
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: kind must be test_run`, [ref]));
|
|
916
|
+
if (normalize_gate(String(loaded.value.gate ?? "")) !== expected.gate)
|
|
917
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: gate must be ${expected.gate}`, [ref]));
|
|
918
|
+
if (loaded.value.task_id !== expected.taskId)
|
|
919
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: task_id must be ${expected.taskId}`, [ref]));
|
|
920
|
+
if (loaded.value.phase !== undefined && loaded.value.phase !== expected.phase)
|
|
921
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: phase must be ${expected.phase}`, [ref]));
|
|
922
|
+
if (loaded.value.semantic_status !== expected.semanticStatus)
|
|
923
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: semantic_status must be ${expected.semanticStatus}`, [ref]));
|
|
924
|
+
if (expected.chainId && loaded.value.apply_worker_chain_id !== expected.chainId)
|
|
925
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: apply_worker_chain_id mismatch`, [ref]));
|
|
926
|
+
if (typeof loaded.value.evidence_id === "string" && loaded.value.evidence_id)
|
|
927
|
+
evidenceId = loaded.value.evidence_id;
|
|
928
|
+
if (expected.chainId)
|
|
929
|
+
blockers.push(...validate_executor_worker_test_run_refs(ctx, loaded.value, ref, expected.taskId, expected.chainId));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
const ev = live_pass(ctx.evidences, { kind: "test_run", task_id: expected.taskId })
|
|
933
|
+
.find((item) => String(item.evidence_id ?? "") === evidenceId);
|
|
934
|
+
if (!ev) {
|
|
935
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `test_run evidence ref is not live/pass for ${expected.taskId}: ${ref}`, [ref]));
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
if (normalize_gate(String(ev.gate ?? "")) !== expected.gate)
|
|
939
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: gate must be ${expected.gate}`, [ref]));
|
|
940
|
+
if (ev.semantic_status !== expected.semanticStatus)
|
|
941
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: semantic_status must be ${expected.semanticStatus}`, [ref]));
|
|
942
|
+
if (ev.phase !== undefined && ev.phase !== expected.phase)
|
|
943
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: phase must be ${expected.phase}`, [ref]));
|
|
944
|
+
if (expected.chainId) {
|
|
945
|
+
if (ev.apply_execution_chain !== "executor_worker")
|
|
946
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: apply_execution_chain must be executor_worker`, [ref]));
|
|
947
|
+
if (ev.apply_worker_chain_id !== expected.chainId)
|
|
948
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: apply_worker_chain_id mismatch`, [ref]));
|
|
949
|
+
blockers.push(...validate_executor_worker_test_run_refs(ctx, ev, ref, expected.taskId, expected.chainId));
|
|
950
|
+
}
|
|
951
|
+
resolved.push(ev);
|
|
952
|
+
}
|
|
953
|
+
return { refs: resolved, blockers };
|
|
954
|
+
}
|
|
955
|
+
function validate_active_apply_worker_chain_refs(ctx, refs, taskId, expectedChainId, expectedPacketFingerprint, expectedSourceImplementationFingerprint, expectedDeclaredTaskWriteScope, expectedPreEditEvidenceRefs) {
|
|
956
|
+
if (refs.length === 0) {
|
|
957
|
+
return {
|
|
958
|
+
refs: [],
|
|
959
|
+
blockers: [reason("missing_apply_worker_chain_ref", "apply-executor-packet --format prompt requires --apply-worker-chain-ref pointing at the recorded active apply_worker_chain evidence")],
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
const resolved = [];
|
|
963
|
+
const blockers = [];
|
|
964
|
+
const hasTerminal = ctx.evidences.some((ev) => isObject(ev)
|
|
965
|
+
&& !ev._invalid
|
|
966
|
+
&& ev.status === "pass"
|
|
967
|
+
&& ev.gate === "task_complete"
|
|
968
|
+
&& ev.kind === "apply_worker_chain"
|
|
969
|
+
&& ev.task_id === taskId
|
|
970
|
+
&& ev.apply_worker_chain_id === expectedChainId
|
|
971
|
+
&& (ev.chain_state === "closed" || ev.chain_state === "abandoned"));
|
|
972
|
+
if (hasTerminal) {
|
|
973
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `apply_worker_chain ${expectedChainId} already has a terminal marker and cannot authorize executor spawn`, [expectedChainId]));
|
|
974
|
+
}
|
|
975
|
+
const stringList = (value) => Array.isArray(value)
|
|
976
|
+
? value.map(String).filter(Boolean).sort()
|
|
977
|
+
: [];
|
|
978
|
+
const activeListBindingBlockers = (value, ref) => {
|
|
979
|
+
const fieldBlockers = [];
|
|
980
|
+
if (!deepEqual(stringList(value.declared_task_write_scope), stringList(expectedDeclaredTaskWriteScope))) {
|
|
981
|
+
fieldBlockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: declared_task_write_scope mismatch`, [ref]));
|
|
982
|
+
}
|
|
983
|
+
if (!deepEqual(stringList(value.pre_edit_evidence_refs), stringList(expectedPreEditEvidenceRefs))) {
|
|
984
|
+
fieldBlockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: pre_edit_evidence_refs mismatch`, [ref]));
|
|
985
|
+
}
|
|
986
|
+
return fieldBlockers;
|
|
987
|
+
};
|
|
988
|
+
for (const ref of refs) {
|
|
989
|
+
let evidenceId = ref;
|
|
990
|
+
const maybePath = safe_within(ctx.changeRoot, ref);
|
|
991
|
+
if (maybePath !== null && existsSync(maybePath) && statSync(maybePath).isFile()) {
|
|
992
|
+
const loaded = read_json_ref(ctx, ref);
|
|
993
|
+
blockers.push(...loaded.blockers);
|
|
994
|
+
if (loaded.value) {
|
|
995
|
+
if (loaded.value.kind !== "apply_worker_chain")
|
|
996
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: kind must be apply_worker_chain`, [ref]));
|
|
997
|
+
if (normalize_gate(String(loaded.value.gate ?? "")) !== "task_complete")
|
|
998
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: gate must be task_complete`, [ref]));
|
|
999
|
+
if (loaded.value.task_id !== taskId)
|
|
1000
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: task_id must be ${taskId}`, [ref]));
|
|
1001
|
+
if (loaded.value.chain_state !== "active")
|
|
1002
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: chain_state must be active`, [ref]));
|
|
1003
|
+
if (loaded.value.apply_worker_chain_id !== expectedChainId)
|
|
1004
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: apply_worker_chain_id mismatch`, [ref]));
|
|
1005
|
+
if (loaded.value.executor_packet_fingerprint !== expectedPacketFingerprint)
|
|
1006
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: executor_packet_fingerprint mismatch`, [ref]));
|
|
1007
|
+
if (!fingerprint_matches(loaded.value.source_implementation_fingerprint, expectedSourceImplementationFingerprint)) {
|
|
1008
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: source_implementation_fingerprint mismatch`, [ref]));
|
|
1009
|
+
}
|
|
1010
|
+
blockers.push(...activeListBindingBlockers(loaded.value, ref));
|
|
1011
|
+
if (typeof loaded.value.evidence_id === "string" && loaded.value.evidence_id)
|
|
1012
|
+
evidenceId = loaded.value.evidence_id;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
const ev = live_pass(ctx.evidences, { gate: "task_complete", kind: "apply_worker_chain", task_id: taskId })
|
|
1016
|
+
.find((item) => String(item.evidence_id ?? "") === evidenceId);
|
|
1017
|
+
if (!ev) {
|
|
1018
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `apply_worker_chain ref is not live/pass for ${taskId}: ${ref}`, [ref]));
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
if (ev.chain_state !== "active")
|
|
1022
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: chain_state must be active`, [ref]));
|
|
1023
|
+
if (ev.apply_worker_chain_id !== expectedChainId)
|
|
1024
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: apply_worker_chain_id mismatch`, [ref]));
|
|
1025
|
+
if (ev.executor_packet_fingerprint !== expectedPacketFingerprint)
|
|
1026
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: executor_packet_fingerprint mismatch`, [ref]));
|
|
1027
|
+
if (!fingerprint_matches(ev.source_implementation_fingerprint, expectedSourceImplementationFingerprint)) {
|
|
1028
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: source_implementation_fingerprint mismatch`, [ref]));
|
|
1029
|
+
}
|
|
1030
|
+
blockers.push(...activeListBindingBlockers(ev, ref));
|
|
1031
|
+
resolved.push(ev);
|
|
1032
|
+
}
|
|
1033
|
+
return { refs: resolved, blockers };
|
|
1034
|
+
}
|
|
1035
|
+
function pre_edit_refs_bound_to_active_chain(ctx, active, refs, label, taskId) {
|
|
1036
|
+
const allowed = new Set(Array.isArray(active?.pre_edit_evidence_refs) ? active.pre_edit_evidence_refs.map(String) : []);
|
|
1037
|
+
if (allowed.size === 0)
|
|
1038
|
+
return [reason("pinned_evidence_ref_invalid", `task ${taskId} active apply_worker_chain has no pre_edit_evidence_refs`)];
|
|
1039
|
+
const blockers = [];
|
|
1040
|
+
for (const ev of refs) {
|
|
1041
|
+
const evidenceId = String(ev.evidence_id ?? "");
|
|
1042
|
+
if (!allowed.has(evidenceId))
|
|
1043
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${label} evidence ref is not part of active apply_worker_chain pre_edit_evidence_refs: ${evidenceId}`, [evidenceId]));
|
|
1044
|
+
}
|
|
1045
|
+
blockers.push(...pre_edit_evidence_ref_reasons(ctx.evidences, active, taskId, "pinned_evidence_ref_invalid"));
|
|
1046
|
+
return blockers;
|
|
1047
|
+
}
|
|
1048
|
+
function write_scope_blockers(taskId, writeScope) {
|
|
1049
|
+
const blockers = [];
|
|
1050
|
+
if (writeScope.length === 0) {
|
|
1051
|
+
blockers.push(reason("missing_write_scope", `task ${taskId} requires declared write_scope before executor worker handoff`));
|
|
1052
|
+
return blockers;
|
|
1053
|
+
}
|
|
1054
|
+
const forbidden = writeScope.filter((item) => item === "."
|
|
1055
|
+
|| item === ""
|
|
1056
|
+
|| item.startsWith("/")
|
|
1057
|
+
|| item.includes("..")
|
|
1058
|
+
|| item === "proposal.md"
|
|
1059
|
+
|| item === "design.md"
|
|
1060
|
+
|| item === "tasks.md"
|
|
1061
|
+
|| item.startsWith("specs/")
|
|
1062
|
+
|| item.startsWith(".superspec/"));
|
|
1063
|
+
if (forbidden.length > 0) {
|
|
1064
|
+
blockers.push(reason("unsafe_write_scope", `task ${taskId} has unsafe executor write_scope entries: ${renderList(forbidden)}`, forbidden));
|
|
1065
|
+
}
|
|
1066
|
+
return blockers;
|
|
1067
|
+
}
|
|
1068
|
+
function pre_edit_evidence_refs(ctx, taskId) {
|
|
1069
|
+
return live_pass(ctx.evidences, { gate: "task_edit", kind: "test_run", task_id: taskId })
|
|
1070
|
+
.filter((ev) => ev.semantic_status === "expected_failure" || ev.semantic_status === "expected_success")
|
|
1071
|
+
.map((ev) => String(ev.evidence_id ?? ""))
|
|
1072
|
+
.filter(Boolean)
|
|
1073
|
+
.sort();
|
|
1074
|
+
}
|
|
1075
|
+
function apply_task_context_fields(ctx, taskId, task, writeScope, expectedGuardRefs = []) {
|
|
1076
|
+
return {
|
|
1077
|
+
task_content_ref: change_pinned_ref(ctx.changeRoot, "tasks.md"),
|
|
1078
|
+
task_acceptance_refs: task ? splitList(task.attrs.requirement_refs ?? "") : [],
|
|
1079
|
+
task_invariant_refs: task ? splitList(task.attrs.invariant_refs ?? "") : [],
|
|
1080
|
+
task_test_refs: task ? splitList(task.attrs.test_refs ?? "") : [],
|
|
1081
|
+
business_invariant_refs: collect_change_refs(ctx.changeRoot, [".superspec/artifacts/business-invariants.md"]),
|
|
1082
|
+
test_contract_refs: collect_change_refs(ctx.changeRoot, [".superspec/artifacts/test-contract.md"]),
|
|
1083
|
+
pre_edit_evidence_refs: pre_edit_evidence_refs(ctx, taskId),
|
|
1084
|
+
current_worktree_refs: dirty_repo_refs(ctx, `apply worker current worktree refs for ${taskId}`),
|
|
1085
|
+
protected_path_refs: apply_worker_protected_path_refs(ctx.repoRoot, ctx.changeRoot, expectedGuardRefs),
|
|
1086
|
+
scope_diff_review_policy: {
|
|
1087
|
+
declared_task_write_scope: writeScope,
|
|
1088
|
+
forbidden_paths: ["proposal.md", "design.md", "tasks.md", "specs/", ".superspec/"],
|
|
1089
|
+
require_scope_verdict: true,
|
|
1090
|
+
mismatch_behavior: "fail_closed",
|
|
1091
|
+
},
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
function apply_test_packet(ctx, args) {
|
|
1095
|
+
const taskId = args.task_id ?? "";
|
|
1096
|
+
const testId = args.test_id ?? "";
|
|
1097
|
+
const phase = args.phase ?? "red";
|
|
1098
|
+
const blockers = [];
|
|
1099
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
1100
|
+
if (!applyReady.allowed)
|
|
1101
|
+
blockers.push(...decision_reasons(applyReady));
|
|
1102
|
+
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1103
|
+
blockers.push(...taskBlockers);
|
|
1104
|
+
const declaredTests = new Set(task ? splitList(task.attrs.test_refs ?? "") : []);
|
|
1105
|
+
if (task && !declaredTests.has(testId)) {
|
|
1106
|
+
blockers.push(reason("test_not_declared_for_task", `test ${testId} is not declared in task ${taskId} test_refs`, [testId]));
|
|
1107
|
+
}
|
|
1108
|
+
const [config, configProblems] = load_config(ctx.repoRoot, ctx.changeRoot);
|
|
1109
|
+
blockers.push(...configProblems);
|
|
1110
|
+
const command = resolve_test_contract_command(ctx.changeRoot, testId, config);
|
|
1111
|
+
blockers.push(...command.blockers);
|
|
1112
|
+
const invariantRefs = [...(test_contract_invariant_refs_by_test(ctx.changeRoot).get(testId) ?? new Set())].sort();
|
|
1113
|
+
if (invariantRefs.length === 0)
|
|
1114
|
+
blockers.push(reason("missing_invariant_refs", `test ${testId} requires invariant refs in test contract`, [testId]));
|
|
1115
|
+
if (phase === "green") {
|
|
1116
|
+
const taskEdit = evaluate_workflow_decision(ctx, "task_edit", taskId);
|
|
1117
|
+
if (!taskEdit.allowed)
|
|
1118
|
+
blockers.push(...decision_reasons(taskEdit));
|
|
1119
|
+
if ((args.task_code_review_report_refs ?? []).length === 0) {
|
|
1120
|
+
blockers.push(reason("missing_task_code_review_report_ref", "apply-test-packet --phase green requires --task-code-review-report-ref before spawning test-runner"));
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
else if ((args.task_code_review_report_refs ?? []).length > 0) {
|
|
1124
|
+
blockers.push(reason("unexpected_task_code_review_report_ref", "apply-test-packet --phase red/characterization must not consume --task-code-review-report-ref"));
|
|
1125
|
+
}
|
|
1126
|
+
const workerChainContext = phase === "green" ? "executor_worker" : "none";
|
|
1127
|
+
const packet = apply_packet_common(ctx, "apply_test", taskId, workerChainContext, blockers);
|
|
1128
|
+
packet.test_id = testId;
|
|
1129
|
+
packet.phase = phase;
|
|
1130
|
+
packet.expected_semantic_status = phase === "red" ? "expected_failure" : "expected_success";
|
|
1131
|
+
packet.test_runner_report_required_fields = [...APPLY_TEST_RUNNER_REPORT_REQUIRED_FIELDS];
|
|
1132
|
+
packet.required_invariant_refs = invariantRefs;
|
|
1133
|
+
packet.allowed_test_command = command.command;
|
|
1134
|
+
packet.test_command_source = command.source;
|
|
1135
|
+
packet.expected_worktree_side_effects = [];
|
|
1136
|
+
if (command.command_ref)
|
|
1137
|
+
packet.test_command_ref = command.command_ref;
|
|
1138
|
+
if (command.expected_failure_signature)
|
|
1139
|
+
packet.expected_failure_signature = command.expected_failure_signature;
|
|
1140
|
+
if (command.expected_failure_classifier)
|
|
1141
|
+
packet.expected_failure_classifier = command.expected_failure_classifier;
|
|
1142
|
+
if (workerChainContext === "executor_worker") {
|
|
1143
|
+
const chainId = require_active_apply_worker_chain(ctx, taskId, blockers);
|
|
1144
|
+
if (chainId) {
|
|
1145
|
+
const reviewRefs = validate_worker_report_refs(ctx, args.task_code_review_report_refs ?? [], {
|
|
1146
|
+
role: "code-reviewer",
|
|
1147
|
+
taskId,
|
|
1148
|
+
chainId,
|
|
1149
|
+
missingCode: "missing_task_code_review_report_ref",
|
|
1150
|
+
missingMessage: "apply-test-packet --phase green requires --task-code-review-report-ref before spawning test-runner",
|
|
1151
|
+
});
|
|
1152
|
+
blockers.push(...reviewRefs.blockers);
|
|
1153
|
+
packet.apply_worker_chain_id = chainId;
|
|
1154
|
+
packet.task_code_review_report_pinned_refs = reviewRefs.refs;
|
|
1155
|
+
packet.task_code_review_report_refs = args.task_code_review_report_refs ?? [];
|
|
1156
|
+
const activeChain = active_apply_worker_chain(ctx, taskId).active;
|
|
1157
|
+
if (reviewRefs.refs[0]) {
|
|
1158
|
+
const reviewReport = read_pinned_artifact_json(ctx.changeRoot, reviewRefs.refs[0]);
|
|
1159
|
+
if (isObject(reviewReport?.executor_report_ref))
|
|
1160
|
+
packet.executor_report_pinned_refs = [reviewReport.executor_report_ref];
|
|
1161
|
+
blockers.push(...code_review_executor_binding_blockers(ctx, reviewRefs.refs[0], activeChain, taskId, chainId));
|
|
1162
|
+
}
|
|
1163
|
+
const activeScope = Array.isArray(activeChain?.declared_task_write_scope) ? activeChain.declared_task_write_scope.map(String).filter(Boolean) : [];
|
|
1164
|
+
const currentImplementation = apply_worker_implementation_fingerprint(ctx.repoRoot, ctx.changeRoot, reviewRefs.refs, { declaredTaskWriteScope: activeScope });
|
|
1165
|
+
packet.post_code_review_worktree_fingerprint = currentImplementation;
|
|
1166
|
+
const observedDigest = fingerprint_digest(reviewRefs.refs[0]?.observed_implementation_fingerprint);
|
|
1167
|
+
if (!observedDigest) {
|
|
1168
|
+
blockers.push(reason("post_code_review_worktree_fingerprint_missing", "apply-test-packet --phase green requires code-review report observed_implementation_fingerprint"));
|
|
1169
|
+
}
|
|
1170
|
+
else if (observedDigest !== currentImplementation.fingerprint_digest) {
|
|
1171
|
+
blockers.push(reason("post_code_review_worktree_fingerprint_mismatch", "apply-test-packet --phase green requires code-review report observed implementation fingerprint to match current worktree"));
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
else if ((args.task_code_review_report_refs ?? []).length === 0) {
|
|
1175
|
+
blockers.push(reason("missing_task_code_review_report_ref", "apply-test-packet --phase green requires --task-code-review-report-ref before spawning test-runner"));
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
1179
|
+
if (blockers.length > 0) {
|
|
1180
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
1181
|
+
packet.block_reasons = blockers;
|
|
1182
|
+
}
|
|
1183
|
+
return finalize_apply_worker_packet(packet);
|
|
1184
|
+
}
|
|
1185
|
+
function apply_executor_packet(ctx, args) {
|
|
1186
|
+
const taskId = args.task_id ?? "";
|
|
1187
|
+
const blockers = [];
|
|
1188
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
1189
|
+
if (!applyReady.allowed)
|
|
1190
|
+
blockers.push(...decision_reasons(applyReady));
|
|
1191
|
+
const taskEdit = evaluate_workflow_decision(ctx, "task_edit", taskId);
|
|
1192
|
+
if (!taskEdit.allowed)
|
|
1193
|
+
blockers.push(...decision_reasons(taskEdit));
|
|
1194
|
+
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1195
|
+
blockers.push(...taskBlockers);
|
|
1196
|
+
const writeScope = task ? splitList(task.attrs.write_scope ?? "") : [];
|
|
1197
|
+
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1198
|
+
if (task && (task.attrs.tdd_required ?? "true").toLowerCase() === "false") {
|
|
1199
|
+
blockers.push(reason("unsupported_executor_tdd_mode", `task ${taskId} has tdd_required:false and must stay on main-thread apply path`));
|
|
1200
|
+
}
|
|
1201
|
+
blockers.push(...apply_worker_chain_packet_state(ctx, taskId).blockers);
|
|
1202
|
+
const packet = apply_packet_common(ctx, "apply_executor", taskId, "executor_worker", blockers);
|
|
1203
|
+
const chainId = active_apply_worker_chain_id(ctx, taskId) ?? generated_apply_worker_chain_id(ctx, taskId, writeScope);
|
|
1204
|
+
packet.worker_chain_context = "executor_worker";
|
|
1205
|
+
packet.apply_worker_chain_id = chainId;
|
|
1206
|
+
packet.declared_task_write_scope = writeScope;
|
|
1207
|
+
packet.task_edit = decision_status(taskEdit);
|
|
1208
|
+
packet.task_complete = safe_decision_status(ctx, "task_complete", taskId);
|
|
1209
|
+
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope));
|
|
1210
|
+
packet.required_test_refs = task ? splitList(task.attrs.test_refs ?? "") : [];
|
|
1211
|
+
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1212
|
+
packet.executor_runtime_policy = {
|
|
1213
|
+
initial_wait_seconds: 60,
|
|
1214
|
+
max_running_seconds: 7200,
|
|
1215
|
+
status_request_after_seconds: 300,
|
|
1216
|
+
max_silent_seconds: 900,
|
|
1217
|
+
stale_after_seconds: 1800,
|
|
1218
|
+
};
|
|
1219
|
+
packet.chain_activation_template = {
|
|
1220
|
+
kind: "apply_worker_chain",
|
|
1221
|
+
gate: "task_complete",
|
|
1222
|
+
status: "pass",
|
|
1223
|
+
task_id: taskId,
|
|
1224
|
+
apply_worker_chain_id: chainId,
|
|
1225
|
+
chain_state: "active",
|
|
1226
|
+
executor_packet_fingerprint: "",
|
|
1227
|
+
source_implementation_fingerprint: implementation_fingerprint(ctx, writeScope),
|
|
1228
|
+
declared_task_write_scope: writeScope,
|
|
1229
|
+
pre_edit_evidence_refs: pre_edit_evidence_refs(ctx, taskId),
|
|
1230
|
+
};
|
|
1231
|
+
if (args.packet_format === "prompt") {
|
|
1232
|
+
const expectedPacketFingerprint = apply_worker_packet_fingerprint(packet);
|
|
1233
|
+
const activeRefs = validate_active_apply_worker_chain_refs(ctx, args.apply_worker_chain_refs ?? [], taskId, chainId, expectedPacketFingerprint, packet.chain_activation_template.source_implementation_fingerprint, packet.chain_activation_template.declared_task_write_scope, packet.chain_activation_template.pre_edit_evidence_refs);
|
|
1234
|
+
blockers.push(...activeRefs.blockers);
|
|
1235
|
+
packet.apply_worker_chain_refs = args.apply_worker_chain_refs ?? [];
|
|
1236
|
+
packet.apply_worker_chain_active_refs = activeRefs.refs;
|
|
1237
|
+
}
|
|
1238
|
+
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
1239
|
+
if (blockers.length > 0) {
|
|
1240
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
1241
|
+
packet.block_reasons = blockers;
|
|
1242
|
+
}
|
|
1243
|
+
finalize_apply_worker_packet(packet);
|
|
1244
|
+
packet.chain_activation_template.executor_packet_fingerprint = packet.guard_fingerprint;
|
|
1245
|
+
return packet;
|
|
1246
|
+
}
|
|
1247
|
+
function apply_code_review_packet(ctx, args) {
|
|
1248
|
+
const taskId = args.task_id ?? "";
|
|
1249
|
+
const blockers = [];
|
|
1250
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
1251
|
+
if (!applyReady.allowed)
|
|
1252
|
+
blockers.push(...decision_reasons(applyReady));
|
|
1253
|
+
const taskEdit = evaluate_workflow_decision(ctx, "task_edit", taskId);
|
|
1254
|
+
if (!taskEdit.allowed)
|
|
1255
|
+
blockers.push(...decision_reasons(taskEdit));
|
|
1256
|
+
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1257
|
+
blockers.push(...taskBlockers);
|
|
1258
|
+
const writeScope = task ? splitList(task.attrs.write_scope ?? "") : [];
|
|
1259
|
+
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1260
|
+
const chainId = require_active_apply_worker_chain(ctx, taskId, blockers);
|
|
1261
|
+
const activeChain = active_apply_worker_chain(ctx, taskId).active;
|
|
1262
|
+
if (chainId) {
|
|
1263
|
+
const executorRefs = validate_worker_report_refs(ctx, args.executor_report_refs ?? [], {
|
|
1264
|
+
role: "executor",
|
|
1265
|
+
taskId,
|
|
1266
|
+
chainId,
|
|
1267
|
+
missingCode: "missing_executor_report_ref",
|
|
1268
|
+
missingMessage: "apply-code-review-packet requires --executor-report-ref",
|
|
1269
|
+
});
|
|
1270
|
+
blockers.push(...executorRefs.blockers);
|
|
1271
|
+
if (executorRefs.refs[0]) {
|
|
1272
|
+
blockers.push(...worker_report_origin_blockers(executorRefs.refs[0], activeChain?.executor_packet_fingerprint, "executor_report_ref"));
|
|
1273
|
+
if (activeChain) {
|
|
1274
|
+
blockers.push(...worker_report_input_digest_blockers(executorRefs.refs[0], apply_worker_executor_input_ref_digest(ctx.evidences, activeChain), "executor_report_ref"));
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
const packet = apply_packet_common(ctx, "apply_code_review", taskId, "executor_worker", blockers);
|
|
1278
|
+
packet.apply_worker_chain_id = chainId;
|
|
1279
|
+
packet.declared_task_write_scope = writeScope;
|
|
1280
|
+
packet.executor_report_pinned_refs = executorRefs.refs;
|
|
1281
|
+
packet.executor_report_refs = args.executor_report_refs ?? [];
|
|
1282
|
+
packet.expected_executor_origin_packet_fingerprint = activeChain?.executor_packet_fingerprint;
|
|
1283
|
+
if (activeChain)
|
|
1284
|
+
packet.expected_executor_input_ref_digest = apply_worker_executor_input_ref_digest(ctx.evidences, activeChain);
|
|
1285
|
+
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1286
|
+
packet.code_review_report_required_fields = [...APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS];
|
|
1287
|
+
packet.task_edit = decision_status(taskEdit);
|
|
1288
|
+
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope, executorRefs.refs));
|
|
1289
|
+
packet.code_review_checks = [
|
|
1290
|
+
"executor_report_matches_current_diff",
|
|
1291
|
+
"changed_files_within_declared_task_write_scope",
|
|
1292
|
+
"protected_paths_unchanged",
|
|
1293
|
+
"test_and_invariant_mapping_supported",
|
|
1294
|
+
"suggest_green_test_ids",
|
|
1295
|
+
];
|
|
1296
|
+
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
1297
|
+
if (blockers.length > 0) {
|
|
1298
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
1299
|
+
packet.block_reasons = blockers;
|
|
1300
|
+
}
|
|
1301
|
+
return finalize_apply_worker_packet(packet);
|
|
1302
|
+
}
|
|
1303
|
+
const packet = apply_packet_common(ctx, "apply_code_review", taskId, "executor_worker", blockers);
|
|
1304
|
+
packet.declared_task_write_scope = writeScope;
|
|
1305
|
+
packet.executor_report_refs = args.executor_report_refs ?? [];
|
|
1306
|
+
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1307
|
+
packet.code_review_report_required_fields = [...APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS];
|
|
1308
|
+
packet.task_edit = decision_status(taskEdit);
|
|
1309
|
+
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope));
|
|
1310
|
+
return finalize_apply_worker_packet(packet);
|
|
1311
|
+
}
|
|
1312
|
+
function apply_verify_packet(ctx, args) {
|
|
1313
|
+
const taskId = args.task_id ?? "";
|
|
1314
|
+
const blockers = [];
|
|
1315
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
1316
|
+
if (!applyReady.allowed)
|
|
1317
|
+
blockers.push(...decision_reasons(applyReady));
|
|
1318
|
+
const taskEdit = evaluate_workflow_decision(ctx, "task_edit", taskId);
|
|
1319
|
+
if (!taskEdit.allowed)
|
|
1320
|
+
blockers.push(...decision_reasons(taskEdit));
|
|
1321
|
+
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1322
|
+
blockers.push(...taskBlockers);
|
|
1323
|
+
const writeScope = task ? splitList(task.attrs.write_scope ?? "") : [];
|
|
1324
|
+
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1325
|
+
const chainId = require_active_apply_worker_chain(ctx, taskId, blockers);
|
|
1326
|
+
const activeChain = active_apply_worker_chain(ctx, taskId).active;
|
|
1327
|
+
const packet = apply_packet_common(ctx, "apply_verify", taskId, "executor_worker", blockers);
|
|
1328
|
+
if (chainId) {
|
|
1329
|
+
const executorRefs = validate_worker_report_refs(ctx, args.executor_report_refs ?? [], {
|
|
1330
|
+
role: "executor",
|
|
1331
|
+
taskId,
|
|
1332
|
+
chainId,
|
|
1333
|
+
missingCode: "missing_executor_report_ref",
|
|
1334
|
+
missingMessage: "apply-verify-packet requires --executor-report-ref",
|
|
1335
|
+
});
|
|
1336
|
+
const codeReviewRefs = validate_worker_report_refs(ctx, args.task_code_review_report_refs ?? [], {
|
|
1337
|
+
role: "code-reviewer",
|
|
1338
|
+
taskId,
|
|
1339
|
+
chainId,
|
|
1340
|
+
missingCode: "missing_task_code_review_report_ref",
|
|
1341
|
+
missingMessage: "apply-verify-packet requires --task-code-review-report-ref",
|
|
1342
|
+
});
|
|
1343
|
+
const greenRefs = validate_test_run_evidence_refs(ctx, args.green_test_run_evidence_refs ?? [], {
|
|
1344
|
+
taskId,
|
|
1345
|
+
gate: "task_complete",
|
|
1346
|
+
phase: "green",
|
|
1347
|
+
semanticStatus: "expected_success",
|
|
1348
|
+
chainId,
|
|
1349
|
+
missingCode: "missing_green_test_run_evidence_ref",
|
|
1350
|
+
missingMessage: "apply-verify-packet requires --green-test-run-evidence-ref",
|
|
1351
|
+
});
|
|
1352
|
+
const redRefs = validate_test_run_evidence_refs(ctx, args.red_test_run_evidence_refs ?? [], {
|
|
1353
|
+
taskId,
|
|
1354
|
+
gate: "task_edit",
|
|
1355
|
+
phase: "red",
|
|
1356
|
+
semanticStatus: "expected_failure",
|
|
1357
|
+
chainId: null,
|
|
1358
|
+
missingCode: "missing_red_test_run_evidence_ref",
|
|
1359
|
+
missingMessage: "apply-verify-packet requires --red-test-run-evidence-ref",
|
|
1360
|
+
});
|
|
1361
|
+
const characterizationRefs = validate_test_run_evidence_refs(ctx, args.characterization_test_run_evidence_refs ?? [], {
|
|
1362
|
+
taskId,
|
|
1363
|
+
gate: "task_edit",
|
|
1364
|
+
phase: "characterization",
|
|
1365
|
+
semanticStatus: "expected_success",
|
|
1366
|
+
chainId: null,
|
|
1367
|
+
missingCode: "missing_characterization_test_run_evidence_ref",
|
|
1368
|
+
missingMessage: "apply-verify-packet requires --characterization-test-run-evidence-ref",
|
|
1369
|
+
});
|
|
1370
|
+
blockers.push(...executorRefs.blockers, ...codeReviewRefs.blockers, ...greenRefs.blockers);
|
|
1371
|
+
if (executorRefs.refs[0]) {
|
|
1372
|
+
blockers.push(...worker_report_origin_blockers(executorRefs.refs[0], activeChain?.executor_packet_fingerprint, "executor_report_ref"));
|
|
1373
|
+
if (activeChain) {
|
|
1374
|
+
blockers.push(...worker_report_input_digest_blockers(executorRefs.refs[0], apply_worker_executor_input_ref_digest(ctx.evidences, activeChain), "executor_report_ref"));
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
if (executorRefs.refs[0] && codeReviewRefs.refs[0]) {
|
|
1378
|
+
blockers.push(...worker_report_input_blockers(codeReviewRefs.refs[0], [executorRefs.refs[0]], "task_code_review_report_ref"));
|
|
1379
|
+
}
|
|
1380
|
+
if ((args.red_test_run_evidence_refs ?? []).length === 0 && (args.characterization_test_run_evidence_refs ?? []).length === 0) {
|
|
1381
|
+
blockers.push(reason("missing_pre_edit_test_run_evidence_ref", "apply-verify-packet requires --red-test-run-evidence-ref or --characterization-test-run-evidence-ref"));
|
|
1382
|
+
}
|
|
1383
|
+
else {
|
|
1384
|
+
if ((args.red_test_run_evidence_refs ?? []).length > 0)
|
|
1385
|
+
blockers.push(...redRefs.blockers, ...pre_edit_refs_bound_to_active_chain(ctx, activeChain, redRefs.refs, "red", taskId));
|
|
1386
|
+
if ((args.characterization_test_run_evidence_refs ?? []).length > 0)
|
|
1387
|
+
blockers.push(...characterizationRefs.blockers, ...pre_edit_refs_bound_to_active_chain(ctx, activeChain, characterizationRefs.refs, "characterization", taskId));
|
|
1388
|
+
}
|
|
1389
|
+
packet.apply_worker_chain_id = chainId;
|
|
1390
|
+
packet.executor_report_pinned_refs = executorRefs.refs;
|
|
1391
|
+
packet.task_code_review_report_pinned_refs = codeReviewRefs.refs;
|
|
1392
|
+
packet.green_test_run_evidence_pinned_refs = greenRefs.refs;
|
|
1393
|
+
packet.red_test_run_evidence_pinned_refs = redRefs.refs;
|
|
1394
|
+
packet.characterization_test_run_evidence_pinned_refs = characterizationRefs.refs;
|
|
1395
|
+
if (executorRefs.refs[0])
|
|
1396
|
+
packet.expected_executor_origin_packet_fingerprint = activeChain?.executor_packet_fingerprint;
|
|
1397
|
+
if (executorRefs.refs[0] && activeChain)
|
|
1398
|
+
packet.expected_executor_input_ref_digest = apply_worker_executor_input_ref_digest(ctx.evidences, activeChain);
|
|
1399
|
+
if (executorRefs.refs[0] && codeReviewRefs.refs[0]) {
|
|
1400
|
+
packet.expected_code_review_input_ref_digest = worker_input_ref_digest([executorRefs.refs[0]]);
|
|
1401
|
+
}
|
|
1402
|
+
if (executorRefs.refs[0] && codeReviewRefs.refs[0] && greenRefs.refs[0]) {
|
|
1403
|
+
const canonicalGreenRefs = [...greenRefs.refs].sort((a, b) => String(a.evidence_id ?? "").localeCompare(String(b.evidence_id ?? "")));
|
|
1404
|
+
const expectedFreshness = compute_apply_worker_freshness(ctx.repoRoot, ctx.changeRoot, ctx.evidences, taskId, chainId, {
|
|
1405
|
+
executor_report_ref: executorRefs.refs[0],
|
|
1406
|
+
task_code_review_report_ref: codeReviewRefs.refs[0],
|
|
1407
|
+
green_test_run_evidence_ref: String(canonicalGreenRefs[0].evidence_id ?? ""),
|
|
1408
|
+
green_test_run_evidence_refs: canonicalGreenRefs.map((ev) => String(ev.evidence_id ?? "")).filter(Boolean),
|
|
1409
|
+
});
|
|
1410
|
+
packet.expected_freshness_fingerprint = expectedFreshness;
|
|
1411
|
+
const preEditRefs = [
|
|
1412
|
+
...(Array.isArray(packet.red_test_run_evidence_pinned_refs) ? packet.red_test_run_evidence_pinned_refs : []),
|
|
1413
|
+
...(Array.isArray(packet.characterization_test_run_evidence_pinned_refs) ? packet.characterization_test_run_evidence_pinned_refs : []),
|
|
1414
|
+
];
|
|
1415
|
+
packet.expected_verifier_input_ref_digest = worker_input_ref_digest([
|
|
1416
|
+
executorRefs.refs[0],
|
|
1417
|
+
codeReviewRefs.refs[0],
|
|
1418
|
+
...canonicalGreenRefs.map((ev) => ({ kind: "test_run", evidence_id: String(ev.evidence_id ?? ""), phase: ev.phase, semantic_status: ev.semantic_status })),
|
|
1419
|
+
...preEditRefs.map((ev) => ({ kind: "test_run", evidence_id: String(ev.evidence_id ?? ""), phase: ev.phase, semantic_status: ev.semantic_status })),
|
|
1420
|
+
]);
|
|
1421
|
+
const missingGreenFingerprint = greenRefs.refs.filter((ev) => !fingerprint_digest(ev.implementation_fingerprint));
|
|
1422
|
+
if (missingGreenFingerprint.length > 0) {
|
|
1423
|
+
blockers.push(reason("green_implementation_fingerprint_missing", "apply-verify-packet requires GREEN evidence to record implementation_fingerprint"));
|
|
1424
|
+
}
|
|
1425
|
+
else if (!greenRefs.refs.every((ev) => fingerprint_matches(ev.implementation_fingerprint, expectedFreshness.implementation_fingerprint))) {
|
|
1426
|
+
blockers.push(reason("green_implementation_fingerprint_mismatch", "apply-verify-packet requires current implementation fingerprint to match GREEN evidence implementation_fingerprint"));
|
|
1427
|
+
}
|
|
1428
|
+
if (!fingerprint_matches(codeReviewRefs.refs[0].observed_implementation_fingerprint, expectedFreshness.implementation_fingerprint)) {
|
|
1429
|
+
blockers.push(reason("task_code_review_implementation_fingerprint_mismatch", "apply-verify-packet requires code-reviewer observed_implementation_fingerprint to match current implementation fingerprint"));
|
|
1430
|
+
}
|
|
1431
|
+
if (Array.isArray(expectedFreshness.protected_dirty_paths) && expectedFreshness.protected_dirty_paths.length > 0) {
|
|
1432
|
+
blockers.push(reason("protected_path_dirty", `apply-verify-packet requires protected change artifacts to stay unchanged during executor-worker verification: ${renderList(expectedFreshness.protected_dirty_paths.map(String))}`, expectedFreshness.protected_dirty_paths.map(String)));
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
packet.declared_task_write_scope = writeScope;
|
|
1437
|
+
packet.task_edit = decision_status(taskEdit);
|
|
1438
|
+
packet.task_complete = safe_decision_status(ctx, "task_complete", taskId);
|
|
1439
|
+
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope, [
|
|
1440
|
+
...(Array.isArray(packet.executor_report_pinned_refs) ? packet.executor_report_pinned_refs : []),
|
|
1441
|
+
...(Array.isArray(packet.task_code_review_report_pinned_refs) ? packet.task_code_review_report_pinned_refs : []),
|
|
1442
|
+
]));
|
|
1443
|
+
packet.current_worktree_refs = dirty_repo_refs(ctx, "apply_verify current worktree refs");
|
|
1444
|
+
packet.protected_path_refs = Array.isArray(packet.expected_freshness_fingerprint?.protected_path_refs)
|
|
1445
|
+
? packet.expected_freshness_fingerprint.protected_path_refs
|
|
1446
|
+
: apply_worker_protected_path_refs(ctx.repoRoot, ctx.changeRoot);
|
|
1447
|
+
packet.scope_diff_review_policy = {
|
|
1448
|
+
declared_task_write_scope: writeScope,
|
|
1449
|
+
forbidden_paths: ["proposal.md", "design.md", "tasks.md", "specs/", ".superspec/"],
|
|
1450
|
+
require_scope_verdict: true,
|
|
1451
|
+
};
|
|
1452
|
+
packet.verification_checks = [
|
|
1453
|
+
"red_or_characterization_pre_edit_evidence_bound_to_active_chain",
|
|
1454
|
+
"executor_report_same_chain_and_fresh",
|
|
1455
|
+
"task_code_review_report_same_chain_and_fresh",
|
|
1456
|
+
"green_test_run_same_chain_and_pinned_transcripts",
|
|
1457
|
+
"current_freshness_matches_expected_freshness_fingerprint",
|
|
1458
|
+
"protected_change_artifacts_clean",
|
|
1459
|
+
];
|
|
1460
|
+
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1461
|
+
packet.code_review_report_required_fields = [...APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS];
|
|
1462
|
+
packet.verifier_report_required_fields = [...APPLY_VERIFIER_REPORT_REQUIRED_FIELDS];
|
|
1463
|
+
packet.test_evidence_required_fields = ["command", "cwd", "exit_code", "repo_head", "implementation_fingerprint", "guard_artifact_manifest_fingerprint", "raw_log_pinned_refs"];
|
|
1464
|
+
packet.executor_report_refs = args.executor_report_refs ?? [];
|
|
1465
|
+
packet.task_code_review_report_refs = args.task_code_review_report_refs ?? [];
|
|
1466
|
+
packet.green_test_run_evidence_refs = args.green_test_run_evidence_refs ?? [];
|
|
1467
|
+
packet.red_test_run_evidence_refs = args.red_test_run_evidence_refs ?? [];
|
|
1468
|
+
packet.characterization_test_run_evidence_refs = args.characterization_test_run_evidence_refs ?? [];
|
|
1469
|
+
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
1470
|
+
if (blockers.length > 0) {
|
|
1471
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
1472
|
+
packet.block_reasons = blockers;
|
|
1473
|
+
}
|
|
1474
|
+
return finalize_apply_worker_packet(packet);
|
|
1475
|
+
}
|
|
1476
|
+
function render_apply_worker_prompt(packet) {
|
|
1477
|
+
const blockedHeaders = {
|
|
1478
|
+
apply_test: "DO NOT SPAWN TEST-RUNNER",
|
|
1479
|
+
apply_executor: "DO NOT SPAWN IMPLEMENTATION EXECUTOR",
|
|
1480
|
+
apply_code_review: "DO NOT SPAWN IMPLEMENTATION CODE-REVIEWER",
|
|
1481
|
+
apply_verify: "DO NOT SPAWN IMPLEMENTATION VERIFIER",
|
|
1482
|
+
};
|
|
1483
|
+
const lines = [];
|
|
1484
|
+
if (packet.worker_state === "blocked") {
|
|
1485
|
+
lines.push(blockedHeaders[String(packet.packet_kind)] ?? "DO NOT SPAWN WORKER", "");
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
lines.push("# SuperSpec Apply Worker Packet", "");
|
|
1489
|
+
}
|
|
1490
|
+
lines.push(`packet_kind: ${String(packet.packet_kind)}`, `change: ${String(packet.change)}`, `task_id: ${String(packet.task_id)}`, `worker_state: ${String(packet.worker_state)}`, `worker_chain_context: ${String(packet.worker_chain_context)}`);
|
|
1491
|
+
if (typeof packet.test_id === "string")
|
|
1492
|
+
lines.push(`test_id: ${packet.test_id}`);
|
|
1493
|
+
if (typeof packet.phase === "string")
|
|
1494
|
+
lines.push(`phase: ${packet.phase}`);
|
|
1495
|
+
if (typeof packet.allowed_test_command === "string")
|
|
1496
|
+
lines.push(`allowed_test_command: ${packet.allowed_test_command}`);
|
|
1497
|
+
if (typeof packet.apply_worker_chain_id === "string")
|
|
1498
|
+
lines.push(`apply_worker_chain_id: ${packet.apply_worker_chain_id}`);
|
|
1499
|
+
if (packet.packet_kind === "apply_executor" && packet.worker_state === "ready") {
|
|
1500
|
+
lines.push("", "CHAIN ACTIVATION VERIFIED FOR IMPLEMENTATION EXECUTOR", "The active apply_worker_chain evidence ref has been validated for this prompt.", "", "chain_activation_template:", JSON.stringify(packet.chain_activation_template ?? {}, null, 2));
|
|
1501
|
+
}
|
|
1502
|
+
if (packet.worker_state === "ready") {
|
|
1503
|
+
lines.push("", "Packet JSON:", JSON.stringify(packet, null, 2));
|
|
1504
|
+
}
|
|
1505
|
+
if (Array.isArray(packet.blockers) && packet.blockers.length > 0) {
|
|
1506
|
+
lines.push("", "Blockers:", ...packet.blockers.map((item) => `- ${item}`));
|
|
1507
|
+
}
|
|
1508
|
+
if (Array.isArray(packet.block_reasons) && packet.block_reasons.length > 0) {
|
|
1509
|
+
lines.push("", "Blocker details:", ...packet.block_reasons.map((item) => `- ${item.code}: ${item.message}`));
|
|
1510
|
+
}
|
|
1511
|
+
if (Array.isArray(packet.stop_conditions) && packet.stop_conditions.length > 0) {
|
|
1512
|
+
lines.push("", "Stop conditions:", ...packet.stop_conditions.map((item) => `- ${item}`));
|
|
1513
|
+
}
|
|
1514
|
+
return `${lines.join("\n")}\n`;
|
|
1515
|
+
}
|
|
579
1516
|
function render_ref(ref) {
|
|
580
1517
|
return `- ${ref.root}:${ref.path} @ ${ref.blob_sha}`;
|
|
581
1518
|
}
|
|
@@ -641,6 +1578,34 @@ export function dispatch_packet(args) {
|
|
|
641
1578
|
}
|
|
642
1579
|
return { output_format: "agent", payload: packet };
|
|
643
1580
|
}
|
|
1581
|
+
if (args.command === "apply-test-packet") {
|
|
1582
|
+
assert_packet_context_clean(ctx);
|
|
1583
|
+
const packet = apply_test_packet(ctx, args);
|
|
1584
|
+
if (args.packet_format === "prompt")
|
|
1585
|
+
return { output_format: "prompt", payload: render_apply_worker_prompt(packet) };
|
|
1586
|
+
return { output_format: "agent", payload: packet };
|
|
1587
|
+
}
|
|
1588
|
+
if (args.command === "apply-executor-packet") {
|
|
1589
|
+
assert_packet_context_clean(ctx);
|
|
1590
|
+
const packet = apply_executor_packet(ctx, args);
|
|
1591
|
+
if (args.packet_format === "prompt")
|
|
1592
|
+
return { output_format: "prompt", payload: render_apply_worker_prompt(packet) };
|
|
1593
|
+
return { output_format: "agent", payload: packet };
|
|
1594
|
+
}
|
|
1595
|
+
if (args.command === "apply-code-review-packet") {
|
|
1596
|
+
assert_packet_context_clean(ctx);
|
|
1597
|
+
const packet = apply_code_review_packet(ctx, args);
|
|
1598
|
+
if (args.packet_format === "prompt")
|
|
1599
|
+
return { output_format: "prompt", payload: render_apply_worker_prompt(packet) };
|
|
1600
|
+
return { output_format: "agent", payload: packet };
|
|
1601
|
+
}
|
|
1602
|
+
if (args.command === "apply-verify-packet") {
|
|
1603
|
+
assert_packet_context_clean(ctx);
|
|
1604
|
+
const packet = apply_verify_packet(ctx, args);
|
|
1605
|
+
if (args.packet_format === "prompt")
|
|
1606
|
+
return { output_format: "prompt", payload: render_apply_worker_prompt(packet) };
|
|
1607
|
+
return { output_format: "agent", payload: packet };
|
|
1608
|
+
}
|
|
644
1609
|
if (args.command === "ledger-render") {
|
|
645
1610
|
assert_packet_context_clean(ctx);
|
|
646
1611
|
return { output_format: "prompt", payload: render_ledger(ctx, args.gate ?? "", args.round) };
|
|
@@ -648,5 +1613,11 @@ export function dispatch_packet(args) {
|
|
|
648
1613
|
throw new GuardError(`unknown packet command: ${args.command}`);
|
|
649
1614
|
}
|
|
650
1615
|
export function is_packet_command(command) {
|
|
651
|
-
return command === "workflow-packet"
|
|
1616
|
+
return command === "workflow-packet"
|
|
1617
|
+
|| command === "review-packet"
|
|
1618
|
+
|| command === "apply-test-packet"
|
|
1619
|
+
|| command === "apply-executor-packet"
|
|
1620
|
+
|| command === "apply-code-review-packet"
|
|
1621
|
+
|| command === "apply-verify-packet"
|
|
1622
|
+
|| command === "ledger-render";
|
|
652
1623
|
}
|