mancode 0.6.2 → 0.6.3
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.en.md +375 -12
- package/README.md +187 -10
- package/dist/{chunk-44UHUCE6.js → chunk-NG72XZ3R.js} +192 -10
- package/dist/chunk-NG72XZ3R.js.map +1 -0
- package/dist/{chunk-GCLUIY65.js → chunk-RDPFQODS.js} +290 -105
- package/dist/chunk-RDPFQODS.js.map +1 -0
- package/dist/cli.js +2093 -127
- package/dist/cli.js.map +1 -1
- package/dist/{store-TQFQCAAF.js → store-C3G3HN3N.js} +2 -2
- package/dist/{v3-adapter-ZLYZAA4P.js → v3-adapter-7KIOYYWS.js} +10 -4
- package/package.json +2 -2
- package/dist/chunk-44UHUCE6.js.map +0 -1
- package/dist/chunk-GCLUIY65.js.map +0 -1
- /package/dist/{store-TQFQCAAF.js.map → store-C3G3HN3N.js.map} +0 -0
- /package/dist/{v3-adapter-ZLYZAA4P.js.map → v3-adapter-7KIOYYWS.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
assertExecutableImplementationScope,
|
|
11
11
|
assertHandoffTransition,
|
|
12
12
|
assertKnownKeys,
|
|
13
|
+
assertManDeliveryVerificationSurfaces,
|
|
13
14
|
assertOperationJournalTransition,
|
|
14
15
|
assertOperationReservationTopology,
|
|
15
16
|
assertParentWorkflowRelation,
|
|
@@ -71,6 +72,8 @@ import {
|
|
|
71
72
|
parseClaim,
|
|
72
73
|
parseFreshness,
|
|
73
74
|
parseHandoff,
|
|
75
|
+
parseManReviewEvidence,
|
|
76
|
+
parseManVerificationSurface,
|
|
74
77
|
parseOperationJournal,
|
|
75
78
|
parseOperationReservation,
|
|
76
79
|
parseProjectConfig,
|
|
@@ -114,8 +117,9 @@ import {
|
|
|
114
117
|
workflowMetadataDigest,
|
|
115
118
|
writeOperationReservation,
|
|
116
119
|
writeProjectFacts
|
|
117
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-NG72XZ3R.js";
|
|
118
121
|
import {
|
|
122
|
+
ACCEPTED_STATE_NARRATIVE_GUIDANCE,
|
|
119
123
|
DEFAULT_MANCODE_END_MARKER,
|
|
120
124
|
DEFAULT_MANCODE_START_MARKER,
|
|
121
125
|
INTERFACE_EMOJI_ICON_GUIDANCE,
|
|
@@ -128,6 +132,7 @@ import {
|
|
|
128
132
|
assertV3AdapterInstallable,
|
|
129
133
|
assertV3AdapterTargetSafe,
|
|
130
134
|
hasManagedBlock,
|
|
135
|
+
inspectUnsafeV3AdapterPaths,
|
|
131
136
|
inspectV3Adapter,
|
|
132
137
|
inspectV3AdapterVersions,
|
|
133
138
|
planV3AdapterFiles,
|
|
@@ -135,11 +140,13 @@ import {
|
|
|
135
140
|
removeManagedBlock,
|
|
136
141
|
removeV3Adapter,
|
|
137
142
|
replaceManagedBlock,
|
|
143
|
+
replaceUnsafeV3AdapterSymlinks,
|
|
138
144
|
stageV3Adapter,
|
|
139
145
|
stageV3AdapterUpgradeFiles,
|
|
140
146
|
v3AdapterTargetPath,
|
|
141
|
-
v3AdapterVersionsFromStatuses
|
|
142
|
-
|
|
147
|
+
v3AdapterVersionsFromStatuses,
|
|
148
|
+
writeThroughResolvedPath
|
|
149
|
+
} from "./chunk-RDPFQODS.js";
|
|
143
150
|
|
|
144
151
|
// src/cli.ts
|
|
145
152
|
import { realpathSync } from "fs";
|
|
@@ -383,6 +390,14 @@ var V3_AUTHORITY_PATHS = [
|
|
|
383
390
|
"local",
|
|
384
391
|
"runtime"
|
|
385
392
|
];
|
|
393
|
+
var LOCAL_CONTINUITY_PATHS = [
|
|
394
|
+
"sessions",
|
|
395
|
+
"workflows",
|
|
396
|
+
"cache",
|
|
397
|
+
"quarantine",
|
|
398
|
+
"publish",
|
|
399
|
+
"runtime"
|
|
400
|
+
];
|
|
386
401
|
async function scanLegacyAuthority(projectRoot) {
|
|
387
402
|
const root = path.resolve(projectRoot, ".mancode");
|
|
388
403
|
const entries = await Promise.all(
|
|
@@ -402,9 +417,10 @@ async function scanLegacyAuthority(projectRoot) {
|
|
|
402
417
|
async function inspectMancodeLayout(projectRoot) {
|
|
403
418
|
const root = path.resolve(projectRoot);
|
|
404
419
|
const mancodeRoot = path.join(root, ".mancode");
|
|
405
|
-
const [legacy, target] = await Promise.all([
|
|
420
|
+
const [legacy, target, entries] = await Promise.all([
|
|
406
421
|
scanLegacyAuthority(root),
|
|
407
|
-
lstatOrNull(mancodeRoot)
|
|
422
|
+
lstatOrNull(mancodeRoot),
|
|
423
|
+
targetEntries(mancodeRoot)
|
|
408
424
|
]);
|
|
409
425
|
const v3AuthorityPathsPresent = (await Promise.all(
|
|
410
426
|
V3_AUTHORITY_PATHS.map(
|
|
@@ -413,10 +429,16 @@ async function inspectMancodeLayout(projectRoot) {
|
|
|
413
429
|
)).filter(
|
|
414
430
|
(value) => value !== null
|
|
415
431
|
);
|
|
432
|
+
const blockingV3AuthorityPresent = v3AuthorityPathsPresent.some(
|
|
433
|
+
(relativePath2) => relativePath2 !== "local"
|
|
434
|
+
);
|
|
435
|
+
const v3ScratchOnly = target?.isDirectory() === true && !blockingV3AuthorityPresent && !await localHasContinuityContent(mancodeRoot);
|
|
416
436
|
return {
|
|
417
437
|
legacy,
|
|
418
438
|
v3TargetExists: target !== null,
|
|
419
|
-
v3AuthorityPathsPresent
|
|
439
|
+
v3AuthorityPathsPresent,
|
|
440
|
+
v3ScratchOnly,
|
|
441
|
+
v3TargetEntries: entries
|
|
420
442
|
};
|
|
421
443
|
}
|
|
422
444
|
async function assertGreenfieldInitializationPreflight(projectRoot) {
|
|
@@ -525,6 +547,23 @@ async function lstatOrNull(target) {
|
|
|
525
547
|
throw error;
|
|
526
548
|
}
|
|
527
549
|
}
|
|
550
|
+
async function targetEntries(root) {
|
|
551
|
+
const stat5 = await lstatOrNull(root);
|
|
552
|
+
if (stat5 === null || !stat5.isDirectory()) return [];
|
|
553
|
+
const names = await readdir(root);
|
|
554
|
+
names.sort(compareUtf8);
|
|
555
|
+
return names;
|
|
556
|
+
}
|
|
557
|
+
async function localHasContinuityContent(root) {
|
|
558
|
+
const localPath = path.join(root, "local");
|
|
559
|
+
const stat5 = await lstatOrNull(localPath);
|
|
560
|
+
if (stat5 === null) return false;
|
|
561
|
+
if (!stat5.isDirectory()) return true;
|
|
562
|
+
const names = await readdir(localPath);
|
|
563
|
+
return names.some(
|
|
564
|
+
(name) => LOCAL_CONTINUITY_PATHS.includes(name)
|
|
565
|
+
);
|
|
566
|
+
}
|
|
528
567
|
function sha256(value) {
|
|
529
568
|
return `sha256:${createHash("sha256").update(value).digest("hex")}`;
|
|
530
569
|
}
|
|
@@ -2697,6 +2736,13 @@ function operationRecoveryPayloadPath(store, operationId) {
|
|
|
2697
2736
|
assertUlid(operationId, "operation recovery payload operationId");
|
|
2698
2737
|
return path3.join(operationRecoveryDirectory(store), `${operationId}.json`);
|
|
2699
2738
|
}
|
|
2739
|
+
function operationRecoveryPayloadVersionPath(store, operationId, payloadDigest) {
|
|
2740
|
+
assertUlid(operationId, "operation recovery payload operationId");
|
|
2741
|
+
return path3.join(
|
|
2742
|
+
operationRecoveryDirectory(store),
|
|
2743
|
+
`${operationId}.${payloadDigestToken(payloadDigest)}.json`
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2700
2746
|
async function writeOperationRecoveryPayload(store, value) {
|
|
2701
2747
|
const payload = parseOperationRecoveryPayload(value);
|
|
2702
2748
|
if (payload.primaryStoreId !== store.storeId) {
|
|
@@ -2725,12 +2771,51 @@ async function writeOperationRecoveryPayload(store, value) {
|
|
|
2725
2771
|
throw new Error("MANCODE_OPERATION_RECOVERY_PAYLOAD_CONFLICT");
|
|
2726
2772
|
}
|
|
2727
2773
|
}
|
|
2774
|
+
async function writeOperationRecoveryPayloadVersion(store, value) {
|
|
2775
|
+
const payload = parseOperationRecoveryPayload(value);
|
|
2776
|
+
if (payload.primaryStoreId !== store.storeId) {
|
|
2777
|
+
throw new Error(
|
|
2778
|
+
"operation recovery payload primaryStoreId does not match its home store"
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
2781
|
+
const digest = operationRecoveryPayloadDigest(payload);
|
|
2782
|
+
const directory = operationRecoveryDirectory(store);
|
|
2783
|
+
const target = operationRecoveryPayloadVersionPath(
|
|
2784
|
+
store,
|
|
2785
|
+
payload.operationId,
|
|
2786
|
+
digest
|
|
2787
|
+
);
|
|
2788
|
+
await mkdir2(directory, { recursive: true });
|
|
2789
|
+
try {
|
|
2790
|
+
await writeFile2(target, serialize(payload), {
|
|
2791
|
+
encoding: "utf8",
|
|
2792
|
+
flag: "wx"
|
|
2793
|
+
});
|
|
2794
|
+
return payload;
|
|
2795
|
+
} catch (error) {
|
|
2796
|
+
if (!isAlreadyExists2(error)) throw error;
|
|
2797
|
+
const existing = await readOperationRecoveryPayloadAtPath(target);
|
|
2798
|
+
if (existing !== null && operationRecoveryPayloadDigest(existing) === digest) {
|
|
2799
|
+
return existing;
|
|
2800
|
+
}
|
|
2801
|
+
throw new Error("MANCODE_OPERATION_RECOVERY_PAYLOAD_CONFLICT");
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2728
2804
|
async function readOperationRecoveryPayload(store, operationId) {
|
|
2805
|
+
return readOperationRecoveryPayloadAtPath(
|
|
2806
|
+
operationRecoveryPayloadPath(store, operationId)
|
|
2807
|
+
);
|
|
2808
|
+
}
|
|
2809
|
+
async function readOperationRecoveryPayloadForDigest(store, operationId, payloadDigest) {
|
|
2810
|
+
const versioned = await readOperationRecoveryPayloadAtPath(
|
|
2811
|
+
operationRecoveryPayloadVersionPath(store, operationId, payloadDigest)
|
|
2812
|
+
);
|
|
2813
|
+
if (versioned !== null) return versioned;
|
|
2814
|
+
return readOperationRecoveryPayload(store, operationId);
|
|
2815
|
+
}
|
|
2816
|
+
async function readOperationRecoveryPayloadAtPath(target) {
|
|
2729
2817
|
try {
|
|
2730
|
-
const raw = await readFile3(
|
|
2731
|
-
operationRecoveryPayloadPath(store, operationId),
|
|
2732
|
-
"utf8"
|
|
2733
|
-
);
|
|
2818
|
+
const raw = await readFile3(target, "utf8");
|
|
2734
2819
|
return parseOperationRecoveryPayload(JSON.parse(raw));
|
|
2735
2820
|
} catch (error) {
|
|
2736
2821
|
if (isNotFound3(error)) return null;
|
|
@@ -2740,6 +2825,13 @@ async function readOperationRecoveryPayload(store, operationId) {
|
|
|
2740
2825
|
throw error;
|
|
2741
2826
|
}
|
|
2742
2827
|
}
|
|
2828
|
+
function payloadDigestToken(payloadDigest) {
|
|
2829
|
+
const match = /^sha256:([a-f0-9]{64})$/.exec(payloadDigest);
|
|
2830
|
+
if (match?.[1] === void 0) {
|
|
2831
|
+
throw new Error("operation recovery payload digest is invalid");
|
|
2832
|
+
}
|
|
2833
|
+
return match[1];
|
|
2834
|
+
}
|
|
2743
2835
|
function serialize(payload) {
|
|
2744
2836
|
return `${JSON.stringify(payload, null, 2)}
|
|
2745
2837
|
`;
|
|
@@ -5420,6 +5512,30 @@ async function writeTaskCheckpointAtRoot(taskRoot2, value) {
|
|
|
5420
5512
|
throw new Error("MANCODE_CHECKPOINT_ID_CONFLICT");
|
|
5421
5513
|
}
|
|
5422
5514
|
}
|
|
5515
|
+
async function assertTaskCheckpointIdAvailableAtRoot(taskRoot2, checkpointId) {
|
|
5516
|
+
assertUlid(checkpointId, "checkpointId");
|
|
5517
|
+
await assertSafeTaskDirectory(taskRoot2);
|
|
5518
|
+
const directory = path11.join(taskRoot2, "checkpoints");
|
|
5519
|
+
try {
|
|
5520
|
+
const entry = await lstat4(directory);
|
|
5521
|
+
if (!entry.isDirectory() || entry.isSymbolicLink()) {
|
|
5522
|
+
throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
|
|
5523
|
+
}
|
|
5524
|
+
} catch (error) {
|
|
5525
|
+
if (isNotFound11(error)) return;
|
|
5526
|
+
throw error;
|
|
5527
|
+
}
|
|
5528
|
+
try {
|
|
5529
|
+
const entry = await lstat4(path11.join(directory, `${checkpointId}.json`));
|
|
5530
|
+
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
5531
|
+
throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
|
|
5532
|
+
}
|
|
5533
|
+
} catch (error) {
|
|
5534
|
+
if (isNotFound11(error)) return;
|
|
5535
|
+
throw error;
|
|
5536
|
+
}
|
|
5537
|
+
throw new Error("MANCODE_CHECKPOINT_ID_CONFLICT");
|
|
5538
|
+
}
|
|
5423
5539
|
async function readTaskCheckpointAtRoot(taskRoot2, checkpointId) {
|
|
5424
5540
|
assertUlid(checkpointId, "checkpointId");
|
|
5425
5541
|
await assertSafeTaskDirectory(taskRoot2);
|
|
@@ -5434,9 +5550,13 @@ async function readTaskCheckpointAtRoot(taskRoot2, checkpointId) {
|
|
|
5434
5550
|
throw error;
|
|
5435
5551
|
}
|
|
5436
5552
|
try {
|
|
5437
|
-
|
|
5553
|
+
const checkpoint = await readImmutableCheckpoint(
|
|
5438
5554
|
path11.join(directory, `${checkpointId}.json`)
|
|
5439
5555
|
);
|
|
5556
|
+
if (checkpoint.checkpointId !== checkpointId) {
|
|
5557
|
+
throw new Error("MANCODE_CHECKPOINT_CORRUPT");
|
|
5558
|
+
}
|
|
5559
|
+
return checkpoint;
|
|
5440
5560
|
} catch (error) {
|
|
5441
5561
|
if (error instanceof Error && error.message === "MANCODE_CHECKPOINT_ID_CONFLICT") {
|
|
5442
5562
|
return null;
|
|
@@ -5607,6 +5727,16 @@ import path12 from "path";
|
|
|
5607
5727
|
var REQUIREMENTS_FILE = "requirements.json";
|
|
5608
5728
|
var REQUIREMENTS_MARKDOWN_FILE = "requirements.md";
|
|
5609
5729
|
var ACCEPTANCE_ID_PATTERN = /^AC-[A-Z0-9][A-Z0-9-]{0,27}$/;
|
|
5730
|
+
var VERIFICATION_SURFACES = /* @__PURE__ */ new Set([
|
|
5731
|
+
"unit",
|
|
5732
|
+
"component",
|
|
5733
|
+
"handler",
|
|
5734
|
+
"real_http",
|
|
5735
|
+
"browser",
|
|
5736
|
+
"device",
|
|
5737
|
+
"external_service",
|
|
5738
|
+
"manual_observation"
|
|
5739
|
+
]);
|
|
5610
5740
|
var REQUIREMENT_DIMENSIONS = [
|
|
5611
5741
|
"platform",
|
|
5612
5742
|
"core_scope",
|
|
@@ -5692,11 +5822,16 @@ function parseRequirementsLedger2(raw, options = {}) {
|
|
|
5692
5822
|
throw new Error(`duplicate acceptance criterion: ${item.id}`);
|
|
5693
5823
|
}
|
|
5694
5824
|
ids.add(item.id);
|
|
5825
|
+
const verificationSurfaces = parseVerificationSurfaces(
|
|
5826
|
+
item.verificationSurfaces,
|
|
5827
|
+
item.method
|
|
5828
|
+
);
|
|
5695
5829
|
return {
|
|
5696
5830
|
id: item.id,
|
|
5697
5831
|
description: item.description.trim(),
|
|
5698
5832
|
required: item.required,
|
|
5699
|
-
method: item.method
|
|
5833
|
+
method: item.method,
|
|
5834
|
+
...verificationSurfaces === void 0 ? {} : { verificationSurfaces }
|
|
5700
5835
|
};
|
|
5701
5836
|
});
|
|
5702
5837
|
if (!options.allowIncomplete && !acceptanceCriteria.some((item) => item.required)) {
|
|
@@ -5757,9 +5892,10 @@ function renderRequirementsMarkdown(ledger) {
|
|
|
5757
5892
|
|
|
5758
5893
|
${items.length > 0 ? items.map((item) => `- ${item}`).join("\n") : "- \u65E0"}
|
|
5759
5894
|
`;
|
|
5760
|
-
const criteria = ledger.acceptanceCriteria.map(
|
|
5761
|
-
|
|
5762
|
-
|
|
5895
|
+
const criteria = ledger.acceptanceCriteria.map((item) => {
|
|
5896
|
+
const surfaces = item.verificationSurfaces ? ` / ${Object.entries(item.verificationSurfaces).map(([slot, surface]) => `${slot}=${surface}`).join(", ")}` : "";
|
|
5897
|
+
return `- **${item.id}** [${item.required ? "\u5FC5\u9700" : "\u53EF\u9009"} / ${item.method}${surfaces}] ${item.description}`;
|
|
5898
|
+
}).join("\n");
|
|
5763
5899
|
const coverage = ledger.coverage.map((item) => `- **${item.dimension}** [${item.status}] ${item.rationale}`).join("\n");
|
|
5764
5900
|
return `# Requirements
|
|
5765
5901
|
|
|
@@ -5800,6 +5936,32 @@ function isNonEmptyStringArray(value) {
|
|
|
5800
5936
|
function isVerificationMethod(value) {
|
|
5801
5937
|
return value === "automated" || value === "manual" || value === "hybrid";
|
|
5802
5938
|
}
|
|
5939
|
+
function parseVerificationSurfaces(value, method) {
|
|
5940
|
+
if (value === void 0) return void 0;
|
|
5941
|
+
if (!isRecord(value))
|
|
5942
|
+
throw new Error("invalid acceptance verificationSurfaces");
|
|
5943
|
+
const unknown = Object.keys(value).filter(
|
|
5944
|
+
(key) => key !== "automated" && key !== "manual"
|
|
5945
|
+
);
|
|
5946
|
+
if (unknown.length)
|
|
5947
|
+
throw new Error("invalid acceptance verificationSurfaces");
|
|
5948
|
+
const automated = parseVerificationSurface(value.automated);
|
|
5949
|
+
const manual = parseVerificationSurface(value.manual);
|
|
5950
|
+
if (method === "automated" && (automated === void 0 || manual !== void 0) || method === "manual" && (automated !== void 0 || manual === void 0) || method === "hybrid" && (automated === void 0 || manual === void 0)) {
|
|
5951
|
+
throw new Error("invalid acceptance verificationSurfaces");
|
|
5952
|
+
}
|
|
5953
|
+
return {
|
|
5954
|
+
...automated === void 0 ? {} : { automated },
|
|
5955
|
+
...manual === void 0 ? {} : { manual }
|
|
5956
|
+
};
|
|
5957
|
+
}
|
|
5958
|
+
function parseVerificationSurface(value) {
|
|
5959
|
+
if (value === void 0) return void 0;
|
|
5960
|
+
if (typeof value !== "string" || !VERIFICATION_SURFACES.has(value)) {
|
|
5961
|
+
throw new Error("invalid acceptance verification surface");
|
|
5962
|
+
}
|
|
5963
|
+
return value;
|
|
5964
|
+
}
|
|
5803
5965
|
function isRequirementDimension(value) {
|
|
5804
5966
|
return REQUIREMENT_DIMENSIONS.some((dimension) => dimension === value);
|
|
5805
5967
|
}
|
|
@@ -9677,7 +9839,7 @@ async function activateLegacyMigration(input) {
|
|
|
9677
9839
|
if (session === null || session.status !== "active") {
|
|
9678
9840
|
throw new Error("MANCODE_SESSION_NOT_FOUND");
|
|
9679
9841
|
}
|
|
9680
|
-
const store = new (await import("./store-
|
|
9842
|
+
const store = new (await import("./store-C3G3HN3N.js")).V3ContextStore(root);
|
|
9681
9843
|
const project = await store.readProjectSnapshot();
|
|
9682
9844
|
const stage = await readMigrationStage(root, input.stageId);
|
|
9683
9845
|
if (stage.state !== "staged" || stage.revision !== input.expectedStageRevision) {
|
|
@@ -9940,7 +10102,7 @@ async function activateLegacyMigration(input) {
|
|
|
9940
10102
|
for (const action of payload.actions.filter(
|
|
9941
10103
|
(action2) => action2.kind === "v3_adapter_file"
|
|
9942
10104
|
)) {
|
|
9943
|
-
const { applyV3AdapterFilePlan: applyV3AdapterFilePlan2 } = await import("./v3-adapter-
|
|
10105
|
+
const { applyV3AdapterFilePlan: applyV3AdapterFilePlan2 } = await import("./v3-adapter-7KIOYYWS.js");
|
|
9944
10106
|
await applyV3AdapterFilePlan2(root, action);
|
|
9945
10107
|
}
|
|
9946
10108
|
journal = await completeActivationStep(
|
|
@@ -12906,8 +13068,17 @@ async function executeOperationRecovery(input) {
|
|
|
12906
13068
|
assertUlid(input.operationId, "operation recovery operationId");
|
|
12907
13069
|
assertUlid(input.actorId, "operation recovery actorId");
|
|
12908
13070
|
assertUlid(input.sessionId, "operation recovery sessionId");
|
|
13071
|
+
if (input.replacementCheckpointId !== void 0) {
|
|
13072
|
+
assertUlid(
|
|
13073
|
+
input.replacementCheckpointId,
|
|
13074
|
+
"operation recovery replacementCheckpointId"
|
|
13075
|
+
);
|
|
13076
|
+
}
|
|
12909
13077
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
12910
13078
|
const mode = input.mode ?? "repair";
|
|
13079
|
+
if (mode === "abort" && input.replacementCheckpointId !== void 0) {
|
|
13080
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_INVALID");
|
|
13081
|
+
}
|
|
12911
13082
|
const stores = await knownOperationStores(input.projectRoot);
|
|
12912
13083
|
const located = await locateJournal(stores, input.operationId);
|
|
12913
13084
|
if (located === null) throw new Error("MANCODE_OPERATION_JOURNAL_NOT_FOUND");
|
|
@@ -12921,23 +13092,30 @@ async function executeOperationRecovery(input) {
|
|
|
12921
13092
|
});
|
|
12922
13093
|
assertRecoveryActor(initialPlan, input.actorId, input.sessionId);
|
|
12923
13094
|
if (located.journal.state === "committed" || located.journal.state === "aborted") {
|
|
13095
|
+
await assertTerminalCheckpointReplacementRetry(
|
|
13096
|
+
located.store,
|
|
13097
|
+
located.journal,
|
|
13098
|
+
input.replacementCheckpointId
|
|
13099
|
+
);
|
|
12924
13100
|
return {
|
|
12925
13101
|
state: "already_terminal",
|
|
12926
13102
|
journal: located.journal,
|
|
12927
13103
|
reason: "terminal"
|
|
12928
13104
|
};
|
|
12929
13105
|
}
|
|
13106
|
+
assertCheckpointReplacementSupported(
|
|
13107
|
+
located.journal,
|
|
13108
|
+
input.replacementCheckpointId
|
|
13109
|
+
);
|
|
12930
13110
|
const locks = await acquireRecoveryOperationLocks(
|
|
12931
13111
|
located.store,
|
|
12932
13112
|
located.journal,
|
|
12933
13113
|
stores,
|
|
12934
|
-
now
|
|
13114
|
+
now,
|
|
13115
|
+
input.replacementCheckpointId === void 0 ? [] : [`checkpoint:${input.replacementCheckpointId}`]
|
|
12935
13116
|
);
|
|
12936
13117
|
try {
|
|
12937
|
-
|
|
12938
|
-
located.store,
|
|
12939
|
-
input.operationId
|
|
12940
|
-
);
|
|
13118
|
+
let journal = await readOperationJournal(located.store, input.operationId);
|
|
12941
13119
|
if (journal === null)
|
|
12942
13120
|
throw new Error("MANCODE_OPERATION_JOURNAL_NOT_FOUND");
|
|
12943
13121
|
const plan = planOperationRecovery({
|
|
@@ -12946,9 +13124,18 @@ async function executeOperationRecovery(input) {
|
|
|
12946
13124
|
});
|
|
12947
13125
|
assertRecoveryActor(plan, input.actorId, input.sessionId);
|
|
12948
13126
|
if (journal.state === "committed" || journal.state === "aborted") {
|
|
13127
|
+
await assertTerminalCheckpointReplacementRetry(
|
|
13128
|
+
located.store,
|
|
13129
|
+
journal,
|
|
13130
|
+
input.replacementCheckpointId
|
|
13131
|
+
);
|
|
12949
13132
|
return { state: "already_terminal", journal, reason: "terminal" };
|
|
12950
13133
|
}
|
|
12951
|
-
|
|
13134
|
+
assertCheckpointReplacementSupported(
|
|
13135
|
+
journal,
|
|
13136
|
+
input.replacementCheckpointId
|
|
13137
|
+
);
|
|
13138
|
+
let payload = await loadBoundPayload(located.store, journal);
|
|
12952
13139
|
if (payload === null) {
|
|
12953
13140
|
if (plan.action === "safe_abort") {
|
|
12954
13141
|
const aborted = await abortOperation(
|
|
@@ -12971,6 +13158,28 @@ async function executeOperationRecovery(input) {
|
|
|
12971
13158
|
reason: "MANCODE_OPERATION_RECOVERY_PAYLOAD_REQUIRED"
|
|
12972
13159
|
};
|
|
12973
13160
|
}
|
|
13161
|
+
if (input.replacementCheckpointId === void 0 && payload.type === "reframe" && await reframeCheckpointReplacementProvenance(
|
|
13162
|
+
located.store,
|
|
13163
|
+
journal,
|
|
13164
|
+
payload
|
|
13165
|
+
) !== null) {
|
|
13166
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_REQUIRED");
|
|
13167
|
+
}
|
|
13168
|
+
let checkpointReplacement;
|
|
13169
|
+
if (input.replacementCheckpointId !== void 0) {
|
|
13170
|
+
const amended = await replaceReframeRecoveryCheckpoint({
|
|
13171
|
+
projectRoot: input.projectRoot,
|
|
13172
|
+
primaryStore: located.store,
|
|
13173
|
+
stores,
|
|
13174
|
+
journal,
|
|
13175
|
+
payload,
|
|
13176
|
+
replacementCheckpointId: input.replacementCheckpointId,
|
|
13177
|
+
now
|
|
13178
|
+
});
|
|
13179
|
+
journal = amended.journal;
|
|
13180
|
+
payload = amended.payload;
|
|
13181
|
+
checkpointReplacement = amended.checkpointReplacement;
|
|
13182
|
+
}
|
|
12974
13183
|
assertOperationRecoveryPayloadCoversJournal(journal, payload);
|
|
12975
13184
|
try {
|
|
12976
13185
|
await removePrivateWorkflowStaging(input.projectRoot, journal, payload);
|
|
@@ -12992,6 +13201,7 @@ async function executeOperationRecovery(input) {
|
|
|
12992
13201
|
}
|
|
12993
13202
|
if (mode === "abort") throw new Error("MANCODE_OPERATION_ABORT_UNSAFE");
|
|
12994
13203
|
try {
|
|
13204
|
+
await assertReframeRecoveryPlanCurrent(input.projectRoot, payload);
|
|
12995
13205
|
const repaired = await applyPayload(
|
|
12996
13206
|
input.projectRoot,
|
|
12997
13207
|
located.store,
|
|
@@ -13010,14 +13220,16 @@ async function executeOperationRecovery(input) {
|
|
|
13010
13220
|
return {
|
|
13011
13221
|
state: "repaired",
|
|
13012
13222
|
journal: repaired,
|
|
13013
|
-
reason: "forward_repair"
|
|
13223
|
+
reason: "forward_repair",
|
|
13224
|
+
...checkpointReplacement === void 0 ? {} : { checkpointReplacement }
|
|
13014
13225
|
};
|
|
13015
13226
|
} catch (error) {
|
|
13016
13227
|
const blocked = await markRepairRequired(located.store, journal, now);
|
|
13017
13228
|
return {
|
|
13018
13229
|
state: "repair_required",
|
|
13019
13230
|
journal: blocked,
|
|
13020
|
-
reason: error instanceof Error && error.message === "MANCODE_OPERATION_RECOVERY_CONFLICT" ? "MANCODE_OPERATION_RECOVERY_CONFLICT" : "MANCODE_OPERATION_RECOVERY_FAILED"
|
|
13231
|
+
reason: error instanceof Error && error.message === "MANCODE_OPERATION_RECOVERY_CONFLICT" ? "MANCODE_OPERATION_RECOVERY_CONFLICT" : "MANCODE_OPERATION_RECOVERY_FAILED",
|
|
13232
|
+
...checkpointReplacement === void 0 ? {} : { checkpointReplacement }
|
|
13021
13233
|
};
|
|
13022
13234
|
}
|
|
13023
13235
|
} finally {
|
|
@@ -13055,7 +13267,7 @@ async function readReservations(journal, stores) {
|
|
|
13055
13267
|
})
|
|
13056
13268
|
).then((values) => values.filter((value) => value !== null));
|
|
13057
13269
|
}
|
|
13058
|
-
async function acquireRecoveryOperationLocks(primaryStore, journal, stores, now) {
|
|
13270
|
+
async function acquireRecoveryOperationLocks(primaryStore, journal, stores, now, extraPrimaryEntityLocks = []) {
|
|
13059
13271
|
const byId = new Map(stores.map((store) => [store.storeId, store]));
|
|
13060
13272
|
const secondaryTargets = journal.secondaryReservations.map((reservation) => {
|
|
13061
13273
|
const store = byId.get(reservation.storeId);
|
|
@@ -13067,7 +13279,12 @@ async function acquireRecoveryOperationLocks(primaryStore, journal, stores, now)
|
|
|
13067
13279
|
return acquireOperationEntityLocks(
|
|
13068
13280
|
journal.operationId,
|
|
13069
13281
|
[
|
|
13070
|
-
{
|
|
13282
|
+
{
|
|
13283
|
+
store: primaryStore,
|
|
13284
|
+
entityLockKeys: [
|
|
13285
|
+
.../* @__PURE__ */ new Set([...journal.entityLocks, ...extraPrimaryEntityLocks])
|
|
13286
|
+
]
|
|
13287
|
+
},
|
|
13071
13288
|
...secondaryTargets
|
|
13072
13289
|
],
|
|
13073
13290
|
{ now }
|
|
@@ -13075,9 +13292,10 @@ async function acquireRecoveryOperationLocks(primaryStore, journal, stores, now)
|
|
|
13075
13292
|
}
|
|
13076
13293
|
async function loadBoundPayload(store, journal) {
|
|
13077
13294
|
if (journal.recoveryPayloadDigest === void 0) return null;
|
|
13078
|
-
const payload = await
|
|
13295
|
+
const payload = await readOperationRecoveryPayloadForDigest(
|
|
13079
13296
|
store,
|
|
13080
|
-
journal.operationId
|
|
13297
|
+
journal.operationId,
|
|
13298
|
+
journal.recoveryPayloadDigest
|
|
13081
13299
|
);
|
|
13082
13300
|
if (payload === null) {
|
|
13083
13301
|
throw new Error("MANCODE_OPERATION_RECOVERY_PAYLOAD_MISSING");
|
|
@@ -13088,6 +13306,301 @@ async function loadBoundPayload(store, journal) {
|
|
|
13088
13306
|
}
|
|
13089
13307
|
return parsed;
|
|
13090
13308
|
}
|
|
13309
|
+
function assertCheckpointReplacementSupported(journal, replacementCheckpointId) {
|
|
13310
|
+
if (replacementCheckpointId === void 0) return;
|
|
13311
|
+
if (journal.type !== "reframe" || journal.state !== "repair_required" || journal.recoveryPayloadDigest === void 0 || journal.secondaryReservations.length !== 0) {
|
|
13312
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_UNSUPPORTED");
|
|
13313
|
+
}
|
|
13314
|
+
}
|
|
13315
|
+
async function assertTerminalCheckpointReplacementRetry(store, journal, replacementCheckpointId) {
|
|
13316
|
+
if (replacementCheckpointId === void 0) return;
|
|
13317
|
+
if (journal.type !== "reframe" || journal.state !== "committed" || journal.recoveryPayloadDigest === void 0 || journal.secondaryReservations.length !== 0) {
|
|
13318
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_UNSUPPORTED");
|
|
13319
|
+
}
|
|
13320
|
+
const payload = await loadBoundPayload(store, journal);
|
|
13321
|
+
if (payload === null) {
|
|
13322
|
+
throw new Error("MANCODE_OPERATION_RECOVERY_PAYLOAD_REQUIRED");
|
|
13323
|
+
}
|
|
13324
|
+
assertOperationRecoveryPayloadCoversJournal(journal, payload);
|
|
13325
|
+
const replacement = await reframeCheckpointReplacementProvenance(
|
|
13326
|
+
store,
|
|
13327
|
+
journal,
|
|
13328
|
+
payload
|
|
13329
|
+
);
|
|
13330
|
+
if (replacement?.replacementCheckpointId !== replacementCheckpointId) {
|
|
13331
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_NOT_APPLICABLE");
|
|
13332
|
+
}
|
|
13333
|
+
}
|
|
13334
|
+
async function replaceReframeRecoveryCheckpoint(input) {
|
|
13335
|
+
const checkpointAction = singleReframeCheckpointAction(input.payload);
|
|
13336
|
+
const previousCheckpointId = checkpointAction.checkpoint.checkpointId;
|
|
13337
|
+
const priorReplacement = await reframeCheckpointReplacementProvenance(
|
|
13338
|
+
input.primaryStore,
|
|
13339
|
+
input.journal,
|
|
13340
|
+
input.payload
|
|
13341
|
+
);
|
|
13342
|
+
if (priorReplacement !== null) {
|
|
13343
|
+
if (priorReplacement.replacementCheckpointId !== input.replacementCheckpointId) {
|
|
13344
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_NOT_APPLICABLE");
|
|
13345
|
+
}
|
|
13346
|
+
await assertRecoveryPayloadState(
|
|
13347
|
+
input.projectRoot,
|
|
13348
|
+
input.stores,
|
|
13349
|
+
input.payload
|
|
13350
|
+
);
|
|
13351
|
+
return {
|
|
13352
|
+
journal: input.journal,
|
|
13353
|
+
payload: input.payload,
|
|
13354
|
+
checkpointReplacement: priorReplacement
|
|
13355
|
+
};
|
|
13356
|
+
}
|
|
13357
|
+
if (previousCheckpointId === input.replacementCheckpointId) {
|
|
13358
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_NOT_APPLICABLE");
|
|
13359
|
+
}
|
|
13360
|
+
const taskRootPath2 = await taskRoot(
|
|
13361
|
+
input.projectRoot,
|
|
13362
|
+
checkpointAction.checkpoint.taskRef
|
|
13363
|
+
);
|
|
13364
|
+
const currentCheckpoint = await readTaskCheckpointAtRoot(
|
|
13365
|
+
taskRootPath2,
|
|
13366
|
+
previousCheckpointId
|
|
13367
|
+
);
|
|
13368
|
+
if (!reframeCheckpointStepReached(input.journal) || currentCheckpoint === null || currentCheckpoint.operationId === input.journal.operationId || !sameTaskRef(
|
|
13369
|
+
currentCheckpoint.taskRef,
|
|
13370
|
+
checkpointAction.checkpoint.taskRef
|
|
13371
|
+
) || digestCanonicalJson(currentCheckpoint) === digestCanonicalJson(checkpointAction.checkpoint)) {
|
|
13372
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_NOT_APPLICABLE");
|
|
13373
|
+
}
|
|
13374
|
+
await assertRecoveryPayloadState(
|
|
13375
|
+
input.projectRoot,
|
|
13376
|
+
input.stores,
|
|
13377
|
+
input.payload,
|
|
13378
|
+
checkpointAction
|
|
13379
|
+
);
|
|
13380
|
+
await assertReframeRecoveryPlanCurrent(input.projectRoot, input.payload);
|
|
13381
|
+
const payload = buildReframeCheckpointReplacementPayload(
|
|
13382
|
+
input.payload,
|
|
13383
|
+
checkpointAction,
|
|
13384
|
+
input.replacementCheckpointId
|
|
13385
|
+
);
|
|
13386
|
+
const replacementAction = singleReframeCheckpointAction(payload);
|
|
13387
|
+
const existingReplacement = await readTaskCheckpointAtRoot(
|
|
13388
|
+
taskRootPath2,
|
|
13389
|
+
input.replacementCheckpointId
|
|
13390
|
+
);
|
|
13391
|
+
if (existingReplacement !== null && digestCanonicalJson(existingReplacement) !== digestCanonicalJson(replacementAction.checkpoint)) {
|
|
13392
|
+
throw new Error("MANCODE_REPLACEMENT_CHECKPOINT_ID_CONFLICT");
|
|
13393
|
+
}
|
|
13394
|
+
await assertRecoveryPayloadState(input.projectRoot, input.stores, payload);
|
|
13395
|
+
const fromKey = `checkpoint:${previousCheckpointId}`;
|
|
13396
|
+
const toKey = `checkpoint:${input.replacementCheckpointId}`;
|
|
13397
|
+
const payloadDigest = operationRecoveryPayloadDigest(payload);
|
|
13398
|
+
const journal = parseOperationJournal({
|
|
13399
|
+
...input.journal,
|
|
13400
|
+
recoveryPayloadDigest: payloadDigest,
|
|
13401
|
+
entityLocks: input.journal.entityLocks.map(
|
|
13402
|
+
(key) => key === fromKey ? toKey : key
|
|
13403
|
+
),
|
|
13404
|
+
expectedRevisions: Object.fromEntries(
|
|
13405
|
+
Object.entries(input.journal.expectedRevisions).map(([key, revision]) => [
|
|
13406
|
+
key === fromKey ? toKey : key,
|
|
13407
|
+
revision
|
|
13408
|
+
])
|
|
13409
|
+
),
|
|
13410
|
+
updatedAt: input.now.toISOString()
|
|
13411
|
+
});
|
|
13412
|
+
assertOperationJournalMatchesDefinition(journal);
|
|
13413
|
+
assertOperationRecoveryPayloadCoversJournal(journal, payload);
|
|
13414
|
+
await writeOperationRecoveryPayloadVersion(input.primaryStore, payload);
|
|
13415
|
+
const updatedJournal = await updateOperationJournal(
|
|
13416
|
+
input.primaryStore,
|
|
13417
|
+
journal,
|
|
13418
|
+
{
|
|
13419
|
+
canAbort: false,
|
|
13420
|
+
reframeCheckpointReplacement: {
|
|
13421
|
+
fromCheckpointId: previousCheckpointId,
|
|
13422
|
+
toCheckpointId: input.replacementCheckpointId
|
|
13423
|
+
}
|
|
13424
|
+
}
|
|
13425
|
+
);
|
|
13426
|
+
throwIfOperationCrashInjected("reframe", "rebind-reframe-checkpoint");
|
|
13427
|
+
return {
|
|
13428
|
+
journal: updatedJournal,
|
|
13429
|
+
payload,
|
|
13430
|
+
checkpointReplacement: {
|
|
13431
|
+
previousCheckpointId,
|
|
13432
|
+
replacementCheckpointId: input.replacementCheckpointId
|
|
13433
|
+
}
|
|
13434
|
+
};
|
|
13435
|
+
}
|
|
13436
|
+
function reframeCheckpointStepReached(journal) {
|
|
13437
|
+
const checkpointIndex = journal.steps.findIndex(
|
|
13438
|
+
(step) => step.id === "write-reframe-checkpoint"
|
|
13439
|
+
);
|
|
13440
|
+
return checkpointIndex >= 0 && journal.steps.slice(0, checkpointIndex).every((step) => step.state === "completed");
|
|
13441
|
+
}
|
|
13442
|
+
async function reframeCheckpointReplacementProvenance(store, journal, payload) {
|
|
13443
|
+
if (journal.type !== "reframe") return null;
|
|
13444
|
+
const canonicalValue = await readOperationRecoveryPayload(
|
|
13445
|
+
store,
|
|
13446
|
+
journal.operationId
|
|
13447
|
+
);
|
|
13448
|
+
if (canonicalValue === null) {
|
|
13449
|
+
throw new Error("MANCODE_OPERATION_RECOVERY_PAYLOAD_MISSING");
|
|
13450
|
+
}
|
|
13451
|
+
const canonical = parseOperationRecoveryPayload(canonicalValue);
|
|
13452
|
+
if (canonical.operationId !== journal.operationId || canonical.type !== journal.type || canonical.primaryStoreId !== journal.primaryStoreId) {
|
|
13453
|
+
throw new Error("MANCODE_OPERATION_RECOVERY_PAYLOAD_MISMATCH");
|
|
13454
|
+
}
|
|
13455
|
+
if (operationRecoveryPayloadDigest(canonical) === operationRecoveryPayloadDigest(payload)) {
|
|
13456
|
+
return null;
|
|
13457
|
+
}
|
|
13458
|
+
const previousCheckpointId = singleReframeCheckpointAction(canonical).checkpoint.checkpointId;
|
|
13459
|
+
const replacementCheckpointId = singleReframeCheckpointAction(payload).checkpoint.checkpointId;
|
|
13460
|
+
if (previousCheckpointId === replacementCheckpointId) return null;
|
|
13461
|
+
const expected = buildReframeCheckpointReplacementPayload(
|
|
13462
|
+
canonical,
|
|
13463
|
+
singleReframeCheckpointAction(canonical),
|
|
13464
|
+
replacementCheckpointId
|
|
13465
|
+
);
|
|
13466
|
+
if (operationRecoveryPayloadDigest(expected) !== operationRecoveryPayloadDigest(payload)) {
|
|
13467
|
+
throw new Error("MANCODE_OPERATION_RECOVERY_PAYLOAD_MISMATCH");
|
|
13468
|
+
}
|
|
13469
|
+
return { previousCheckpointId, replacementCheckpointId };
|
|
13470
|
+
}
|
|
13471
|
+
async function assertReframeRecoveryPlanCurrent(projectRoot, payload) {
|
|
13472
|
+
if (payload.type !== "reframe") return;
|
|
13473
|
+
const checkpointAction = singleReframeCheckpointAction(payload);
|
|
13474
|
+
const archiveAction = singleTaskArchiveAction(payload);
|
|
13475
|
+
const task = await new V3ContextStore(projectRoot).readTaskSnapshot(
|
|
13476
|
+
checkpointAction.checkpoint.taskRef
|
|
13477
|
+
);
|
|
13478
|
+
if ((task.plan?.digest ?? null) !== archiveAction.sourcePlanDigest) {
|
|
13479
|
+
throw new Error("MANCODE_OPERATION_RECOVERY_CONFLICT");
|
|
13480
|
+
}
|
|
13481
|
+
}
|
|
13482
|
+
function buildReframeCheckpointReplacementPayload(payload, checkpointAction, replacementCheckpointId) {
|
|
13483
|
+
const finalMetadataAction = singleTaskAuthorityAction(
|
|
13484
|
+
payload,
|
|
13485
|
+
"commit-reframed-metadata",
|
|
13486
|
+
"metadata.json"
|
|
13487
|
+
);
|
|
13488
|
+
const requirementsAction = singleTaskAuthorityAction(
|
|
13489
|
+
payload,
|
|
13490
|
+
"write-requirements-draft",
|
|
13491
|
+
"requirements.json"
|
|
13492
|
+
);
|
|
13493
|
+
const reviewAction = singleTaskAuthorityAction(
|
|
13494
|
+
payload,
|
|
13495
|
+
"mark-review-verification-stale",
|
|
13496
|
+
"review-ledger.json"
|
|
13497
|
+
);
|
|
13498
|
+
const verificationAction = singleTaskAuthorityAction(
|
|
13499
|
+
payload,
|
|
13500
|
+
"mark-review-verification-stale",
|
|
13501
|
+
"verification-ledger.json"
|
|
13502
|
+
);
|
|
13503
|
+
const archiveAction = singleTaskArchiveAction(payload);
|
|
13504
|
+
const metadata = parseWorkflowMetadata(
|
|
13505
|
+
JSON.parse(finalMetadataAction.targetContent)
|
|
13506
|
+
);
|
|
13507
|
+
if (metadata.lastOperationId !== payload.operationId || metadata.latestCheckpointRef?.kind !== "checkpoint" || metadata.latestCheckpointRef.artifactId !== checkpointAction.checkpoint.checkpointId) {
|
|
13508
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_INVALID");
|
|
13509
|
+
}
|
|
13510
|
+
const checkpoint = parseCheckpoint({
|
|
13511
|
+
...checkpointAction.checkpoint,
|
|
13512
|
+
checkpointId: replacementCheckpointId
|
|
13513
|
+
});
|
|
13514
|
+
const replacementMetadata = parseWorkflowMetadata({
|
|
13515
|
+
...metadata,
|
|
13516
|
+
latestCheckpointRef: {
|
|
13517
|
+
...metadata.latestCheckpointRef,
|
|
13518
|
+
artifactId: replacementCheckpointId
|
|
13519
|
+
}
|
|
13520
|
+
});
|
|
13521
|
+
const aggregate = buildTaskAggregateManifest({
|
|
13522
|
+
metadata: replacementMetadata,
|
|
13523
|
+
requirements: parseRequirementsLedger(
|
|
13524
|
+
JSON.parse(requirementsAction.targetContent)
|
|
13525
|
+
),
|
|
13526
|
+
review: parseReviewLedger(JSON.parse(reviewAction.targetContent)),
|
|
13527
|
+
verification: parseVerificationLedger(
|
|
13528
|
+
JSON.parse(verificationAction.targetContent)
|
|
13529
|
+
),
|
|
13530
|
+
planDigest: archiveAction.sourcePlanDigest,
|
|
13531
|
+
latestCheckpoint: checkpoint
|
|
13532
|
+
});
|
|
13533
|
+
const fenceActions = payload.actions.filter(
|
|
13534
|
+
(action) => action.kind === "task_head_fence"
|
|
13535
|
+
);
|
|
13536
|
+
if (fenceActions.length > 1) {
|
|
13537
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_INVALID");
|
|
13538
|
+
}
|
|
13539
|
+
return parseOperationRecoveryPayload({
|
|
13540
|
+
...payload,
|
|
13541
|
+
actions: payload.actions.map((action) => {
|
|
13542
|
+
if (action === checkpointAction) {
|
|
13543
|
+
return { ...action, checkpoint };
|
|
13544
|
+
}
|
|
13545
|
+
if (action === finalMetadataAction) {
|
|
13546
|
+
return {
|
|
13547
|
+
...action,
|
|
13548
|
+
targetContent: serializeTaskAuthority(replacementMetadata)
|
|
13549
|
+
};
|
|
13550
|
+
}
|
|
13551
|
+
if (action.kind === "task_head_fence") {
|
|
13552
|
+
return {
|
|
13553
|
+
...action,
|
|
13554
|
+
fence: parseTaskHeadFence({
|
|
13555
|
+
...action.fence,
|
|
13556
|
+
aggregateDigest: taskAggregateDigest(aggregate)
|
|
13557
|
+
})
|
|
13558
|
+
};
|
|
13559
|
+
}
|
|
13560
|
+
return action;
|
|
13561
|
+
})
|
|
13562
|
+
});
|
|
13563
|
+
}
|
|
13564
|
+
function singleReframeCheckpointAction(payload) {
|
|
13565
|
+
const matches = payload.actions.filter(
|
|
13566
|
+
(action) => action.kind === "checkpoint" && action.stepId === "write-reframe-checkpoint" && action.beforeDigest === null
|
|
13567
|
+
);
|
|
13568
|
+
if (matches.length !== 1 || matches[0] === void 0) {
|
|
13569
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_UNSUPPORTED");
|
|
13570
|
+
}
|
|
13571
|
+
return matches[0];
|
|
13572
|
+
}
|
|
13573
|
+
function singleTaskAuthorityAction(payload, stepId, fileName) {
|
|
13574
|
+
const matches = payload.actions.filter(
|
|
13575
|
+
(action) => action.kind === "task_authority_file" && action.stepId === stepId && action.fileName === fileName
|
|
13576
|
+
);
|
|
13577
|
+
if (matches.length !== 1 || matches[0] === void 0) {
|
|
13578
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_UNSUPPORTED");
|
|
13579
|
+
}
|
|
13580
|
+
return matches[0];
|
|
13581
|
+
}
|
|
13582
|
+
function singleTaskArchiveAction(payload) {
|
|
13583
|
+
const matches = payload.actions.filter(
|
|
13584
|
+
(action) => action.kind === "task_archive" && action.stepId === "archive-requirements-plan"
|
|
13585
|
+
);
|
|
13586
|
+
if (matches.length !== 1 || matches[0] === void 0) {
|
|
13587
|
+
throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_UNSUPPORTED");
|
|
13588
|
+
}
|
|
13589
|
+
return matches[0];
|
|
13590
|
+
}
|
|
13591
|
+
async function assertRecoveryPayloadState(projectRoot, stores, payload, ignoredAction) {
|
|
13592
|
+
for (const [index, action] of payload.actions.entries()) {
|
|
13593
|
+
if (action === ignoredAction) continue;
|
|
13594
|
+
const current = await currentActionDigest(projectRoot, stores, action);
|
|
13595
|
+
const target = recoveryActionTargetDigest(action);
|
|
13596
|
+
const laterTarget = payload.actions.slice(index + 1).some(
|
|
13597
|
+
(candidate) => recoveryActionResourceKey(candidate) === recoveryActionResourceKey(action) && recoveryActionTargetDigest(candidate) === current
|
|
13598
|
+
);
|
|
13599
|
+
if (current !== target && current !== action.beforeDigest && !laterTarget) {
|
|
13600
|
+
throw new Error("MANCODE_OPERATION_RECOVERY_CONFLICT");
|
|
13601
|
+
}
|
|
13602
|
+
}
|
|
13603
|
+
}
|
|
13091
13604
|
async function allActionsAtInitialState(projectRoot, stores, payload) {
|
|
13092
13605
|
const initialByResource = /* @__PURE__ */ new Map();
|
|
13093
13606
|
for (const action of payload.actions) {
|
|
@@ -13796,7 +14309,7 @@ async function upgradeV3Adapters(input) {
|
|
|
13796
14309
|
if (unfinished.length > 0) {
|
|
13797
14310
|
throw new Error("MANCODE_ADAPTER_UPGRADE_OPERATION_PENDING");
|
|
13798
14311
|
}
|
|
13799
|
-
const actualVersions = await import("./v3-adapter-
|
|
14312
|
+
const actualVersions = await import("./v3-adapter-7KIOYYWS.js").then(
|
|
13800
14313
|
({ inspectV3AdapterVersions: inspectV3AdapterVersions2 }) => inspectV3AdapterVersions2(
|
|
13801
14314
|
root,
|
|
13802
14315
|
managedAdapterNames(project.manifest.managedAdapters)
|
|
@@ -14808,6 +15321,7 @@ trigger: <\u5177\u4F53\u4E8B\u5B9E>
|
|
|
14808
15321
|
|
|
14809
15322
|
**\u6536\u5C3E\u9636\u6BB5**\uFF1A
|
|
14810
15323
|
- \u4E00\u8F6E\u4FEE\u590D\u540E\u91CD\u8DD1\u53D7\u5F71\u54CD\u7684 build/lint/typecheck/test \u548C\u5FC5\u8981 smoke test\uFF0C\u4E0D\u91CD\u590D\u8FD0\u884C\u5DF2\u5B8C\u6210 reviewer\u3002
|
|
15324
|
+
- ${ACCEPTED_STATE_NARRATIVE_GUIDANCE}
|
|
14811
15325
|
- \u751F\u6210 summary\uFF1A\u6539\u52A8/\u65B0\u5EFA\u6587\u4EF6\u3001\u590D\u7528\u8D44\u6E90\u3001\u9A8C\u8BC1\u7ED3\u679C\u3001\u5BA1\u67E5\u6DF1\u5EA6\u3001\u95EE\u9898\u5904\u7F6E\u3001\u8DF3\u8FC7\u6B65\u9AA4\u548C\u6B8B\u4F59\u98CE\u9669\u3002
|
|
14812
15326
|
- \u53EA\u6709\u9A8C\u8BC1\u901A\u8FC7\u3001\u6240\u9700\u5BA1\u67E5\u9886\u57DF\u5B8C\u6210\u4E14 blocker \u6E05\u96F6\u624D\u5EFA\u8BAE \`completed\`\uFF1B\u5426\u5219\u5199 \`blocked\` \u4E0E\u660E\u786E blockingReason\u3002
|
|
14813
15327
|
- \u5C06\u5173\u952E\u51B3\u7B56\u4EA4\u7ED9\u8C03\u7528\u65B9 appendTeamDecision\uFF0C\u5E76\u66F4\u65B0 Active Plans\u3002
|
|
@@ -15388,6 +15902,9 @@ trigger: <\u5177\u4F53\u4E8B\u5B9E>
|
|
|
15388
15902
|
- \u547D\u540D\u3001\u53EF\u8BFB\u6027\u3001DRY\u3001loading/error \u5F62\u5F0F\u7B49\u5EFA\u8BAE\u4E0D\u81EA\u52A8\u6269\u5927\u6539\u52A8\uFF1B\u4E0E\u9700\u6C42\u65E0\u5173\u65F6\u4E0D\u8F93\u51FA\u3002
|
|
15389
15903
|
- \u9274\u6743\u3001\u652F\u4ED8\u3001\u654F\u611F\u6570\u636E\u3001\u5220\u9664\u3001\u516C\u5F00 API\u3001\u672A\u53EF\u4FE1\u8F93\u5165\u3001\u5E76\u53D1\u6216\u57FA\u7840\u8BBE\u65BD\u7B49\u786C\u98CE\u9669\u540C\u6837\u5C5E\u4E8E\u5347\u7EA7\u4FE1\u53F7\uFF1B\u82E5\u6CA1\u6709\u89E6\u53D1 realignment\uFF0C\u7528\u6237\u660E\u786E\u9009\u62E9\u7EE7\u7EED solo \u540E\u53EF\u4EE5\u7EE7\u7EED\u3002
|
|
15390
15904
|
|
|
15905
|
+
### \u6700\u7EC8\u4EA4\u4ED8\u53D9\u4E8B
|
|
15906
|
+
${ACCEPTED_STATE_NARRATIVE_GUIDANCE}
|
|
15907
|
+
|
|
15391
15908
|
## \u4F60\u7684\u98CE\u683C
|
|
15392
15909
|
|
|
15393
15910
|
- \u76F4\u63A5\u3001\u7B80\u6D01\u3001\u4E0D\u5E9F\u8BDD
|
|
@@ -15403,7 +15920,11 @@ var CORE_CODING_PRINCIPLES = `## \u94C1\u5F8B\uFF08\u6C38\u4E0D\u8FDD\u53CD\uFF0
|
|
|
15403
15920
|
2. **\u5148\u9A8C\u8BC1\u518D\u58F0\u79F0\u5B8C\u6210** \u2014 \u5F00\u5DE5\u524D\u5199\u660E\u5B9E\u8D28\u5047\u8BBE\u548C\u53EF\u9A8C\u8BC1\u6210\u529F\u6807\u51C6\uFF1Bbuild/lint/test \u5FC5\u987B\u5B9E\u9645\u8DD1
|
|
15404
15921
|
3. **\u5931\u8D25\u4E24\u6B21\u5FC5\u987B\u505C\u4E0B** \u2014 \u4E0D\u76F2\u8BD5
|
|
15405
15922
|
4. **\u4E0D\u53EF\u9006\u64CD\u4F5C\u5148\u95EE** \u2014 \u5220\u9664\u3001force push\u3001worktree \u5408\u5E76
|
|
15406
|
-
5. **\u53EA\u89E3\u51B3\u88AB\u95EE\u5230\u7684\u95EE\u9898** \u2014 \u4F18\u5148\u590D\u7528\u73B0\u6709\u5B9E\u73B0\uFF0C\u9009\u62E9\u6EE1\u8DB3\u9A8C\u6536\u7684\u6700\u5C0F\u76F4\u63A5\u6539\u52A8\uFF1B\u4E0D\u52A0\u63A8\u6D4B\u6027\u529F\u80FD\u3001\u4E00\u6B21\u6027\u62BD\u8C61\u3001\u65E0\u5173\u6E05\u7406\u6216\u591A\u4F59\u914D\u7F6E
|
|
15923
|
+
5. **\u53EA\u89E3\u51B3\u88AB\u95EE\u5230\u7684\u95EE\u9898** \u2014 \u4F18\u5148\u590D\u7528\u73B0\u6709\u5B9E\u73B0\uFF0C\u9009\u62E9\u6EE1\u8DB3\u9A8C\u6536\u7684\u6700\u5C0F\u76F4\u63A5\u6539\u52A8\uFF1B\u4E0D\u52A0\u63A8\u6D4B\u6027\u529F\u80FD\u3001\u4E00\u6B21\u6027\u62BD\u8C61\u3001\u65E0\u5173\u6E05\u7406\u6216\u591A\u4F59\u914D\u7F6E
|
|
15924
|
+
|
|
15925
|
+
## \u6700\u7EC8\u4EA4\u4ED8\u53D9\u4E8B
|
|
15926
|
+
|
|
15927
|
+
${ACCEPTED_STATE_NARRATIVE_GUIDANCE}`;
|
|
15407
15928
|
|
|
15408
15929
|
// src/templates/skills/man.ts
|
|
15409
15930
|
var MAN_SKILL = {
|
|
@@ -15415,6 +15936,10 @@ var MAN_SKILL = {
|
|
|
15415
15936
|
|
|
15416
15937
|
\u7528 \`mancode workflow create man "<task>" --json\` \u521B\u5EFA workflow \u5E76\u8BFB\u53D6\u8FD4\u56DE\u7684 taskId\uFF1B\u4E0D\u5F97\u76F4\u63A5\u521B\u5EFA\u6216\u6539\u5199 metadata.json\u3002\u968F\u540E\u53EA\u7528 workflow CLI \u66F4\u65B0\u9700\u6C42\u3001\u9A8C\u8BC1\u3001step/status/planVersion/skippedSteps\u3002\u901A\u7528 \`--skipped\` \u53EA\u7528\u4E8E Step 1\u20132 \u7684 \`clarification\`\uFF1B\u7528\u6237\u660E\u786E\u8DF3\u8FC7\u6574\u4E2A review \u65F6\u5FC5\u987B\u5728 Step 6 \u4F7F\u7528\u4E13\u7528 \`workflow review ... skip --reason\`\uFF0C\u4E0D\u80FD\u5199\u6570\u5B57\u3001\`film-1\` \u6216 \`film-2\`\u3002state \u7684 \`currentMode\`\u3001\`currentTask\` \u548C \`currentWorkflowMode\` \u6307\u5411\u672C task\u3002
|
|
15417
15938
|
|
|
15939
|
+
\u5F53\u4EFB\u52A1\u663E\u5F0F\u4F7F\u7528 document-bound delivery\uFF08\`mancode workflow create man "<task>" --delivery\`\uFF0Cplanning policy 3\uFF09\u65F6\uFF0C\u4EE5\u4E0B\u4EA4\u4ED8\u89C4\u5219\u53EA\u5BF9\u8BE5\u65B0 \`/man\` \u4EFB\u52A1\u751F\u6548\uFF1B\u666E\u901A \`/man\`\u3001\u65E7\u4EFB\u52A1\u3001\`/manba\`\u3001\`/manteam\`\u3001\`/manps\`\u3001\`/mansolo\` \u548C Solo handoff \u4FDD\u6301\u539F\u534F\u8BAE\u3002\u8BA1\u5212\u5FC5\u987B\u7ED1\u5B9A\u9879\u76EE\u6307\u5B9A\u7684\u8BA1\u5212\u6587\u4EF6\uFF08\u9ED8\u8BA4 \`doc/\`\uFF0C\u5DF2\u6709 \`docs/\` \u7EA6\u5B9A\u4F18\u5148\uFF09\uFF0C\u6A21\u5757\u5B8C\u6210\u540E\u628A\u5B9E\u9645\u4EA4\u4ED8\u56DE\u5199\u5230\u540C\u4E00\u6587\u4EF6\u7684 delivery record\uFF0C\u518D\u505A\u4E00\u6B21\u6A21\u5757\u603B\u5BA1\u3002\u603B\u5BA1\u8BFB\u53D6\u5DF2\u6279\u51C6\u8BA1\u5212\u3001\u76F8\u5173\u67B6\u6784\u3001\u5B8C\u6574\u6A21\u5757 diff\u3001\u5165\u53E3/\u8C03\u7528\u94FE\u548C\u9A8C\u8BC1\u8BC1\u636E\uFF0C\u68C0\u67E5\u76EE\u6807\u8986\u76D6\u3001\u771F\u5B9E\u6B63\u786E\u6027/\u6F0F\u6D1E\uFF0C\u4EE5\u53CA\u65E0\u4F9D\u636E\u7684\u9632\u5FA1\u548C\u62BD\u8C61\uFF1B\u5B83\u4E0D\u80FD\u53EA\u542C\u5B9E\u73B0\u8005\u603B\u7ED3\u3002reviewer \u8FDB\u7A0B\u9000\u51FA\u7801\u4E3A 0 \u6216\u81EA\u7136\u8BED\u8A00\u8BF4\u201C\u901A\u8FC7\u201D\u90FD\u4E0D\u4EE3\u8868 review \u5DF2\u5199\u5165\uFF1A\u5FC5\u987B\u91CD\u65B0\u8FD0\u884C \`mancode workflow delivery <TaskRef> inspect --json\`\uFF0C\u770B\u5230 ledger \u4ECD\u4E3A \`pending\`\u3001\`in_review\`\u3001\`stale\` \u6216 \`blocked\` \u65F6\uFF0C\u62A5\u544A \`review_incomplete\` \u5E76\u7EE7\u7EED\u5BA1\u6838\u6216\u4FEE\u590D\u3002\`reviewer: independent\` \u53EA\u662F\u8C03\u7528\u8005\u81EA\u8FF0\u7684\u5BA1\u6838\u5143\u6570\u636E\uFF0C\u4E0D\u662F actor/session \u8EAB\u4EFD\u8BA4\u8BC1\uFF0C\u4E0D\u80FD\u636E\u6B64\u5BA3\u79F0\u7CFB\u7EDF\u5DF2\u8BC1\u660E\u72EC\u7ACB\u5BA1\u6838\u3002
|
|
15940
|
+
|
|
15941
|
+
delivery requirements \u7684\u6BCF\u4E2A\u5FC5\u9700\u9A8C\u6536\u9879\u90FD\u5FC5\u987B\u7528 \`verificationSurfaces\` \u6309 automated/manual slot \u58F0\u660E\u671F\u671B observation surface\uFF1Bdelivery \u9A8C\u8BC1\u8F93\u5165\u518D\u58F0\u660E\u5B9E\u9645 surface\uFF1A\`unit\`\u3001\`component\`\u3001\`handler\`\u3001\`real_http\`\u3001\`browser\`\u3001\`device\`\u3001\`external_service\` \u6216 \`manual_observation\`\u3002\u4F8B\u5982\u771F\u5B9E HTTP \u7684\u671F\u671B\u548C\u5B9E\u9645\u90FD\u5FC5\u987B\u662F \`real_http\`\uFF1B\u4E0D\u80FD\u4ECE\u547D\u4EE4\u540D\u731C\u6D4B\uFF0C\u4E5F\u4E0D\u80FD\u628A handler/mock/\u622A\u56FE\u5F53\u6210\u771F\u5B9E HTTP\u3002\u7F3A\u5C11\u6216\u4E0D\u5339\u914D\u7684\u8BC1\u636E\u4FDD\u6301\u672A\u9A8C\u8BC1\u3002\u5B8C\u6210\u524D\u4EE5 \`delivery check --json\` \u7684\u7ED3\u6784\u5316 finalization blockers \u4E3A\u51C6\uFF0C\u5148\u89E3\u51B3 review\u3001verification\u3001delivery record\u3001scope \u548C\u672A\u63D0\u4EA4\u53D8\u66F4\uFF0C\u518D\u8FD0\u884C \`workflow complete\`\uFF1B\u8303\u56F4\u5916\u672A\u63D0\u4EA4\u6587\u4EF6\u5FC5\u987B\u5148\u79FB\u51FA\u3001stash \u6216\u5355\u72EC\u63D0\u4EA4\uFF0C\u4E0D\u80FD\u6DF7\u5165\u672C\u4EFB\u52A1\u63D0\u4EA4\u3002\u6CA1\u6709 upstream \u6216 push \u5931\u8D25\u8868\u793A\u672A\u53D1\u5E03\uFF0C\u4E0D\u662F\u4E1A\u52A1\u963B\u585E\u3002include/exclude \u53EA\u63A5\u6536 repo-relative path \u6216 glob\uFF0C\u8BED\u4E49\u8303\u56F4\u5199\u5728 requirements\uFF1B\u8BA1\u5212\u8DEF\u5F84\u8D8A\u754C\u65F6\u6309 CLI \u7ED9\u51FA\u7684\u5177\u4F53\u8DEF\u5F84\u4FEE\u6B63\uFF0C\u4E0D\u8981\u9760\u731C\u6D4B\u8BCD\u8868\u6269\u5927\u9632\u5FA1\u3002
|
|
15942
|
+
|
|
15418
15943
|
## \u8BA1\u5212\u804C\u8D23\u4E0E\u6280\u672F\u9009\u62E9\u5173\u5361
|
|
15419
15944
|
|
|
15420
15945
|
\u628A \`/man\` \u4F5C\u4E3A\u6B63\u5F0F\u8BA1\u5212\u5165\u53E3\uFF1A\u5148\u5BF9\u9F50\u9700\u6C42\u5E76\u4EA7\u51FA\u53EF\u786E\u8BA4\u7684\u8BA1\u5212\uFF0C\u4E0D\u56E0\u8BA1\u5212\u5B8C\u6210\u800C\u81EA\u52A8\u8FDB\u5165\u5B8C\u6574\u5B9E\u65BD\u3002\u8BA1\u5212\u5173\u5361\u5FC5\u987B\u8BA9\u7528\u6237\u9009\u62E9\u53EA\u4FDD\u7559\u8BA1\u5212\u3001\u4EA4\u7ED9\u9ED8\u8BA4 \`solo\` \u8F7B\u91CF\u5B9E\u65BD\u3001\u7EE7\u7EED\u5B8C\u6574 \`/man\` \u6216\u4FEE\u6539\u8BA1\u5212\u3002
|
|
@@ -15438,7 +15963,7 @@ var MAN_SKILL = {
|
|
|
15438
15963
|
|
|
15439
15964
|
\u6309\u4EFB\u52A1\u5B9E\u9645\u9002\u7528\u8303\u56F4\u68C0\u67E5\uFF1A\u7528\u6237\u76EE\u6807\u4E0E\u5E73\u53F0\u3001\u6838\u5FC3\u6D41\u7A0B\u3001\u9996\u671F\u8303\u56F4\u3001\u6392\u9664\u9879\u3001\u6280\u672F\u4E0E\u8FD0\u884C\u7EA6\u675F\u3001\u6570\u636E/\u72B6\u6001/\u96C6\u6210\u3001\u6027\u80FD/\u517C\u5BB9\u6027/\u5B89\u5168\u3001\u53EF\u9A8C\u8BC1\u5B8C\u6210\u6807\u51C6\u3002\u8FDE\u7EED\u6F84\u6E05\u6CA1\u6709\u51CF\u5C11 blocking \u9879\u65F6\uFF0C\u4E0D\u6563\u95EE\uFF1B\u8BA9\u7528\u6237\u9009\u62E9\u7F29\u5C0F\u9996\u671F\u8303\u56F4\u3001\u63A5\u53D7\u5217\u660E\u7684\u63A8\u8350\u9ED8\u8BA4\u503C\u6216\u6682\u505C\u3002
|
|
15440
15965
|
|
|
15441
|
-
\u628A\u7ED3\u8BBA\u5199\u5165\u7ED3\u6784\u5316\u9700\u6C42\u8F93\u5165\uFF0C\u81F3\u5C11\u5305\u542B \`goal\`\u3001\u975E\u7A7A \`confirmedScope\`\u3001\`excludedScope\`\u3001\`technicalDecisions\`\u3001\`defaults\`\u3001\`blockingUnknowns\`\u3001\`coverage\` \u548C \`acceptanceCriteria\`\u3002coverage \u5FC5\u987B\u9010\u9879\u8BF4\u660E platform\u3001core_scope\u3001technical_stack\u3001data_and_persistence\u3001performance\u3001compatibility\u3001security \u662F \`confirmed\`\u3001\`defaulted\` \u6216\u5E26\u7406\u7531\u7684 \`not_applicable\`\uFF0C\u4E0D\u80FD\u7528\u7A7A\u6570\u7EC4\u5047\u88C5\u5DF2\u7ECF\u8003\u8651\u3002\u6BCF\u4E2A\u6838\u5FC3\u7528\u6237\u884C\u4E3A\u5FC5\u987B\u6709\u7A33\u5B9A\u9A8C\u6536 ID\uFF08\`AC-1\` \u7B49\uFF09\u3001\u63CF\u8FF0\u3001\`required\` \u548C\u9A8C\u8BC1\u65B9\u5F0F \`automated\` / \`manual\` / \`hybrid\`\
|
|
15966
|
+
\u628A\u7ED3\u8BBA\u5199\u5165\u7ED3\u6784\u5316\u9700\u6C42\u8F93\u5165\uFF0C\u81F3\u5C11\u5305\u542B \`goal\`\u3001\u975E\u7A7A \`confirmedScope\`\u3001\`excludedScope\`\u3001\`technicalDecisions\`\u3001\`defaults\`\u3001\`blockingUnknowns\`\u3001\`coverage\` \u548C \`acceptanceCriteria\`\u3002coverage \u5FC5\u987B\u9010\u9879\u8BF4\u660E platform\u3001core_scope\u3001technical_stack\u3001data_and_persistence\u3001performance\u3001compatibility\u3001security \u662F \`confirmed\`\u3001\`defaulted\` \u6216\u5E26\u7406\u7531\u7684 \`not_applicable\`\uFF0C\u4E0D\u80FD\u7528\u7A7A\u6570\u7EC4\u5047\u88C5\u5DF2\u7ECF\u8003\u8651\u3002\u6BCF\u4E2A\u6838\u5FC3\u7528\u6237\u884C\u4E3A\u5FC5\u987B\u6709\u7A33\u5B9A\u9A8C\u6536 ID\uFF08\`AC-1\` \u7B49\uFF09\u3001\u63CF\u8FF0\u3001\`required\` \u548C\u9A8C\u8BC1\u65B9\u5F0F \`automated\` / \`manual\` / \`hybrid\`\uFF1Bdocument-bound delivery \u7684\u5FC5\u9700\u9879\u8FD8\u8981\u63D0\u4F9B\u4E0E method \u69FD\u4F4D\u4E00\u81F4\u7684 \`verificationSurfaces\`\uFF0C\u4F8B\u5982 \`{ "automated": "real_http" }\`\uFF0Chybrid \u540C\u65F6\u63D0\u4F9B automated \u548C manual\u3002\u81F3\u5C11\u4E00\u4E2A\u9A8C\u6536\u9879\u5FC5\u9700\u3002\u8FD0\u884C \`mancode workflow requirements <taskId> finalize --file <requirements-input.json>\`\uFF0C\u7531 CLI \u6821\u9A8C\u5E76\u751F\u6210\u6743\u5A01 \`requirements.json\` \u4E0E \`requirements.md\`\uFF0C\u4E0D\u8981\u624B\u5DE5\u5236\u9020\u4E24\u4EFD\u53EF\u80FD\u51B2\u7A81\u7684\u7ED3\u8BBA\u3002\u65E0\u987B\u63D0\u95EE\u65F6\u53EF\u628A \`clarification\` \u8BB0\u5165\u7D2F\u8BA1 skippedSteps\uFF0C\u4F46\u4E0D\u80FD\u8DF3\u8FC7\u9700\u6C42\u6458\u8981\u4E0E\u5C31\u7EEA\u5224\u65AD\u3002\u4ECD\u6709 blocking \u9879\u65F6\u505C\u5728 Step 2\uFF1B\u53EA\u6709 CLI \u8FD4\u56DE ready \u624D\u8FD0\u884C \`mancode workflow update <taskId> --step 3\`\u3002
|
|
15442
15967
|
|
|
15443
15968
|
### Step 3: Plan Coach \u51FA\u8BA1\u5212
|
|
15444
15969
|
|
|
@@ -15492,7 +16017,7 @@ Plan Coach \u5FC5\u987B\u8BC1\u660E\u6240\u6709\u9009\u9879\u89E3\u51B3\u540C\u4
|
|
|
15492
16017
|
|
|
15493
16018
|
1. \u5B58\u5728 open blocker \u65F6\uFF0CHead Coach \u4E00\u6B21\u6027\u4FEE\u590D\u5168\u90E8 blocker\uFF0C\u5E76\u7528 \`mancode workflow review <taskId> remediate --resolved Q1,D1\` \u8BB0\u5F55\u552F\u4E00\u4E00\u8F6E\u4FEE\u590D\uFF1B\u6CA1\u6709 blocker \u65F6\u4E0D\u8FD0\u884C remediate\u3002\u4E0D\u8981\u4E3A \u{1F7E1}/\u{1F7E2} \u6269\u5927\u6539\u52A8\u3002
|
|
15494
16019
|
2. remediation \u4F1A\u4F7F\u65E7\u9A8C\u8BC1\u6574\u6279\u5931\u6548\u3002\u5728 Step 9 \u91CD\u8DD1\u5168\u90E8 required \u9A8C\u6536\uFF0C\u5E76\u901A\u8FC7 verify record/require-manual/confirm-manual \u91CD\u65B0\u767B\u8BB0\u8BC1\u636E\uFF1B\u672A\u91CD\u65B0\u5168\u90E8\u901A\u8FC7\u4E0D\u80FD completed\u3002\u4E0D\u91CD\u65B0\u8FD0\u884C\u5DF2\u5B8C\u6210\u7684 reviewer\u3002\u4FEE\u590D\u82E5\u5F15\u5165\u65B0\u7684\u9AD8\u98CE\u9669\u9762\u5219\u6807\u8BB0 blocked\uFF0C\u4E0D\u80FD\u5F00\u542F\u65E0\u754C review \u5FAA\u73AF\u3002
|
|
15495
|
-
3. \u5199 \`summary.md
|
|
16020
|
+
3. \u57FA\u4E8E accepted target\u3001\u4EFB\u52A1\u8D77\u59CB baseline\u3001\u5B9E\u9645\u8BFB\u56DE\u72B6\u6001\u548C task-owned diff \u5199 \`summary.md\` \u53CA\u4EFB\u4F55 commit/PR \u6587\u6848\uFF1A\u8BB0\u5F55\u6539\u52A8\u3001\u65B0\u5EFA\u3001\u590D\u7528\u3001\u9A8C\u8BC1\u3001\u5BA1\u67E5\u6DF1\u5EA6\u3001findings \u5904\u7F6E\u3001\u8DF3\u8FC7\u6B65\u9AA4\u548C\u6B8B\u4F59\u98CE\u9669\uFF1B\u4FDD\u7559 \`excludedScope\`\u3001\u5931\u8D25\u9A8C\u8BC1\u3001blocker\u3001\u8FC1\u79FB/\u56DE\u6EDA\u548C\u5BA1\u8BA1\u4E8B\u5B9E\u3002
|
|
15496
16021
|
4. CLI \u786E\u8BA4\u6240\u9700\u5BA1\u67E5\u9886\u57DF\u5B8C\u6210\u4E14 blocker \u6E05\u96F6\u540E\u624D\u5199 \`completed\`\uFF1B\u5426\u5219\u7528 \`--status blocked --blocking-reason "<\u539F\u56E0>"\`\u3002
|
|
15497
16022
|
5. \u5173\u952E\u51B3\u7B56 appendTeamDecision \u5230 \`decisions.md\`\uFF0C\u66F4\u65B0 Active Plans\u3002\u53D1\u73B0\u65B0\u7684\u9AD8\u9891\u9886\u57DF\u8BCD\u65F6\u5411\u7528\u6237\u63D0\u8BAE\uFF0C\u7ECF\u7528\u6237\u786E\u8BA4\u540E\u7528 \`mancode context glossary add\` \u767B\u8BB0\uFF0C\u4E0D\u5F97\u672A\u7ECF\u786E\u8BA4\u5199\u5165\u672F\u8BED\u8868\u3002
|
|
15498
16023
|
6. worktree \u5408\u5E76\u524D\u53D6\u5F97\u7528\u6237\u786E\u8BA4\uFF1B\u7EC8\u6001\u5199\u5165\u6210\u529F\u540E state \u56DE solo \u5E76\u6E05\u7A7A workflow \u6307\u9488\u3002
|
|
@@ -15540,7 +16065,7 @@ var MAMBA_SKILL = {
|
|
|
15540
16065
|
|
|
15541
16066
|
## 5. \u771F\u5B9E\u56DE\u5F52\u4E0E\u7ED3\u8BBA
|
|
15542
16067
|
|
|
15543
|
-
\u91CD\u8DD1\u539F\u8DEF\u5F84\uFF0C\u8986\u76D6\u53D7\u5F71\u54CD\u7684\u5173\u952E\u6B63\u5411\u3001\u8D1F\u5411\u548C\u6743\u9650\u8FB9\u754C\u8DEF\u5F84\uFF0C\u5E76\u8FD0\u884C\u5FC5\u8981 build/lint/typecheck/test\u3002\u5199 \`mamba-report.md\`\uFF1A\u73AF\u5883\u3001\u6B65\u9AA4\u3001\u7ED3\u679C\u3001\u4EA7\u7269\u8DEF\u5F84\u3001\u56DE\u5F52\u8303\u56F4\u3001\u98CE\u9669\u4E0E\u5EFA\u8BAE\u3002
|
|
16068
|
+
\u91CD\u8DD1\u539F\u8DEF\u5F84\uFF0C\u8986\u76D6\u53D7\u5F71\u54CD\u7684\u5173\u952E\u6B63\u5411\u3001\u8D1F\u5411\u548C\u6743\u9650\u8FB9\u754C\u8DEF\u5F84\uFF0C\u5E76\u8FD0\u884C\u5FC5\u8981 build/lint/typecheck/test\u3002\u57FA\u4E8E\u5B9E\u9645\u89C2\u5BDF\u5230\u7684\u6700\u7EC8\u72B6\u6001\u5199 \`mamba-report.md\`\uFF1A\u73AF\u5883\u3001\u6B65\u9AA4\u3001\u7ED3\u679C\u3001\u4EA7\u7269\u8DEF\u5F84\u3001\u56DE\u5F52\u8303\u56F4\u3001\u98CE\u9669\u4E0E\u5EFA\u8BAE\uFF1B\u62A5\u544A\u5FC5\u987B\u5982\u5B9E\u4FDD\u7559 no_repro\u3001blocked/manual_test_required\u3001\u5931\u8D25\u9A8C\u8BC1\u3001\u8D1F\u5411\u6216\u6743\u9650\u7ED3\u679C\u548C\u6B8B\u4F59\u98CE\u9669\u3002
|
|
15544
16069
|
|
|
15545
16070
|
- \u5DF2\u4FEE\u590D\uFF1A\`status: "completed", outcome: "fixed"\`
|
|
15546
16071
|
- \u5DF2\u9A8C\u8BC1\u4F46\u672A\u6539\u4EE3\u7801\uFF1A\`status: "completed", outcome: "verified"\`
|
|
@@ -15844,6 +16369,8 @@ var MANTEAM_SKILL = {
|
|
|
15844
16369
|
- Follow-up TODOs, only if unavoidable
|
|
15845
16370
|
- Suggested commit message
|
|
15846
16371
|
|
|
16372
|
+
handoff\u3001commit \u548C PR \u6587\u6848\u5FC5\u987B\u6765\u81EA accepted target\u3001\u4EFB\u52A1\u8D77\u59CB baseline\u3001\u5B9E\u9645\u8BFB\u56DE\u72B6\u6001\u4E0E task-owned diff\u3002\u4E0D\u5F97\u8BA9\u4F1A\u8BDD\u4E2D\u88AB\u5426\u51B3\u7684\u65B9\u6848\u5B9A\u4E49\u4EA4\u4ED8\u8EAB\u4EFD\uFF0C\u4F46\u5FC5\u987B\u4FDD\u7559\u51B2\u7A81\u3001\u672A\u5B8C\u6210\u5DE5\u4F5C\u3001\u5931\u8D25\u9A8C\u8BC1\u3001blocker\u3001\u8FC1\u79FB/\u56DE\u6EDA\u3001\u5BA1\u8BA1\u4E8B\u5B9E\uFF0C\u4EE5\u53CA\u6B63\u5F0F handoff \u7684 rejected/cancelled \u72B6\u6001\u548C reason\u3002
|
|
16373
|
+
|
|
15847
16374
|
\u53EA\u6709\u5728\u7528\u6237\u786E\u8BA4\u5B9E\u65BD\u5E76\u5B8C\u6210 workflow \u540E\uFF0C\u624D\u628A\u6700\u7EC8 ADR \u8FFD\u52A0\u5230 \`.mancode/memory/decisions.md\`\u3002abandoned / plan-only workflow \u53EA\u80FD\u4FDD\u7559\u5728 \`.mancode/workflows/<taskId>/team-context.md\`\u3001\`plan.md\` \u548C \`handoff.md\`\uFF0C\u4E0D\u80FD\u6C61\u67D3\u957F\u671F\u56E2\u961F memory\u3002
|
|
15848
16375
|
|
|
15849
16376
|
## Commit Discipline
|
|
@@ -16814,7 +17341,7 @@ var MODE_META = {
|
|
|
16814
17341
|
" If environment, permission, or data is missing, set blocked with `--blocking-reason` and keep state resumable instead of advancing.",
|
|
16815
17342
|
"3. Reproduce the shortest path and write `diagnosis.md` with evidence, root cause or candidates, impact, and confidence; then update to Step 4.",
|
|
16816
17343
|
"4. Apply only the requested minimum fix, or make no code change when the user requested validation only. New architecture or cross-module scope returns to /man. Then update to Step 5.",
|
|
16817
|
-
"5. Re-run the path and affected positive, negative, and permission boundaries; write `mamba-report.md` with environment, evidence, artifacts, regression scope, and risks.",
|
|
17344
|
+
"5. Re-run the path and affected positive, negative, and permission boundaries; write `mamba-report.md` from the observed final state with environment, evidence, artifacts, regression scope, and risks. Preserve authoritative no_repro, blocked/manual_test_required, failed validation, negative or permission results, and residual risks.",
|
|
16818
17345
|
"",
|
|
16819
17346
|
"Finish through the CLI with exactly one result: completed+fixed, completed+verified, completed+no_repro, completed+manual_test_required, or blocked+blockingReason. Never claim real testing in restricted mode. Child blocked/manual_test_required propagation is handled by the CLI. After a child finishes with fixed/verified/no_repro, inspect its parent; if that parent is still blocked because of this child, run `mancode workflow update <parentTaskId> --status in_progress` before restoring parent state at Step 6. Never auto-resume manual_test_required."
|
|
16820
17347
|
].join("\n")
|
|
@@ -16840,7 +17367,7 @@ var MODE_META = {
|
|
|
16840
17367
|
"Step 6 \u2014 Run `workflow verify <taskId> init`, execute detected build/lint/typecheck/test and smoke checks, and record every required acceptance ID with reproducible evidence; automated passed/failed records include the command and exit code. Use require-manual when a foreground browser, device, or human judgment is necessary; stop for explicit user confirmation before confirm-manual. The CLI blocks Step 7 and review until all checks pass. Then write `review-scope.md` and initialize targeted or full review. An explicit user review skip uses `workflow review <taskId> skip --reason <reason>` at Step 6, never generic skipped metadata.",
|
|
16841
17368
|
"Step 7 \u2014 Run one quality review limited to the changed diff and direct impact. Compare every changed path and behavior with confirmed requirements, acceptance IDs, and implementation scope; any unauthorized behavior, path outside include, or path matching exclude is a blocker. Findings require changed-line evidence and user impact, with at most three new findings. Record stable blocker IDs through `workflow review ... complete`; do not fix yet.",
|
|
16842
17369
|
"Step 8 \u2014 Only full review runs the security/boundary reviewer. It must read Film #1, mark the same root cause duplicate, and stay within security, permissions, recovery, resources, and boundaries. A targeted review treats the second domain as not applicable; it is never recorded as a skipped step.",
|
|
16843
|
-
"Step 9 \u2014 If open blockers exist, fix them in one remediation round and record resolved IDs through `workflow review ... remediate`; remediation invalidates all earlier acceptance evidence, so re-run and re-record every required check at Step 9 without re-running completed reviewers.
|
|
17370
|
+
"Step 9 \u2014 If open blockers exist, fix them in one remediation round and record resolved IDs through `workflow review ... remediate`; remediation invalidates all earlier acceptance evidence, so re-run and re-record every required check at Step 9 without re-running completed reviewers. Build `summary.md`, commit, and PR copy from the accepted target, authoritative baseline, observed final state, and task-owned diff; preserve excluded scope, failed verification, blockers, migration or rollback, and audit facts. Set completed only when verification and required review domains are complete and blockers are zero.",
|
|
16844
17371
|
"",
|
|
16845
17372
|
"Stop and return the read-only diagnostic `NEEDS_REALIGNMENT` with reason `MANCODE_REFRAME_REQUIRED` when new evidence invalidates the confirmed goal, semantic owner, source of truth, or acceptance; platform entries would produce different semantics; status/contract/policy/transition meaning must change; an adapter is stale, the writer is incompatible, an operation is unfinished, an active child/open handoff/active solo assignment exists; or the change exceeds the current requirements/plan scope. Preserve metadata, requirements, plan, ledgers, claims, and handoffs. Do not call generic workflow update, write blocked/currentStep/planning, archive files, release claims, or claim to be back at Step 2.",
|
|
16846
17373
|
"",
|
|
@@ -16870,7 +17397,7 @@ var MODE_META = {
|
|
|
16870
17397
|
"`workflow update --status in_progress`; never auto-resume manual_test_required.",
|
|
16871
17398
|
"",
|
|
16872
17399
|
"Read `.mancode/memory/prd.md`, `spec.md`, and `decisions.md`; append durable",
|
|
16873
|
-
"decisions only after confirmed implementation.
|
|
17400
|
+
"decisions only after confirmed implementation. Build `handoff.md`, commit, and PR copy from the accepted target, authoritative baseline, observed final state, and task-owned diff; preserve conflicts, incomplete work, failed verification, migration or rollback, audit facts, and formal rejected or cancelled handoff states and reasons. Never",
|
|
16874
17401
|
"overwrite teammate changes or commit/merge/push without explicit approval."
|
|
16875
17402
|
].join("\n")
|
|
16876
17403
|
},
|
|
@@ -16977,6 +17504,8 @@ function renderPracticeRules() {
|
|
|
16977
17504
|
"",
|
|
16978
17505
|
"For every task, consider: why this change, what already exists, and what is the smallest useful diff?",
|
|
16979
17506
|
"",
|
|
17507
|
+
`- ${ACCEPTED_STATE_NARRATIVE_GUIDANCE}`,
|
|
17508
|
+
"",
|
|
16980
17509
|
"In solo mode, use the narrowest meaningful validation and one bounded self-check limited to the current diff. Do not start another reviewer or repeat the review.",
|
|
16981
17510
|
"Recommend man and explain the trigger when platform entry/flow differs, the semantic owner or source of truth is unclear, status/contract/policy semantics change, scope/architecture/cost/acceptance crosses files or modules, or historical compatibility, migration, cross-platform, or team evidence is required. Auth, payment, sensitive data, deletion, public APIs, untrusted input, concurrency, and infrastructure remain hard-risk signals. Advice alone never changes mode, step, policy, or authority.",
|
|
16982
17511
|
"While executing confirmed requirements/plan, new evidence that invalidates its goal, owner, source of truth, acceptance, or scope, or a stale adapter/incompatible writer/unfinished operation/active child/open handoff/active solo assignment, requires the read-only diagnostic `NEEDS_REALIGNMENT` with reason `MANCODE_REFRAME_REQUIRED`. Preserve metadata, requirements, plan, ledgers, claims, and handoffs; do not call generic workflow update, write blocked/currentStep/planning, archive files, release claims, or claim to be back at Step 2."
|
|
@@ -23357,14 +23886,14 @@ async function planLocalCacheRetention(root, localCacheDays, now) {
|
|
|
23357
23886
|
async function applyContextCompaction(plan) {
|
|
23358
23887
|
const deleted = [];
|
|
23359
23888
|
for (const candidate of plan.candidates) {
|
|
23360
|
-
await removeRegularFile(candidate.target);
|
|
23361
|
-
deleted.push(candidate.target);
|
|
23362
23889
|
for (const target of candidate.relatedTargets) {
|
|
23363
23890
|
if (await regularFileExists(target)) {
|
|
23364
23891
|
await removeRegularFile(target);
|
|
23365
23892
|
deleted.push(target);
|
|
23366
23893
|
}
|
|
23367
23894
|
}
|
|
23895
|
+
await removeRegularFile(candidate.target);
|
|
23896
|
+
deleted.push(candidate.target);
|
|
23368
23897
|
}
|
|
23369
23898
|
return { ...plan, deleted };
|
|
23370
23899
|
}
|
|
@@ -23446,6 +23975,7 @@ async function planOperationRetention(stores, now) {
|
|
|
23446
23975
|
for (const store of stores) {
|
|
23447
23976
|
const directory = operationDirectory(store);
|
|
23448
23977
|
const entries = await readDirectoryOrEmpty(directory);
|
|
23978
|
+
let recoveryEntries = null;
|
|
23449
23979
|
for (const entry of entries) {
|
|
23450
23980
|
if (!entry.endsWith(".json")) continue;
|
|
23451
23981
|
const target = path42.join(directory, entry);
|
|
@@ -23462,7 +23992,12 @@ async function planOperationRetention(stores, now) {
|
|
|
23462
23992
|
}
|
|
23463
23993
|
}
|
|
23464
23994
|
} else if (Date.parse(journal.updatedAt) < threshold) {
|
|
23465
|
-
|
|
23995
|
+
recoveryEntries ??= await readDirectoryOrEmpty(
|
|
23996
|
+
operationRecoveryDirectory(store)
|
|
23997
|
+
);
|
|
23998
|
+
candidates.push(
|
|
23999
|
+
operationRetentionCandidate(store, journal, target, recoveryEntries)
|
|
24000
|
+
);
|
|
23466
24001
|
}
|
|
23467
24002
|
}
|
|
23468
24003
|
}
|
|
@@ -23495,15 +24030,38 @@ async function planGitRefWorkflowRepairRetention(projectRoot, now) {
|
|
|
23495
24030
|
}
|
|
23496
24031
|
return { candidates, protectedSessionIds, protectedTaskRefs };
|
|
23497
24032
|
}
|
|
23498
|
-
function operationRetentionCandidate(store, journal, target) {
|
|
24033
|
+
function operationRetentionCandidate(store, journal, target, recoveryEntries) {
|
|
24034
|
+
const relatedTargets = journal.recoveryPayloadDigest === void 0 ? [] : operationRecoveryTargetsForRetention(store, journal, recoveryEntries);
|
|
23499
24035
|
return {
|
|
23500
24036
|
kind: "terminal_operation",
|
|
23501
24037
|
target,
|
|
23502
24038
|
reason: `${journal.state} operation exceeds ${TERMINAL_JOURNAL_RETENTION_DAYS} day retention`,
|
|
23503
24039
|
taskRef: null,
|
|
23504
|
-
relatedTargets
|
|
24040
|
+
relatedTargets
|
|
23505
24041
|
};
|
|
23506
24042
|
}
|
|
24043
|
+
function operationRecoveryTargetsForRetention(store, journal, recoveryEntries) {
|
|
24044
|
+
if (journal.recoveryPayloadDigest === void 0) return [];
|
|
24045
|
+
const directory = operationRecoveryDirectory(store);
|
|
24046
|
+
const versionPrefix = `${journal.operationId}.`;
|
|
24047
|
+
const discoveredVersions = recoveryEntries.filter((entry) => {
|
|
24048
|
+
if (!entry.startsWith(versionPrefix)) return false;
|
|
24049
|
+
return /^[a-f0-9]{64}\.json$/.test(entry.slice(versionPrefix.length));
|
|
24050
|
+
}).map((entry) => path42.join(directory, entry));
|
|
24051
|
+
return [
|
|
24052
|
+
.../* @__PURE__ */ new Set([
|
|
24053
|
+
operationRecoveryPayloadPath(store, journal.operationId),
|
|
24054
|
+
operationRecoveryPayloadVersionPath(
|
|
24055
|
+
store,
|
|
24056
|
+
journal.operationId,
|
|
24057
|
+
journal.recoveryPayloadDigest
|
|
24058
|
+
),
|
|
24059
|
+
...discoveredVersions
|
|
24060
|
+
])
|
|
24061
|
+
].sort(
|
|
24062
|
+
(left, right) => Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"))
|
|
24063
|
+
);
|
|
24064
|
+
}
|
|
23507
24065
|
function operationEntityKeys(journal) {
|
|
23508
24066
|
return [
|
|
23509
24067
|
...journal.entityLocks,
|
|
@@ -26812,6 +27370,7 @@ function errorCode3(error, fallback) {
|
|
|
26812
27370
|
}
|
|
26813
27371
|
|
|
26814
27372
|
// src/commands/init.ts
|
|
27373
|
+
import { randomUUID } from "crypto";
|
|
26815
27374
|
import { promises as fs4 } from "fs";
|
|
26816
27375
|
import os from "os";
|
|
26817
27376
|
import path55 from "path";
|
|
@@ -27532,6 +28091,54 @@ function createTerminalPrompter() {
|
|
|
27532
28091
|
} finally {
|
|
27533
28092
|
rl.close();
|
|
27534
28093
|
}
|
|
28094
|
+
},
|
|
28095
|
+
async resolveUnsafeAdapterPaths({ locale, paths, writeThroughAvailable }) {
|
|
28096
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
28097
|
+
try {
|
|
28098
|
+
console.log(
|
|
28099
|
+
locale === "zh-CN" ? "\n\u68C0\u6D4B\u5230\u9002\u914D\u5668\u76EE\u6807\u8DEF\u5F84\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A" : "\nAdapter target paths are symbolic links:"
|
|
28100
|
+
);
|
|
28101
|
+
for (const item of paths) {
|
|
28102
|
+
const detail = item.resolvedTo ? ` -> ${item.resolvedTo}` : "";
|
|
28103
|
+
console.log(` ${item.relative}${detail}`);
|
|
28104
|
+
}
|
|
28105
|
+
console.log(locale === "zh-CN" ? "1. \u9000\u51FA" : "1. Exit");
|
|
28106
|
+
console.log(
|
|
28107
|
+
locale === "zh-CN" ? "2. \u5C06\u7B26\u53F7\u94FE\u63A5\u66FF\u6362\u4E3A\u666E\u901A\u6587\u4EF6\uFF08\u4FDD\u7559\u539F\u5185\u5BB9\uFF09\u5E76\u7EE7\u7EED\u521D\u59CB\u5316" : "2. Replace the symbolic link(s) with regular file(s) (content preserved) and continue"
|
|
28108
|
+
);
|
|
28109
|
+
if (writeThroughAvailable) {
|
|
28110
|
+
console.log(
|
|
28111
|
+
locale === "zh-CN" ? "3. \u4FDD\u7559\u94FE\u63A5\uFF0Cmancode \u76F4\u63A5\u8BFB\u5199\u5176\u89E3\u6790\u76EE\u6807\uFF08CLAUDE.md -> AGENTS.md \u7EA6\u5B9A\uFF09" : "3. Keep the link(s); mancode reads and writes the resolved file (CLAUDE.md -> AGENTS.md convention)"
|
|
28112
|
+
);
|
|
28113
|
+
}
|
|
28114
|
+
const answer = (await rl.question(
|
|
28115
|
+
locale === "zh-CN" ? `\u9009\u62E9 [1/${writeThroughAvailable ? "2/3" : "2"}]: ` : `Choose [1/${writeThroughAvailable ? "2/3" : "2"}]: `
|
|
28116
|
+
)).trim().toLowerCase();
|
|
28117
|
+
if (answer === "2") return "replace";
|
|
28118
|
+
if (answer === "3" && writeThroughAvailable) return "write-through";
|
|
28119
|
+
return "exit";
|
|
28120
|
+
} finally {
|
|
28121
|
+
rl.close();
|
|
28122
|
+
}
|
|
28123
|
+
},
|
|
28124
|
+
async resolveScratchMancodeTarget({ locale, entries }) {
|
|
28125
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
28126
|
+
try {
|
|
28127
|
+
console.log(
|
|
28128
|
+
locale === "zh-CN" ? "\n`.mancode` \u5DF2\u5B58\u5728\uFF0C\u4F46\u53EA\u5305\u542B\u975E Continuity \u7684\u672C\u5730\u8349\u7A3F\uFF08\u53D1\u5E03\u5DE5\u4EF6\u3001\u5176\u4ED6\u5DE5\u5177\u5907\u4EFD\uFF09\uFF1A" : "\n`.mancode` exists but contains only non-Continuity local scratch (release artifacts, other-tool backups):"
|
|
28129
|
+
);
|
|
28130
|
+
for (const entry of entries) console.log(` .mancode/${entry}`);
|
|
28131
|
+
console.log(locale === "zh-CN" ? "1. \u9000\u51FA" : "1. Exit");
|
|
28132
|
+
console.log(
|
|
28133
|
+
locale === "zh-CN" ? "2. \u628A `.mancode` \u79FB\u5230\u4E00\u8FB9\uFF08\u5185\u5BB9\u4FDD\u7559\uFF09\u5E76\u7EE7\u7EED\u521D\u59CB\u5316\uFF1B\u6210\u529F\u540E\u539F\u6837\u5F52\u4F4D\u5230 `.mancode/local/`" : "2. Move `.mancode` aside (content preserved) and continue; it is restored under `.mancode/local/` after success"
|
|
28134
|
+
);
|
|
28135
|
+
const answer = (await rl.question(
|
|
28136
|
+
locale === "zh-CN" ? "\u9009\u62E9 [1/2]: " : "Choose [1/2]: "
|
|
28137
|
+
)).trim().toLowerCase();
|
|
28138
|
+
return answer === "2" ? "relocate" : "exit";
|
|
28139
|
+
} finally {
|
|
28140
|
+
rl.close();
|
|
28141
|
+
}
|
|
27535
28142
|
}
|
|
27536
28143
|
};
|
|
27537
28144
|
}
|
|
@@ -28449,12 +29056,12 @@ function parseAdapterPlans(value) {
|
|
|
28449
29056
|
assertRecord(candidate, "greenfield initialization adapter plan");
|
|
28450
29057
|
assertKnownKeys(
|
|
28451
29058
|
candidate,
|
|
28452
|
-
["target", "beforeContent", "targetContent"],
|
|
29059
|
+
["target", "beforeContent", "targetContent", "resolvedTarget"],
|
|
28453
29060
|
"greenfield initialization adapter plan"
|
|
28454
29061
|
);
|
|
28455
29062
|
if (typeof candidate.target !== "string" || !V3_ADAPTER_FILE_TARGETS.includes(
|
|
28456
29063
|
candidate.target
|
|
28457
|
-
) || seen.has(candidate.target) || candidate.beforeContent !== null && typeof candidate.beforeContent !== "string" || typeof candidate.targetContent !== "string" || !candidate.targetContent.trim()) {
|
|
29064
|
+
) || seen.has(candidate.target) || candidate.beforeContent !== null && typeof candidate.beforeContent !== "string" || typeof candidate.targetContent !== "string" || !candidate.targetContent.trim() || candidate.resolvedTarget !== void 0 && (typeof candidate.resolvedTarget !== "string" || !candidate.resolvedTarget.trim() || path54.isAbsolute(candidate.resolvedTarget) || candidate.resolvedTarget.split(/[\\/]/).includes(".."))) {
|
|
28458
29065
|
throw new Error(
|
|
28459
29066
|
"greenfield initialization journal adapterPlans is invalid"
|
|
28460
29067
|
);
|
|
@@ -28464,7 +29071,8 @@ function parseAdapterPlans(value) {
|
|
|
28464
29071
|
return {
|
|
28465
29072
|
target,
|
|
28466
29073
|
beforeContent: candidate.beforeContent,
|
|
28467
|
-
targetContent: candidate.targetContent
|
|
29074
|
+
targetContent: candidate.targetContent,
|
|
29075
|
+
resolvedTarget: candidate.resolvedTarget
|
|
28468
29076
|
};
|
|
28469
29077
|
});
|
|
28470
29078
|
}
|
|
@@ -29138,7 +29746,16 @@ async function initializeV3(rootDir, options) {
|
|
|
29138
29746
|
console.error(" Use one or more supported platform names, or all.");
|
|
29139
29747
|
return EXIT_INIT_FAILED;
|
|
29140
29748
|
}
|
|
29749
|
+
let scratchBackup = null;
|
|
29141
29750
|
try {
|
|
29751
|
+
if (!existingV3) {
|
|
29752
|
+
const unsafeExit = await resolveUnsafeInitAdapterPaths(
|
|
29753
|
+
rootDir,
|
|
29754
|
+
options,
|
|
29755
|
+
selectedPlatforms
|
|
29756
|
+
);
|
|
29757
|
+
if (unsafeExit !== null) return unsafeExit;
|
|
29758
|
+
}
|
|
29142
29759
|
if (existingV3 && selectedPlatforms.some((platform) => !registeredAdapters.has(platform))) {
|
|
29143
29760
|
throw new Error(
|
|
29144
29761
|
"MANCODE_ADAPTER_REGISTRATION_REQUIRED: run `mancode adapter upgrade --platform <platform> --dry-run`, then confirm that operation with an active session."
|
|
@@ -29161,6 +29778,9 @@ async function initializeV3(rootDir, options) {
|
|
|
29161
29778
|
console.log("\u2139\uFE0F mancode is already initialized.");
|
|
29162
29779
|
return EXIT_ALREADY_INITIALIZED;
|
|
29163
29780
|
}
|
|
29781
|
+
const scratch = await resolveScratchMancodeTarget(rootDir, options);
|
|
29782
|
+
if (scratch.exit !== null) return scratch.exit;
|
|
29783
|
+
scratchBackup = scratch.backupPath;
|
|
29164
29784
|
const result2 = await initializeV3Project({
|
|
29165
29785
|
projectRoot: rootDir,
|
|
29166
29786
|
managedAdapters: Object.fromEntries(
|
|
@@ -29171,6 +29791,9 @@ async function initializeV3(rootDir, options) {
|
|
|
29171
29791
|
console.log("\u2713 Initialized mancode project.");
|
|
29172
29792
|
console.log(` workspace: ${result2.runtime.workspaceId}`);
|
|
29173
29793
|
console.log(` operation: ${result2.journal.operationId}`);
|
|
29794
|
+
if (scratchBackup !== null) {
|
|
29795
|
+
await restoreScratchBackup(rootDir, scratchBackup);
|
|
29796
|
+
}
|
|
29174
29797
|
if (options.team !== void 0) {
|
|
29175
29798
|
console.log(
|
|
29176
29799
|
` team policy: ${options.team ? "on (--team)" : "off (--no-team)"}`
|
|
@@ -29185,16 +29808,157 @@ async function initializeV3(rootDir, options) {
|
|
|
29185
29808
|
}
|
|
29186
29809
|
return EXIT_OK2;
|
|
29187
29810
|
} catch (error) {
|
|
29811
|
+
if (scratchBackup !== null) {
|
|
29812
|
+
console.error(
|
|
29813
|
+
` Previous .mancode scratch was preserved at ${path55.relative(rootDir, scratchBackup)}.`
|
|
29814
|
+
);
|
|
29815
|
+
}
|
|
29188
29816
|
printV3InitError(error);
|
|
29189
29817
|
return EXIT_INIT_FAILED;
|
|
29190
29818
|
}
|
|
29191
29819
|
}
|
|
29820
|
+
async function resolveUnsafeInitAdapterPaths(rootDir, options, selectedPlatforms) {
|
|
29821
|
+
const prompter = options.prompter ?? (options.interactive ? createTerminalPrompter() : null);
|
|
29822
|
+
if (!prompter) return null;
|
|
29823
|
+
const locale = detectInitLocale(options.lang) ?? "en";
|
|
29824
|
+
const found = [];
|
|
29825
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29826
|
+
for (const platform of selectedPlatforms) {
|
|
29827
|
+
for (const entry of await inspectUnsafeV3AdapterPaths(rootDir, platform)) {
|
|
29828
|
+
if (seen.has(entry.target)) continue;
|
|
29829
|
+
seen.add(entry.target);
|
|
29830
|
+
found.push(entry);
|
|
29831
|
+
}
|
|
29832
|
+
}
|
|
29833
|
+
const fixable = found.filter(
|
|
29834
|
+
(entry) => entry.kind === "symlink" && entry.finalTarget
|
|
29835
|
+
);
|
|
29836
|
+
if (fixable.length === 0) return null;
|
|
29837
|
+
const writeThroughAvailable = found.length > 0 && (await Promise.all(
|
|
29838
|
+
found.map(
|
|
29839
|
+
(entry) => writeThroughResolvedPath(path55.resolve(rootDir), entry.target)
|
|
29840
|
+
)
|
|
29841
|
+
)).every((resolved) => resolved !== null);
|
|
29842
|
+
const choice = await prompter.resolveUnsafeAdapterPaths({
|
|
29843
|
+
locale,
|
|
29844
|
+
paths: found.map(({ relative, resolvedTo }) => ({ relative, resolvedTo })),
|
|
29845
|
+
writeThroughAvailable
|
|
29846
|
+
});
|
|
29847
|
+
if (choice === "exit") {
|
|
29848
|
+
console.log(
|
|
29849
|
+
locale === "zh-CN" ? "\u5DF2\u53D6\u6D88\u521D\u59CB\u5316\u3002" : "Initialization cancelled."
|
|
29850
|
+
);
|
|
29851
|
+
return EXIT_USER_CANCEL;
|
|
29852
|
+
}
|
|
29853
|
+
if (choice === "write-through") {
|
|
29854
|
+
console.log(
|
|
29855
|
+
locale === "zh-CN" ? "\u2139\uFE0F \u4FDD\u7559\u7B26\u53F7\u94FE\u63A5\uFF0Cmancode \u5C06\u8BFB\u5199\u5176\u89E3\u6790\u76EE\u6807\u3002" : "\u2139\uFE0F Keeping the symbolic link(s); mancode reads and writes the resolved target."
|
|
29856
|
+
);
|
|
29857
|
+
return null;
|
|
29858
|
+
}
|
|
29859
|
+
await replaceUnsafeV3AdapterSymlinks(fixable);
|
|
29860
|
+
return null;
|
|
29861
|
+
}
|
|
29862
|
+
var SCRATCH_BACKUP_PREFIX = ".mancode.preinit-scratch-";
|
|
29863
|
+
async function resolveScratchMancodeTarget(rootDir, options) {
|
|
29864
|
+
const inspection = await inspectMancodeLayout(rootDir);
|
|
29865
|
+
if (!inspection.v3ScratchOnly) {
|
|
29866
|
+
return { exit: null, backupPath: null };
|
|
29867
|
+
}
|
|
29868
|
+
const mancodeRoot = path55.join(rootDir, ".mancode");
|
|
29869
|
+
if (inspection.v3TargetEntries.length === 0) {
|
|
29870
|
+
await fs4.rmdir(mancodeRoot);
|
|
29871
|
+
console.log(
|
|
29872
|
+
"\u2139\uFE0F Removed an empty .mancode directory before initialization."
|
|
29873
|
+
);
|
|
29874
|
+
return { exit: null, backupPath: null };
|
|
29875
|
+
}
|
|
29876
|
+
const prompter = options.prompter ?? (options.interactive ? createTerminalPrompter() : null);
|
|
29877
|
+
if (!prompter) {
|
|
29878
|
+
throw new Error("MANCODE_V3_SCRATCH_TARGET_REQUIRES_CHOICE");
|
|
29879
|
+
}
|
|
29880
|
+
const locale = detectInitLocale(options.lang) ?? "en";
|
|
29881
|
+
const choice = prompter.resolveScratchMancodeTarget ? await prompter.resolveScratchMancodeTarget({
|
|
29882
|
+
locale,
|
|
29883
|
+
entries: inspection.v3TargetEntries
|
|
29884
|
+
}) : "exit";
|
|
29885
|
+
if (choice !== "relocate") {
|
|
29886
|
+
console.log(
|
|
29887
|
+
locale === "zh-CN" ? "\u5DF2\u53D6\u6D88\u521D\u59CB\u5316\u3002" : "Initialization cancelled."
|
|
29888
|
+
);
|
|
29889
|
+
return { exit: EXIT_USER_CANCEL, backupPath: null };
|
|
29890
|
+
}
|
|
29891
|
+
const backupPath = path55.join(
|
|
29892
|
+
rootDir,
|
|
29893
|
+
`${SCRATCH_BACKUP_PREFIX}${randomUUID()}`
|
|
29894
|
+
);
|
|
29895
|
+
await fs4.rename(mancodeRoot, backupPath);
|
|
29896
|
+
console.log(
|
|
29897
|
+
`\u2139\uFE0F Moved non-Continuity .mancode scratch aside: ${path55.relative(rootDir, backupPath)}`
|
|
29898
|
+
);
|
|
29899
|
+
return { exit: null, backupPath };
|
|
29900
|
+
}
|
|
29901
|
+
async function restoreScratchBackup(rootDir, backupPath) {
|
|
29902
|
+
const localTarget = path55.join(rootDir, ".mancode", "local");
|
|
29903
|
+
const leftover = path55.relative(rootDir, backupPath);
|
|
29904
|
+
try {
|
|
29905
|
+
await fs4.mkdir(localTarget, { recursive: true });
|
|
29906
|
+
const entries = await fs4.readdir(backupPath, { withFileTypes: true });
|
|
29907
|
+
for (const entry of entries) {
|
|
29908
|
+
const source = path55.join(backupPath, entry.name);
|
|
29909
|
+
if (entry.isDirectory() && entry.name === "local") {
|
|
29910
|
+
for (const child of await fs4.readdir(source)) {
|
|
29911
|
+
const destination = path55.join(localTarget, child);
|
|
29912
|
+
if (await pathExists6(destination)) continue;
|
|
29913
|
+
await fs4.rename(path55.join(source, child), destination);
|
|
29914
|
+
}
|
|
29915
|
+
} else {
|
|
29916
|
+
const preinitScratch = path55.join(localTarget, "preinit-scratch");
|
|
29917
|
+
await fs4.mkdir(preinitScratch, { recursive: true });
|
|
29918
|
+
const destination = path55.join(preinitScratch, entry.name);
|
|
29919
|
+
if (await pathExists6(destination)) continue;
|
|
29920
|
+
await fs4.rename(source, destination);
|
|
29921
|
+
}
|
|
29922
|
+
}
|
|
29923
|
+
await fs4.rmdir(path55.join(backupPath, "local")).catch(() => void 0);
|
|
29924
|
+
await fs4.rmdir(backupPath).catch(() => void 0);
|
|
29925
|
+
if (await pathExists6(backupPath)) {
|
|
29926
|
+
console.warn(
|
|
29927
|
+
`\u26A0\uFE0F Could not fully restore previous scratch; it remains at ${leftover}.`
|
|
29928
|
+
);
|
|
29929
|
+
} else {
|
|
29930
|
+
console.log(
|
|
29931
|
+
"\u2139\uFE0F Restored previous .mancode scratch into .mancode/local/."
|
|
29932
|
+
);
|
|
29933
|
+
}
|
|
29934
|
+
} catch {
|
|
29935
|
+
console.warn(
|
|
29936
|
+
`\u26A0\uFE0F Failed to restore previous .mancode scratch; it remains at ${leftover}.`
|
|
29937
|
+
);
|
|
29938
|
+
}
|
|
29939
|
+
}
|
|
29192
29940
|
function printV3InitError(error) {
|
|
29193
29941
|
const message = error instanceof Error ? error.message : "mancode initialization failed";
|
|
29194
29942
|
console.error(`\u2717 ${message}`);
|
|
29195
29943
|
if (message === "MANCODE_LEGACY_AUTHORITY_PRESENT") {
|
|
29196
29944
|
console.error(" Run `mancode migrate context --dry-run` instead.");
|
|
29197
29945
|
}
|
|
29946
|
+
if (message === "MANCODE_V3_TARGET_EXISTS") {
|
|
29947
|
+
console.error(
|
|
29948
|
+
" `.mancode` already exists with Continuity authority content; refusing to overwrite it."
|
|
29949
|
+
);
|
|
29950
|
+
console.error(
|
|
29951
|
+
" For an initialized project, use `mancode adapter status` / `mancode adapter upgrade`; otherwise inspect `.mancode` first."
|
|
29952
|
+
);
|
|
29953
|
+
}
|
|
29954
|
+
if (message === "MANCODE_V3_SCRATCH_TARGET_REQUIRES_CHOICE") {
|
|
29955
|
+
console.error(
|
|
29956
|
+
" `.mancode` holds only non-Continuity scratch (release artifacts, other-tool backups)."
|
|
29957
|
+
);
|
|
29958
|
+
console.error(
|
|
29959
|
+
" Run init interactively to move it aside, or remove/rename the directory yourself."
|
|
29960
|
+
);
|
|
29961
|
+
}
|
|
29198
29962
|
}
|
|
29199
29963
|
async function updateConfigOptions(mancodeDir, patch, preserveInstalledPlatforms) {
|
|
29200
29964
|
const configPath = path55.join(mancodeDir, "config.json");
|
|
@@ -29318,15 +30082,17 @@ async function validateV3CliProjectBoundary(rootDir, options, locale, legacyInit
|
|
|
29318
30082
|
const isGitRepo = await pathExists6(path55.join(rootDir, ".git"));
|
|
29319
30083
|
const hasEvidence = await hasProjectEvidence(rootDir);
|
|
29320
30084
|
if (isGitRepo || hasEvidence) return null;
|
|
29321
|
-
const
|
|
29322
|
-
|
|
29323
|
-
|
|
29324
|
-
const
|
|
29325
|
-
if (!genericSafety.ok && !((legacyAuthorityPresent || v3Initialized) && genericSafety.reason === "nonempty")) {
|
|
30085
|
+
const inspection = await inspectMancodeLayout(rootDir);
|
|
30086
|
+
const v3Initialized = inspection.v3AuthorityPathsPresent.includes("schema.json");
|
|
30087
|
+
const legacyAuthorityPresent = legacyInitialized || inspection.legacy.authorityPresent;
|
|
30088
|
+
const scratchOnlyMancode = inspection.v3ScratchOnly;
|
|
30089
|
+
if (!genericSafety.ok && !((legacyAuthorityPresent || v3Initialized || scratchOnlyMancode) && genericSafety.reason === "nonempty")) {
|
|
29326
30090
|
printNotProjectDirectory(rootDir, locale, genericSafety.reason);
|
|
29327
30091
|
return EXIT_NOT_A_PROJECT_DIR;
|
|
29328
30092
|
}
|
|
29329
|
-
if (legacyAuthorityPresent || v3Initialized)
|
|
30093
|
+
if (legacyAuthorityPresent || v3Initialized || scratchOnlyMancode) {
|
|
30094
|
+
return null;
|
|
30095
|
+
}
|
|
29330
30096
|
const prompter = options.prompter ?? (options.interactive ? createTerminalPrompter() : null);
|
|
29331
30097
|
const confirmed = options.empty || options.yes ? true : prompter ? await prompter.confirmGenericProject({ rootDir, locale }) : false;
|
|
29332
30098
|
if (!confirmed) {
|
|
@@ -31243,8 +32009,19 @@ async function runOperationMutation(rootDir, operationId, options, mode) {
|
|
|
31243
32009
|
EXIT_V3_INVALID_ARGUMENT
|
|
31244
32010
|
);
|
|
31245
32011
|
}
|
|
32012
|
+
if (options.replacementCheckpointId !== void 0 && !isUlid(options.replacementCheckpointId)) {
|
|
32013
|
+
return printV3Error(
|
|
32014
|
+
options.json,
|
|
32015
|
+
"MANCODE_REPLACEMENT_CHECKPOINT_ID_INVALID",
|
|
32016
|
+
"replacement checkpoint ID must be a canonical ULID.",
|
|
32017
|
+
EXIT_V3_INVALID_ARGUMENT
|
|
32018
|
+
);
|
|
32019
|
+
}
|
|
31246
32020
|
try {
|
|
31247
32021
|
assertUlid(operationId, "operationId");
|
|
32022
|
+
if (options.replacementCheckpointId !== void 0) {
|
|
32023
|
+
assertUlid(options.replacementCheckpointId, "replacementCheckpointId");
|
|
32024
|
+
}
|
|
31248
32025
|
const project = await readV3CommandProject(rootDir);
|
|
31249
32026
|
const session = await resolveV3CommandSession(project, options);
|
|
31250
32027
|
const result2 = await executeOperationRecovery({
|
|
@@ -31252,7 +32029,8 @@ async function runOperationMutation(rootDir, operationId, options, mode) {
|
|
|
31252
32029
|
operationId,
|
|
31253
32030
|
actorId: session.actorId,
|
|
31254
32031
|
sessionId: session.sessionId,
|
|
31255
|
-
mode
|
|
32032
|
+
mode,
|
|
32033
|
+
replacementCheckpointId: options.replacementCheckpointId
|
|
31256
32034
|
});
|
|
31257
32035
|
return printV3Result(options.json, { schemaVersion: 1, ...result2 });
|
|
31258
32036
|
} catch (error) {
|
|
@@ -31804,7 +32582,7 @@ async function projectUpgrade(rootDir, options) {
|
|
|
31804
32582
|
}
|
|
31805
32583
|
|
|
31806
32584
|
// src/commands/refresh-project.ts
|
|
31807
|
-
import { randomUUID } from "crypto";
|
|
32585
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
31808
32586
|
import { promises as fs7 } from "fs";
|
|
31809
32587
|
import path62 from "path";
|
|
31810
32588
|
import process9 from "process";
|
|
@@ -31980,7 +32758,7 @@ async function readOptionalText(filePath) {
|
|
|
31980
32758
|
function temporaryPath(filePath) {
|
|
31981
32759
|
return path62.join(
|
|
31982
32760
|
path62.dirname(filePath),
|
|
31983
|
-
`.${path62.basename(filePath)}.${process9.pid}.${
|
|
32761
|
+
`.${path62.basename(filePath)}.${process9.pid}.${randomUUID2()}.tmp`
|
|
31984
32762
|
);
|
|
31985
32763
|
}
|
|
31986
32764
|
async function refreshStaticPlatforms(rootDir, config, fallbackPlatform, stack, uiLibrary, profile) {
|
|
@@ -41784,7 +42562,8 @@ var WORKFLOW_SUBCOMMANDS = [
|
|
|
41784
42562
|
"checkpoint",
|
|
41785
42563
|
"child",
|
|
41786
42564
|
"promote",
|
|
41787
|
-
"handoff"
|
|
42565
|
+
"handoff",
|
|
42566
|
+
"delivery"
|
|
41788
42567
|
];
|
|
41789
42568
|
var WORKFLOW_SUBCOMMAND_SET = new Set(
|
|
41790
42569
|
WORKFLOW_SUBCOMMANDS
|
|
@@ -41792,8 +42571,8 @@ var WORKFLOW_SUBCOMMAND_SET = new Set(
|
|
|
41792
42571
|
var CONTINUITY_COMPATIBILITY_SUBCOMMANDS = /* @__PURE__ */ new Set(["clean"]);
|
|
41793
42572
|
|
|
41794
42573
|
// src/commands/workflow.ts
|
|
41795
|
-
import { access as access6, readFile as
|
|
41796
|
-
import
|
|
42574
|
+
import { access as access6, readFile as readFile51, rm as rm26, writeFile as writeFile53 } from "fs/promises";
|
|
42575
|
+
import path74 from "path";
|
|
41797
42576
|
|
|
41798
42577
|
// src/context/child-result-merge.ts
|
|
41799
42578
|
async function mergeV3ChildResult(input) {
|
|
@@ -42121,8 +42900,746 @@ function assertPositiveRevision6(value, label) {
|
|
|
42121
42900
|
}
|
|
42122
42901
|
}
|
|
42123
42902
|
|
|
42903
|
+
// src/context/man-delivery-runtime.ts
|
|
42904
|
+
import { execFile as execFileCallback7 } from "child_process";
|
|
42905
|
+
import { createHash as createHash11, randomUUID as randomUUID4 } from "crypto";
|
|
42906
|
+
import {
|
|
42907
|
+
lstat as lstat28,
|
|
42908
|
+
readFile as readFile48,
|
|
42909
|
+
readlink as readlink2,
|
|
42910
|
+
realpath as realpath4,
|
|
42911
|
+
rm as rm24,
|
|
42912
|
+
writeFile as writeFile51
|
|
42913
|
+
} from "fs/promises";
|
|
42914
|
+
import path71 from "path";
|
|
42915
|
+
import { promisify as promisify9 } from "util";
|
|
42916
|
+
|
|
42917
|
+
// src/context/man-delivery-plan.ts
|
|
42918
|
+
import path69 from "path";
|
|
42919
|
+
var SOURCE_PREFIX = "<!-- mancode:delivery-plan ";
|
|
42920
|
+
var MARKERS = [
|
|
42921
|
+
"<!-- mancode:plan-baseline:start -->",
|
|
42922
|
+
"<!-- mancode:plan-baseline:end -->",
|
|
42923
|
+
"<!-- mancode:delivery-record:start -->",
|
|
42924
|
+
"<!-- mancode:delivery-record:end -->"
|
|
42925
|
+
];
|
|
42926
|
+
function parseManPlanDocument(document) {
|
|
42927
|
+
const markers = unfencedLines(document).filter(
|
|
42928
|
+
(line) => MARKERS.includes(line.marker)
|
|
42929
|
+
);
|
|
42930
|
+
if (markers.length !== 4 || markers.some((value, index) => value.marker !== MARKERS[index])) {
|
|
42931
|
+
throw new Error("MANCODE_MAN_PLAN_MARKERS_INVALID");
|
|
42932
|
+
}
|
|
42933
|
+
const [baselineStart, baselineEnd, recordStart, recordEnd] = markers;
|
|
42934
|
+
if (!baselineStart || !baselineEnd || !recordStart || !recordEnd) {
|
|
42935
|
+
throw new Error("MANCODE_MAN_PLAN_MARKERS_INVALID");
|
|
42936
|
+
}
|
|
42937
|
+
const baseline = document.slice(baselineStart.end, baselineEnd.start).trim();
|
|
42938
|
+
if (!baseline) throw new Error("MANCODE_MAN_PLAN_BASELINE_REQUIRED");
|
|
42939
|
+
return {
|
|
42940
|
+
baseline,
|
|
42941
|
+
record: document.slice(recordStart.end, recordEnd.start).trim(),
|
|
42942
|
+
recordStart: recordStart.end,
|
|
42943
|
+
recordEnd: recordEnd.start
|
|
42944
|
+
};
|
|
42945
|
+
}
|
|
42946
|
+
function manProgressTaskId(baseline) {
|
|
42947
|
+
const bindings = unfencedLines(baseline).map(
|
|
42948
|
+
(line) => /^<!-- mancode:progress-task ([a-zA-Z0-9:_-]+) -->$/.exec(
|
|
42949
|
+
line.marker
|
|
42950
|
+
)?.[1]
|
|
42951
|
+
).filter((id) => id !== void 0);
|
|
42952
|
+
if (bindings.length > 1)
|
|
42953
|
+
throw new Error("ambiguous plan progress task binding");
|
|
42954
|
+
return bindings[0] ?? null;
|
|
42955
|
+
}
|
|
42956
|
+
function unfencedLines(document) {
|
|
42957
|
+
let offset = 0;
|
|
42958
|
+
let fence = null;
|
|
42959
|
+
const markers = [];
|
|
42960
|
+
for (const line of document.match(/[^\n]*(?:\n|$)/g) ?? []) {
|
|
42961
|
+
const text = line.replace(/\r?\n$/, "");
|
|
42962
|
+
const delimiter = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(text);
|
|
42963
|
+
if (fence !== null) {
|
|
42964
|
+
if (delimiter && delimiter[1]?.[0] === fence.character && delimiter[1].length >= fence.length && delimiter[2]?.trim() === "")
|
|
42965
|
+
fence = null;
|
|
42966
|
+
} else if (delimiter?.[1]) {
|
|
42967
|
+
fence = {
|
|
42968
|
+
character: delimiter[1][0],
|
|
42969
|
+
length: delimiter[1].length
|
|
42970
|
+
};
|
|
42971
|
+
} else {
|
|
42972
|
+
markers.push({ marker: text, start: offset, end: offset + line.length });
|
|
42973
|
+
}
|
|
42974
|
+
offset += line.length;
|
|
42975
|
+
}
|
|
42976
|
+
return markers;
|
|
42977
|
+
}
|
|
42978
|
+
function replaceManDeliveryRecord(document, record) {
|
|
42979
|
+
const parsed = parseManPlanDocument(document);
|
|
42980
|
+
const next = `${document.slice(0, parsed.recordStart)}${record.trim()}
|
|
42981
|
+
${document.slice(parsed.recordEnd)}`;
|
|
42982
|
+
const checked = parseManPlanDocument(next);
|
|
42983
|
+
if (checked.baseline !== parsed.baseline)
|
|
42984
|
+
throw new Error("MANCODE_MAN_PLAN_BASELINE_CHANGED");
|
|
42985
|
+
return next;
|
|
42986
|
+
}
|
|
42987
|
+
function assertManPlanPath(file) {
|
|
42988
|
+
if (!file || path69.posix.isAbsolute(file) || /[\\\r\n\0<>:]/.test(file) || file.split("/").some((part) => !part || part === "." || part === "..") || /^(?:\.git|\.mancode|架构|项目接口)(?:\/|$)/.test(file) || !file.endsWith(".md") || !file.includes("/"))
|
|
42989
|
+
throw new Error("MANCODE_MAN_PLAN_PATH_INVALID");
|
|
42990
|
+
}
|
|
42991
|
+
function parseSource(value) {
|
|
42992
|
+
assertRecord(value, "man plan source");
|
|
42993
|
+
assertKnownKeys(value, ["version", "path", "baseHead"], "man plan source");
|
|
42994
|
+
if (value.version !== 1 || typeof value.path !== "string") {
|
|
42995
|
+
throw new Error("MANCODE_MAN_PLAN_SOURCE_INVALID");
|
|
42996
|
+
}
|
|
42997
|
+
assertManPlanPath(value.path);
|
|
42998
|
+
if (value.baseHead !== null && (typeof value.baseHead !== "string" || !/^[a-f0-9]{40,64}$/.test(value.baseHead))) {
|
|
42999
|
+
throw new Error("MANCODE_MAN_PLAN_BASE_INVALID");
|
|
43000
|
+
}
|
|
43001
|
+
return { version: 1, path: value.path, baseHead: value.baseHead };
|
|
43002
|
+
}
|
|
43003
|
+
function compileManDeliveryPlan(source, document) {
|
|
43004
|
+
return `${SOURCE_PREFIX}${JSON.stringify(parseSource(source))} -->
|
|
43005
|
+
${parseManPlanDocument(document).baseline}`;
|
|
43006
|
+
}
|
|
43007
|
+
function parseManDeliveryPlan(plan) {
|
|
43008
|
+
if (!plan.startsWith(SOURCE_PREFIX)) return null;
|
|
43009
|
+
const end = plan.indexOf(" -->\n");
|
|
43010
|
+
if (end < 0) throw new Error("MANCODE_MAN_PLAN_SOURCE_INVALID");
|
|
43011
|
+
const source = parseSource(JSON.parse(plan.slice(SOURCE_PREFIX.length, end)));
|
|
43012
|
+
const baseline = plan.slice(end + " -->\n".length);
|
|
43013
|
+
if (!baseline.trim()) throw new Error("MANCODE_MAN_PLAN_BASELINE_REQUIRED");
|
|
43014
|
+
return { source, baseline };
|
|
43015
|
+
}
|
|
43016
|
+
|
|
43017
|
+
// src/context/man-progress.ts
|
|
43018
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
43019
|
+
import { lstat as lstat27, readFile as readFile47, realpath as realpath3, rm as rm23, writeFile as writeFile50 } from "fs/promises";
|
|
43020
|
+
import path70 from "path";
|
|
43021
|
+
var START = '<script type="application/json" id="mancode-progress-data">';
|
|
43022
|
+
var END = "</script>";
|
|
43023
|
+
function updateManProgressHtml(html, taskId, status2, reason) {
|
|
43024
|
+
if (html.split(START).length !== 2 || [...html.matchAll(/\bid\s*=\s*["']mancode-progress-data["']/gi)].length !== 1)
|
|
43025
|
+
throw new Error("progress data block is missing or ambiguous");
|
|
43026
|
+
const tokens = [
|
|
43027
|
+
...html.matchAll(
|
|
43028
|
+
/<!--[\s\S]*?-->|<(script|style|textarea|title|xmp|iframe|noembed|noframes|noscript)\b(?:[^"'<>]|"[^"]*"|'[^']*')*>[\s\S]*?<\/\1\s*>|<(?:[^"'<>]|"[^"]*"|'[^']*')*>/gi
|
|
43029
|
+
)
|
|
43030
|
+
];
|
|
43031
|
+
if (!tokens.some(
|
|
43032
|
+
(token) => token.index === html.indexOf(START) && token[0].startsWith(START)
|
|
43033
|
+
))
|
|
43034
|
+
throw new Error("progress data must be an active standalone script block");
|
|
43035
|
+
const start = html.indexOf(START) + START.length;
|
|
43036
|
+
const end = html.indexOf(END, start);
|
|
43037
|
+
if (end < start) throw new Error("progress data block is unterminated");
|
|
43038
|
+
const data = JSON.parse(html.slice(start, end));
|
|
43039
|
+
if (!data || typeof data !== "object" || !("schemaVersion" in data) || data.schemaVersion !== 1 || !("tasks" in data) || !Array.isArray(data.tasks))
|
|
43040
|
+
throw new Error("unsupported progress data");
|
|
43041
|
+
const matches = data.tasks.filter(
|
|
43042
|
+
(item) => item && typeof item === "object" && item.taskId === taskId
|
|
43043
|
+
);
|
|
43044
|
+
if (matches.length !== 1)
|
|
43045
|
+
throw new Error("progress taskId is missing or ambiguous");
|
|
43046
|
+
Object.assign(matches[0], { status: status2, reason });
|
|
43047
|
+
return `${html.slice(0, start)}
|
|
43048
|
+
${JSON.stringify(data).replaceAll("<", "\\u003c")}
|
|
43049
|
+
${html.slice(end)}`;
|
|
43050
|
+
}
|
|
43051
|
+
async function syncManProgressPage(root, taskId, status2, reason, allowed) {
|
|
43052
|
+
const target = path70.join(root, "\u9879\u76EE\u8FDB\u5EA6.html");
|
|
43053
|
+
let temporary;
|
|
43054
|
+
try {
|
|
43055
|
+
const stat5 = await lstat27(target);
|
|
43056
|
+
if (!allowed)
|
|
43057
|
+
return {
|
|
43058
|
+
status: "manual_sync",
|
|
43059
|
+
reason: "progress page is outside the approved write scope"
|
|
43060
|
+
};
|
|
43061
|
+
if (!stat5.isFile() || await realpath3(target) !== path70.join(await realpath3(root), "\u9879\u76EE\u8FDB\u5EA6.html"))
|
|
43062
|
+
return {
|
|
43063
|
+
status: "manual_sync",
|
|
43064
|
+
reason: "progress page must be a regular project file"
|
|
43065
|
+
};
|
|
43066
|
+
const previous = await readFile47(target, "utf8");
|
|
43067
|
+
const next = updateManProgressHtml(previous, taskId, status2, reason);
|
|
43068
|
+
if (next !== previous) {
|
|
43069
|
+
temporary = `${target}.${randomUUID3()}.tmp`;
|
|
43070
|
+
await writeFile50(temporary, next, { flag: "wx", mode: stat5.mode });
|
|
43071
|
+
if (await readFile47(target, "utf8") !== previous)
|
|
43072
|
+
throw new Error("progress page changed during sync");
|
|
43073
|
+
await replaceFileAtomically(temporary, target);
|
|
43074
|
+
}
|
|
43075
|
+
return { status: "synced" };
|
|
43076
|
+
} catch (error) {
|
|
43077
|
+
if (error.code === "ENOENT" && !temporary)
|
|
43078
|
+
return { status: "absent" };
|
|
43079
|
+
return {
|
|
43080
|
+
status: "manual_sync",
|
|
43081
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
43082
|
+
};
|
|
43083
|
+
} finally {
|
|
43084
|
+
if (temporary) await rm23(temporary, { force: true });
|
|
43085
|
+
}
|
|
43086
|
+
}
|
|
43087
|
+
|
|
43088
|
+
// src/context/man-delivery-runtime.ts
|
|
43089
|
+
var execFile10 = promisify9(execFileCallback7);
|
|
43090
|
+
var MAN_DELIVERY_POLICY = 3;
|
|
43091
|
+
function isManDelivery(metadata) {
|
|
43092
|
+
return metadata.workflowMode === "man" && metadata.governance.policyVersions.planning === MAN_DELIVERY_POLICY && metadata.governance.planDecision !== "solo_handoff";
|
|
43093
|
+
}
|
|
43094
|
+
async function manGit(root, args) {
|
|
43095
|
+
return (await execFile10("git", args, {
|
|
43096
|
+
cwd: root,
|
|
43097
|
+
encoding: "utf8",
|
|
43098
|
+
maxBuffer: 16 * 1024 * 1024
|
|
43099
|
+
})).stdout;
|
|
43100
|
+
}
|
|
43101
|
+
async function hasGitWorktree(root) {
|
|
43102
|
+
try {
|
|
43103
|
+
return (await manGit(root, ["rev-parse", "--is-inside-work-tree"])).trim() === "true";
|
|
43104
|
+
} catch (error) {
|
|
43105
|
+
if (error.code === 128 && String(error.stderr).includes(
|
|
43106
|
+
"not a git repository"
|
|
43107
|
+
))
|
|
43108
|
+
return false;
|
|
43109
|
+
throw error;
|
|
43110
|
+
}
|
|
43111
|
+
}
|
|
43112
|
+
async function readManPlanFile(root, file) {
|
|
43113
|
+
assertManPlanPath(file);
|
|
43114
|
+
const resolvedRoot = await realpath4(root);
|
|
43115
|
+
const resolved = await realpath4(path71.join(root, file));
|
|
43116
|
+
const relative = path71.relative(resolvedRoot, resolved).split(path71.sep).join("/");
|
|
43117
|
+
assertManPlanPath(relative);
|
|
43118
|
+
if (relative !== file) throw new Error("MANCODE_MAN_PLAN_USE_REAL_PATH");
|
|
43119
|
+
if (!(await lstat28(resolved)).isFile())
|
|
43120
|
+
throw new Error("MANCODE_MAN_PLAN_FILE_REQUIRED");
|
|
43121
|
+
if (!await hasGitWorktree(root)) return readFile48(resolved, "utf8");
|
|
43122
|
+
try {
|
|
43123
|
+
await manGit(root, ["check-ignore", "--no-index", "-q", "--", relative]);
|
|
43124
|
+
} catch (error) {
|
|
43125
|
+
if (error.code === 1)
|
|
43126
|
+
return readFile48(resolved, "utf8");
|
|
43127
|
+
throw error;
|
|
43128
|
+
}
|
|
43129
|
+
throw new Error("MANCODE_MAN_PLAN_IGNORED");
|
|
43130
|
+
}
|
|
43131
|
+
async function bindManPlan(root, file, document, previous) {
|
|
43132
|
+
if (await readManPlanFile(root, file) !== document)
|
|
43133
|
+
throw new Error("MANCODE_MAN_PLAN_FILE_CHANGED");
|
|
43134
|
+
const existing = previous === null ? null : parseManDeliveryPlan(previous);
|
|
43135
|
+
return compileManDeliveryPlan(
|
|
43136
|
+
{
|
|
43137
|
+
version: 1,
|
|
43138
|
+
path: file,
|
|
43139
|
+
baseHead: existing ? existing.source.baseHead : await readCheckoutCodeHead(root)
|
|
43140
|
+
},
|
|
43141
|
+
document
|
|
43142
|
+
);
|
|
43143
|
+
}
|
|
43144
|
+
async function readBoundManPlan(root, task) {
|
|
43145
|
+
const plan = task.plan && parseManDeliveryPlan(task.plan.content);
|
|
43146
|
+
if (!plan) throw new Error("MANCODE_MAN_PLAN_SOURCE_REQUIRED");
|
|
43147
|
+
const document = await readManPlanFile(root, plan.source.path);
|
|
43148
|
+
if (parseManPlanDocument(document).baseline !== plan.baseline)
|
|
43149
|
+
throw new Error("MANCODE_MAN_PLAN_BASELINE_CHANGED");
|
|
43150
|
+
return { ...plan, document };
|
|
43151
|
+
}
|
|
43152
|
+
async function captureManSubject(root, task) {
|
|
43153
|
+
const bound = await readBoundManPlan(root, task);
|
|
43154
|
+
if (!await hasGitWorktree(root))
|
|
43155
|
+
throw new Error(
|
|
43156
|
+
"MANCODE_MAN_DELIVERY_GIT_REQUIRED: planning is available; versioned delivery is not"
|
|
43157
|
+
);
|
|
43158
|
+
const files = [
|
|
43159
|
+
...new Set(
|
|
43160
|
+
(await manGit(root, [
|
|
43161
|
+
"ls-files",
|
|
43162
|
+
"-z",
|
|
43163
|
+
"--cached",
|
|
43164
|
+
"--others",
|
|
43165
|
+
"--exclude-standard"
|
|
43166
|
+
])).split("\0").filter(Boolean)
|
|
43167
|
+
)
|
|
43168
|
+
].sort();
|
|
43169
|
+
const hashes = [];
|
|
43170
|
+
for (const file of files) {
|
|
43171
|
+
if (file.startsWith(".mancode/") || file === "\u9879\u76EE\u8FDB\u5EA6.html") continue;
|
|
43172
|
+
const absolute = path71.join(root, file);
|
|
43173
|
+
let contents;
|
|
43174
|
+
let kind = "deleted";
|
|
43175
|
+
try {
|
|
43176
|
+
const stat5 = await lstat28(absolute);
|
|
43177
|
+
kind = `file:${stat5.mode & 73}`;
|
|
43178
|
+
if (stat5.isSymbolicLink()) {
|
|
43179
|
+
kind = "symlink";
|
|
43180
|
+
const target = path71.relative(
|
|
43181
|
+
await realpath4(root),
|
|
43182
|
+
await realpath4(absolute)
|
|
43183
|
+
);
|
|
43184
|
+
if (target.startsWith(`..${path71.sep}`) || path71.isAbsolute(target))
|
|
43185
|
+
throw new Error("MANCODE_MAN_EXTERNAL_DEPENDENCY_UNVERIFIED");
|
|
43186
|
+
contents = `${await readlink2(absolute)}\0${await readFile48(absolute, "base64")}`;
|
|
43187
|
+
} else if (stat5.isFile()) {
|
|
43188
|
+
contents = file === bound.source.path ? bound.baseline : await readFile48(absolute);
|
|
43189
|
+
} else throw new Error("MANCODE_MAN_CONTENT_NODE_UNSUPPORTED");
|
|
43190
|
+
} catch (error) {
|
|
43191
|
+
if (error.code !== "ENOENT") throw error;
|
|
43192
|
+
kind = "deleted";
|
|
43193
|
+
contents = "<deleted>";
|
|
43194
|
+
}
|
|
43195
|
+
hashes.push([
|
|
43196
|
+
file,
|
|
43197
|
+
createHash11("sha256").update(`${kind}\0`).update(contents).digest("hex")
|
|
43198
|
+
]);
|
|
43199
|
+
}
|
|
43200
|
+
return {
|
|
43201
|
+
contentDigest: digestCanonicalJson(hashes),
|
|
43202
|
+
environment: `${process.platform}/${process.arch};node=${process.version}`
|
|
43203
|
+
};
|
|
43204
|
+
}
|
|
43205
|
+
function assertManReviewCoverage(task, review, subject) {
|
|
43206
|
+
if (!isManDelivery(task.metadata)) {
|
|
43207
|
+
if (review.delivery) throw new Error("MANCODE_MAN_DELIVERY_MODE_REQUIRED");
|
|
43208
|
+
return;
|
|
43209
|
+
}
|
|
43210
|
+
for (const blocker of task.review.blockers.filter(
|
|
43211
|
+
(item) => item.status === "open"
|
|
43212
|
+
)) {
|
|
43213
|
+
if (!review.blockers.some((item) => item.blockerId === blocker.blockerId))
|
|
43214
|
+
throw new Error(
|
|
43215
|
+
`MANCODE_MAN_REVIEW_FINDING_DROPPED: ${blocker.displayId}`
|
|
43216
|
+
);
|
|
43217
|
+
}
|
|
43218
|
+
if (review.status === "skipped") return;
|
|
43219
|
+
if (!review.delivery && review.status !== "passed") return;
|
|
43220
|
+
if (!review.delivery || review.delivery.subject.contentDigest !== subject.contentDigest || review.delivery.subject.environment !== subject.environment) {
|
|
43221
|
+
throw new Error("MANCODE_MAN_REVIEW_SUBJECT_STALE");
|
|
43222
|
+
}
|
|
43223
|
+
const criteria = task.requirements.acceptanceCriteria;
|
|
43224
|
+
if (review.delivery.coverage.some(
|
|
43225
|
+
(row) => !criteria.some((criterion) => criterion.displayId === row.acceptanceId)
|
|
43226
|
+
))
|
|
43227
|
+
throw new Error("MANCODE_MAN_REVIEW_UNKNOWN_ACCEPTANCE");
|
|
43228
|
+
if (review.status !== "passed") return;
|
|
43229
|
+
for (const criterion of criteria.filter((item) => item.required)) {
|
|
43230
|
+
if (!review.delivery.coverage.some(
|
|
43231
|
+
(row) => row.acceptanceId === criterion.displayId && row.status === "met"
|
|
43232
|
+
)) {
|
|
43233
|
+
throw new Error(
|
|
43234
|
+
`MANCODE_MAN_REVIEW_ACCEPTANCE_MISSING: ${criterion.displayId}`
|
|
43235
|
+
);
|
|
43236
|
+
}
|
|
43237
|
+
}
|
|
43238
|
+
}
|
|
43239
|
+
function assertManVerificationSubjects(task, verification, subject) {
|
|
43240
|
+
for (const check of verification.checks) {
|
|
43241
|
+
for (const evidence of [check.automated, check.manual]) {
|
|
43242
|
+
if (!isManDelivery(task.metadata)) {
|
|
43243
|
+
if (evidence?.subject)
|
|
43244
|
+
throw new Error("MANCODE_MAN_DELIVERY_MODE_REQUIRED");
|
|
43245
|
+
} else if (evidence?.status === "passed" && (evidence.subject?.contentDigest !== subject.contentDigest || evidence.subject.environment !== subject.environment)) {
|
|
43246
|
+
throw new Error(
|
|
43247
|
+
`MANCODE_MAN_VERIFICATION_SUBJECT_STALE: ${check.displayId}`
|
|
43248
|
+
);
|
|
43249
|
+
}
|
|
43250
|
+
}
|
|
43251
|
+
}
|
|
43252
|
+
}
|
|
43253
|
+
function manScopeContains(metadata, file) {
|
|
43254
|
+
return metadata.implementationScope.include.some(
|
|
43255
|
+
(include) => path71.matchesGlob(file, include)
|
|
43256
|
+
) && !metadata.implementationScope.exclude.some(
|
|
43257
|
+
(exclude) => path71.matchesGlob(file, exclude)
|
|
43258
|
+
);
|
|
43259
|
+
}
|
|
43260
|
+
function assertManPlanInScope(metadata, file) {
|
|
43261
|
+
if (!manScopeContains(metadata, file))
|
|
43262
|
+
throw new Error(
|
|
43263
|
+
`MANCODE_MAN_PLAN_OUTSIDE_SCOPE: ${file} is not covered by implementationScope.include; add that exact repo-relative path or a covering glob`
|
|
43264
|
+
);
|
|
43265
|
+
}
|
|
43266
|
+
function manDeliveryFinalization(task, subject, recordCurrent, outsideScope, outsideScopeDirty, pendingCommit) {
|
|
43267
|
+
const blockers = [];
|
|
43268
|
+
if (task.metadata.governance.planDecision !== "governed_execution")
|
|
43269
|
+
blockers.push({
|
|
43270
|
+
code: "plan_execution_required",
|
|
43271
|
+
status: task.metadata.governance.planDecision ?? "unconfirmed",
|
|
43272
|
+
nextAction: "Confirm the bound plan for governed execution."
|
|
43273
|
+
});
|
|
43274
|
+
const reviewSubjectCurrent = task.review.status === "skipped" || task.review.status === "passed" && task.review.delivery?.subject.contentDigest === subject.contentDigest && task.review.delivery?.subject.environment === subject.environment;
|
|
43275
|
+
if (!reviewSubjectCurrent)
|
|
43276
|
+
blockers.push({
|
|
43277
|
+
code: "review_incomplete",
|
|
43278
|
+
status: task.review.status === "passed" ? "stale" : task.review.status,
|
|
43279
|
+
nextAction: task.review.status === "blocked" ? "Fix the recorded findings, verify the changed module, and submit the targeted review result." : "Complete one module review for the current subject and apply its review ledger."
|
|
43280
|
+
});
|
|
43281
|
+
const verification = assessManVerification(task, subject);
|
|
43282
|
+
if (verification.status !== "passed")
|
|
43283
|
+
blockers.push({
|
|
43284
|
+
code: "verification_incomplete",
|
|
43285
|
+
status: verification.status,
|
|
43286
|
+
nextAction: verification.nextAction
|
|
43287
|
+
});
|
|
43288
|
+
if (!recordCurrent)
|
|
43289
|
+
blockers.push({
|
|
43290
|
+
code: "delivery_record_stale",
|
|
43291
|
+
status: "stale",
|
|
43292
|
+
nextAction: "Sync the delivery record after the latest evidence change."
|
|
43293
|
+
});
|
|
43294
|
+
if (outsideScope.length)
|
|
43295
|
+
blockers.push({
|
|
43296
|
+
code: "committed_outside_scope",
|
|
43297
|
+
status: "blocked",
|
|
43298
|
+
nextAction: "Remove the unrelated committed changes or obtain an approved scope realignment.",
|
|
43299
|
+
files: outsideScope
|
|
43300
|
+
});
|
|
43301
|
+
if (outsideScopeDirty.length)
|
|
43302
|
+
blockers.push({
|
|
43303
|
+
code: "uncommitted_outside_scope",
|
|
43304
|
+
status: "blocked",
|
|
43305
|
+
nextAction: "Move, stash, or separately commit unrelated working-tree changes before recording final delivery evidence; do not add them to this task commit.",
|
|
43306
|
+
files: outsideScopeDirty
|
|
43307
|
+
});
|
|
43308
|
+
if (pendingCommit.length)
|
|
43309
|
+
blockers.push({
|
|
43310
|
+
code: "uncommitted_changes",
|
|
43311
|
+
status: "pending",
|
|
43312
|
+
nextAction: "Commit only the task-owned versionable changes.",
|
|
43313
|
+
files: pendingCommit
|
|
43314
|
+
});
|
|
43315
|
+
return {
|
|
43316
|
+
status: blockers.length ? "incomplete" : "ready",
|
|
43317
|
+
blockers
|
|
43318
|
+
};
|
|
43319
|
+
}
|
|
43320
|
+
function manInspectionFailureFinalization(error) {
|
|
43321
|
+
const diagnostic = error instanceof Error ? error.message : String(error);
|
|
43322
|
+
let nextAction = "Resolve the reported inspection diagnostic, then run delivery inspect again.";
|
|
43323
|
+
if (diagnostic.includes("MANCODE_MAN_PLAN_BASELINE_CHANGED")) {
|
|
43324
|
+
nextAction = "Restore the approved plan baseline or explicitly reframe the task before inspecting delivery again.";
|
|
43325
|
+
} else if (diagnostic.includes("MANCODE_MAN_PLAN_IGNORED")) {
|
|
43326
|
+
nextAction = "Remove the ignore rule for the approved plan file, then inspect delivery again.";
|
|
43327
|
+
} else if (diagnostic.includes("MANCODE_MAN_DELIVERY_GIT_REQUIRED")) {
|
|
43328
|
+
nextAction = "Use a Git worktree for versioned delivery, then inspect the task again.";
|
|
43329
|
+
} else if (diagnostic.includes("MANCODE_MAN_CONTENT_NODE_UNSUPPORTED")) {
|
|
43330
|
+
nextAction = "Remove the unsupported repository content node from the delivery subject or use a supported versioned file, then inspect again.";
|
|
43331
|
+
}
|
|
43332
|
+
return {
|
|
43333
|
+
status: "incomplete",
|
|
43334
|
+
blockers: [
|
|
43335
|
+
{
|
|
43336
|
+
code: "inspection_failed",
|
|
43337
|
+
status: "failed",
|
|
43338
|
+
nextAction,
|
|
43339
|
+
diagnostic
|
|
43340
|
+
}
|
|
43341
|
+
]
|
|
43342
|
+
};
|
|
43343
|
+
}
|
|
43344
|
+
function assessManVerification(task, subject) {
|
|
43345
|
+
const missingConfiguredCriteria = task.requirements.acceptanceCriteria.filter(
|
|
43346
|
+
(criterion) => criterion.required && criterion.verificationSurfaces === void 0
|
|
43347
|
+
).map((criterion) => criterion.displayId);
|
|
43348
|
+
if (missingConfiguredCriteria.length) {
|
|
43349
|
+
return {
|
|
43350
|
+
status: "surface_requirement_missing",
|
|
43351
|
+
nextAction: `Reframe or re-finalize requirements with verificationSurfaces for ${missingConfiguredCriteria.join(", ")}.`
|
|
43352
|
+
};
|
|
43353
|
+
}
|
|
43354
|
+
if (task.verification.status !== "passed") {
|
|
43355
|
+
return {
|
|
43356
|
+
status: task.verification.status,
|
|
43357
|
+
nextAction: "Run or confirm the missing acceptance evidence against the current subject."
|
|
43358
|
+
};
|
|
43359
|
+
}
|
|
43360
|
+
const criteria = new Map(
|
|
43361
|
+
task.requirements.acceptanceCriteria.map((criterion) => [
|
|
43362
|
+
criterion.criterionId,
|
|
43363
|
+
criterion
|
|
43364
|
+
])
|
|
43365
|
+
);
|
|
43366
|
+
const missingRequirements = [];
|
|
43367
|
+
const missingActual = [];
|
|
43368
|
+
const stale = [];
|
|
43369
|
+
const mismatches = [];
|
|
43370
|
+
for (const check of task.verification.checks) {
|
|
43371
|
+
if (!check.required) continue;
|
|
43372
|
+
const criterion = criteria.get(check.criterionId);
|
|
43373
|
+
for (const slot of ["automated", "manual"]) {
|
|
43374
|
+
const evidence = check[slot];
|
|
43375
|
+
if (evidence?.status !== "passed") continue;
|
|
43376
|
+
const expected = criterion?.verificationSurfaces?.[slot];
|
|
43377
|
+
const label = `${check.displayId}:${slot}`;
|
|
43378
|
+
if (!expected) {
|
|
43379
|
+
missingRequirements.push(label);
|
|
43380
|
+
continue;
|
|
43381
|
+
}
|
|
43382
|
+
if (!evidence.surface) {
|
|
43383
|
+
missingActual.push(label);
|
|
43384
|
+
continue;
|
|
43385
|
+
}
|
|
43386
|
+
if (subject !== void 0 && (evidence.subject?.contentDigest !== subject.contentDigest || evidence.subject.environment !== subject.environment)) {
|
|
43387
|
+
stale.push(label);
|
|
43388
|
+
continue;
|
|
43389
|
+
}
|
|
43390
|
+
if (evidence.surface !== expected) {
|
|
43391
|
+
mismatches.push(
|
|
43392
|
+
`${label} requires ${expected}, recorded ${evidence.surface}`
|
|
43393
|
+
);
|
|
43394
|
+
}
|
|
43395
|
+
}
|
|
43396
|
+
}
|
|
43397
|
+
if (missingRequirements.length) {
|
|
43398
|
+
return {
|
|
43399
|
+
status: "surface_requirement_missing",
|
|
43400
|
+
nextAction: `Reframe or re-finalize requirements with verificationSurfaces for ${missingRequirements.join(", ")}.`
|
|
43401
|
+
};
|
|
43402
|
+
}
|
|
43403
|
+
if (missingActual.length) {
|
|
43404
|
+
return {
|
|
43405
|
+
status: "surface_required",
|
|
43406
|
+
nextAction: `Record the actual observation surface for ${missingActual.join(", ")}.`
|
|
43407
|
+
};
|
|
43408
|
+
}
|
|
43409
|
+
if (stale.length) {
|
|
43410
|
+
return {
|
|
43411
|
+
status: "stale",
|
|
43412
|
+
nextAction: `Re-run or confirm evidence against the current subject for ${stale.join(", ")}.`
|
|
43413
|
+
};
|
|
43414
|
+
}
|
|
43415
|
+
if (mismatches.length) {
|
|
43416
|
+
return {
|
|
43417
|
+
status: "surface_mismatch",
|
|
43418
|
+
nextAction: `Record evidence at the required observation surface: ${mismatches.join("; ")}.`
|
|
43419
|
+
};
|
|
43420
|
+
}
|
|
43421
|
+
return { status: "passed", nextAction: "No verification action required." };
|
|
43422
|
+
}
|
|
43423
|
+
async function inspectManDelivery(root, task) {
|
|
43424
|
+
if (!isManDelivery(task.metadata))
|
|
43425
|
+
throw new Error("MANCODE_MAN_DELIVERY_MODE_REQUIRED");
|
|
43426
|
+
const bound = await readBoundManPlan(root, task);
|
|
43427
|
+
const subject = await captureManSubject(root, task);
|
|
43428
|
+
const head = await readCheckoutCodeHead(root);
|
|
43429
|
+
const trackedDirty = (await manGit(
|
|
43430
|
+
root,
|
|
43431
|
+
head === null ? ["ls-files", "-z", "--cached"] : ["diff", "--name-only", "--relative", "-z", "HEAD", "--", "."]
|
|
43432
|
+
)).split("\0").filter(Boolean);
|
|
43433
|
+
const untracked = (await manGit(root, ["ls-files", "--others", "--exclude-standard", "-z"])).split("\0").filter(Boolean);
|
|
43434
|
+
const inTask = (file) => file === bound.source.path || manScopeContains(task.metadata, file);
|
|
43435
|
+
const dirtyFiles = [.../* @__PURE__ */ new Set([...trackedDirty, ...untracked])].filter(
|
|
43436
|
+
(file) => !file.startsWith(".mancode/")
|
|
43437
|
+
);
|
|
43438
|
+
const pendingCommit = dirtyFiles.filter(inTask);
|
|
43439
|
+
const outsideScopeDirty = dirtyFiles.filter((file) => !inTask(file));
|
|
43440
|
+
const committedChanges = head === null ? [] : bound.source.baseHead === null ? (await manGit(root, ["ls-tree", "-r", "--name-only", "-z", "HEAD"])).split("\0").filter(Boolean) : (await manGit(root, [
|
|
43441
|
+
"diff",
|
|
43442
|
+
"--name-only",
|
|
43443
|
+
"--relative",
|
|
43444
|
+
"-z",
|
|
43445
|
+
bound.source.baseHead,
|
|
43446
|
+
"HEAD",
|
|
43447
|
+
"--",
|
|
43448
|
+
"."
|
|
43449
|
+
])).split("\0").filter(Boolean);
|
|
43450
|
+
const outsideScope = committedChanges.filter(
|
|
43451
|
+
(file) => !file.startsWith(".mancode/") && !inTask(file)
|
|
43452
|
+
);
|
|
43453
|
+
const upstream = (await manGit(root, [
|
|
43454
|
+
"for-each-ref",
|
|
43455
|
+
"--format=%(upstream:short)",
|
|
43456
|
+
`refs/heads/${(await manGit(root, ["branch", "--show-current"])).trim()}`
|
|
43457
|
+
])).trim() || null;
|
|
43458
|
+
const recordCurrent = parseManPlanDocument(bound.document).record === renderManDeliveryRecord(task, subject).trim();
|
|
43459
|
+
return {
|
|
43460
|
+
subject,
|
|
43461
|
+
source: bound.source,
|
|
43462
|
+
pendingCommit,
|
|
43463
|
+
outsideScopeDirty,
|
|
43464
|
+
outsideScope,
|
|
43465
|
+
upstream,
|
|
43466
|
+
publication: upstream === null ? "unpublished" : "unknown",
|
|
43467
|
+
finalization: manDeliveryFinalization(
|
|
43468
|
+
task,
|
|
43469
|
+
subject,
|
|
43470
|
+
recordCurrent,
|
|
43471
|
+
outsideScope,
|
|
43472
|
+
outsideScopeDirty,
|
|
43473
|
+
pendingCommit
|
|
43474
|
+
)
|
|
43475
|
+
};
|
|
43476
|
+
}
|
|
43477
|
+
async function inspectManPublication(root) {
|
|
43478
|
+
const head = await readCheckoutCodeHead(root);
|
|
43479
|
+
const branch = (await manGit(root, ["branch", "--show-current"])).trim();
|
|
43480
|
+
if (!head || !branch)
|
|
43481
|
+
return { status: "unpublished", reason: "no committed task branch" };
|
|
43482
|
+
const [remote, ref] = (await manGit(root, [
|
|
43483
|
+
"for-each-ref",
|
|
43484
|
+
"--format=%(upstream:remotename)%00%(upstream:remoteref)",
|
|
43485
|
+
`refs/heads/${branch}`
|
|
43486
|
+
])).trim().split("\0");
|
|
43487
|
+
if (!remote || !ref)
|
|
43488
|
+
return { status: "unpublished", reason: "no upstream configured" };
|
|
43489
|
+
try {
|
|
43490
|
+
const remoteHead = (await manGit(root, ["ls-remote", "--exit-code", "--", remote, ref])).trim().split(/\s+/)[0];
|
|
43491
|
+
if (!remoteHead)
|
|
43492
|
+
return { status: "unpublished", reason: "upstream ref absent" };
|
|
43493
|
+
if (remoteHead === head) return { status: "published", head, remoteHead };
|
|
43494
|
+
try {
|
|
43495
|
+
await manGit(root, ["merge-base", "--is-ancestor", head, remoteHead]);
|
|
43496
|
+
return { status: "published", head, remoteHead };
|
|
43497
|
+
} catch (error) {
|
|
43498
|
+
if (error.code === 1)
|
|
43499
|
+
return {
|
|
43500
|
+
status: "unpublished",
|
|
43501
|
+
head,
|
|
43502
|
+
remoteHead,
|
|
43503
|
+
reason: "upstream does not contain this commit"
|
|
43504
|
+
};
|
|
43505
|
+
throw error;
|
|
43506
|
+
}
|
|
43507
|
+
} catch (error) {
|
|
43508
|
+
return {
|
|
43509
|
+
status: "unverified",
|
|
43510
|
+
reason: "upstream publication could not be verified; no fetch, push or business-state change was performed",
|
|
43511
|
+
diagnostic: error instanceof Error ? error.message : String(error)
|
|
43512
|
+
};
|
|
43513
|
+
}
|
|
43514
|
+
}
|
|
43515
|
+
function renderManDeliveryRecord(task, subject) {
|
|
43516
|
+
const reviewStale = subject && task.review.delivery && (task.review.delivery.subject.contentDigest !== subject.contentDigest || task.review.delivery.subject.environment !== subject.environment);
|
|
43517
|
+
const verificationStatus = assessManVerification(task, subject).status;
|
|
43518
|
+
return [
|
|
43519
|
+
`Task: ${task.metadata.taskRef.namespace}:${task.metadata.taskRef.taskId}`,
|
|
43520
|
+
`Plan version: ${task.metadata.governance.planVersion}`,
|
|
43521
|
+
`Review: ${reviewStale ? "stale" : task.review.status}`,
|
|
43522
|
+
`Verification: ${verificationStatus}`,
|
|
43523
|
+
"",
|
|
43524
|
+
...task.review.delivery ? [
|
|
43525
|
+
`Reviewer declaration: ${task.review.delivery.reviewer}`,
|
|
43526
|
+
`Direction: ${task.review.delivery.direction}`,
|
|
43527
|
+
`Correctness: ${task.review.delivery.correctness}`,
|
|
43528
|
+
`Proportionality: ${task.review.delivery.proportionality}`,
|
|
43529
|
+
`Next: ${task.review.delivery.nextAction}`
|
|
43530
|
+
] : [
|
|
43531
|
+
"Next: finish relevant verification, then review the complete module."
|
|
43532
|
+
],
|
|
43533
|
+
...task.review.blockers.map(
|
|
43534
|
+
(item) => `- ${item.displayId}: ${item.status} \u2014 ${item.summary}`
|
|
43535
|
+
),
|
|
43536
|
+
...task.review.delivery?.coverage.map(
|
|
43537
|
+
(row) => `- ${row.acceptanceId}: ${row.status} \u2014 ${row.evidence}`
|
|
43538
|
+
) ?? [],
|
|
43539
|
+
...task.verification.checks.map((check) => {
|
|
43540
|
+
const component = (item) => item === null ? "n/a" : `${item.status}(surface=${item.surface ?? "unspecified"})`;
|
|
43541
|
+
return `- ${check.displayId}: automated=${component(check.automated)}; manual=${component(check.manual)}; ${check.automated?.summary ?? check.manual?.summary ?? "No evidence yet."}`;
|
|
43542
|
+
})
|
|
43543
|
+
].join("\n");
|
|
43544
|
+
}
|
|
43545
|
+
async function syncManDeliveryRecord(root, task) {
|
|
43546
|
+
const bound = await readBoundManPlan(root, task);
|
|
43547
|
+
if (task.metadata.governance.planDecision === "governed_execution" && !manScopeContains(task.metadata, bound.source.path))
|
|
43548
|
+
assertManPlanInScope(task.metadata, bound.source.path);
|
|
43549
|
+
const subject = await hasGitWorktree(root) ? await captureManSubject(root, task) : void 0;
|
|
43550
|
+
const next = replaceManDeliveryRecord(
|
|
43551
|
+
bound.document,
|
|
43552
|
+
renderManDeliveryRecord(task, subject)
|
|
43553
|
+
);
|
|
43554
|
+
if (scanSharedText(next).some(
|
|
43555
|
+
(finding) => ["authorization", "cookie", "private_key", "secret"].includes(
|
|
43556
|
+
finding.kind
|
|
43557
|
+
)
|
|
43558
|
+
))
|
|
43559
|
+
throw new Error(
|
|
43560
|
+
"MANCODE_MAN_DOCUMENT_SENSITIVE: redact credential-like text before versioning"
|
|
43561
|
+
);
|
|
43562
|
+
const target = await realpath4(path71.join(root, bound.source.path));
|
|
43563
|
+
if (next !== bound.document) {
|
|
43564
|
+
const temporary = `${target}.${randomUUID4()}.tmp`;
|
|
43565
|
+
try {
|
|
43566
|
+
await writeFile51(temporary, next, {
|
|
43567
|
+
flag: "wx",
|
|
43568
|
+
mode: (await lstat28(target)).mode
|
|
43569
|
+
});
|
|
43570
|
+
if (await readFile48(target, "utf8") !== bound.document)
|
|
43571
|
+
throw new Error("MANCODE_MAN_PLAN_FILE_CHANGED");
|
|
43572
|
+
await replaceFileAtomically(temporary, target);
|
|
43573
|
+
} finally {
|
|
43574
|
+
await rm24(temporary, { force: true });
|
|
43575
|
+
}
|
|
43576
|
+
}
|
|
43577
|
+
let taskId;
|
|
43578
|
+
try {
|
|
43579
|
+
taskId = manProgressTaskId(bound.baseline) ?? `${task.metadata.taskRef.namespace}:${task.metadata.taskRef.taskId}`;
|
|
43580
|
+
} catch (error) {
|
|
43581
|
+
return {
|
|
43582
|
+
status: "manual_sync",
|
|
43583
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
43584
|
+
};
|
|
43585
|
+
}
|
|
43586
|
+
const unresolved = task.requirements.blockingUnknowns.filter(
|
|
43587
|
+
(item) => item.status === "open"
|
|
43588
|
+
);
|
|
43589
|
+
const reason = task.metadata.status === "blocked" && unresolved.length ? unresolved.map((item) => item.statement).join("; ") : null;
|
|
43590
|
+
const verified = subject !== void 0 && assessManVerification(task, subject).status === "passed";
|
|
43591
|
+
const reviewed = task.review.status === "skipped" || task.review.status === "passed" && task.review.delivery?.subject.contentDigest === subject?.contentDigest && task.review.delivery?.subject.environment === subject?.environment;
|
|
43592
|
+
const status2 = reason ? "\u963B\u585E" : verified && reviewed ? "\u5DF2\u5B8C\u6210" : verified && task.review.status !== "blocked" ? "\u5F85\u5BA1\u6838" : task.metadata.currentStep >= 5 ? "\u8FDB\u884C\u4E2D" : "\u672A\u5B8C\u6210";
|
|
43593
|
+
return syncManProgressPage(
|
|
43594
|
+
root,
|
|
43595
|
+
taskId,
|
|
43596
|
+
status2,
|
|
43597
|
+
reason,
|
|
43598
|
+
manScopeContains(task.metadata, "\u9879\u76EE\u8FDB\u5EA6.html")
|
|
43599
|
+
);
|
|
43600
|
+
}
|
|
43601
|
+
async function assertManDeliveryReady(root, task) {
|
|
43602
|
+
if (!isManDelivery(task.metadata)) return;
|
|
43603
|
+
const result2 = await inspectManDelivery(root, task);
|
|
43604
|
+
const blocker = (code) => result2.finalization.blockers.find((item) => item.code === code);
|
|
43605
|
+
const plan = blocker("plan_execution_required");
|
|
43606
|
+
if (plan)
|
|
43607
|
+
throw new Error(
|
|
43608
|
+
`MANCODE_MAN_DELIVERY_EXECUTION_REQUIRED: ${plan.nextAction}`
|
|
43609
|
+
);
|
|
43610
|
+
const verification = blocker("verification_incomplete");
|
|
43611
|
+
if (verification)
|
|
43612
|
+
throw new Error(
|
|
43613
|
+
`MANCODE_MAN_VERIFICATION_INCOMPLETE: ${verification.status}; ${verification.nextAction}`
|
|
43614
|
+
);
|
|
43615
|
+
const review = blocker("review_incomplete");
|
|
43616
|
+
if (review)
|
|
43617
|
+
throw new Error(
|
|
43618
|
+
`MANCODE_MAN_REVIEW_INCOMPLETE: ${review.status}; ${review.nextAction}`
|
|
43619
|
+
);
|
|
43620
|
+
assertManReviewCoverage(task, task.review, result2.subject);
|
|
43621
|
+
assertManVerificationSubjects(task, task.verification, result2.subject);
|
|
43622
|
+
if (blocker("delivery_record_stale"))
|
|
43623
|
+
throw new Error("MANCODE_MAN_DELIVERY_RECORD_STALE");
|
|
43624
|
+
const outside = blocker("committed_outside_scope");
|
|
43625
|
+
if (outside)
|
|
43626
|
+
throw new Error(
|
|
43627
|
+
`MANCODE_MAN_COMMIT_OUTSIDE_SCOPE: ${outside.files?.join(", ")}`
|
|
43628
|
+
);
|
|
43629
|
+
const outsideDirty = blocker("uncommitted_outside_scope");
|
|
43630
|
+
if (outsideDirty)
|
|
43631
|
+
throw new Error(
|
|
43632
|
+
`MANCODE_MAN_UNCOMMITTED_OUTSIDE_SCOPE: ${outsideDirty.files?.join(", ")}`
|
|
43633
|
+
);
|
|
43634
|
+
const pending = blocker("uncommitted_changes");
|
|
43635
|
+
if (pending)
|
|
43636
|
+
throw new Error(
|
|
43637
|
+
`MANCODE_MAN_DELIVERY_UNCOMMITTED: ${pending.files?.join(", ")}`
|
|
43638
|
+
);
|
|
43639
|
+
}
|
|
43640
|
+
|
|
42124
43641
|
// src/context/scope-change.ts
|
|
42125
|
-
import { createHash as
|
|
43642
|
+
import { createHash as createHash12 } from "crypto";
|
|
42126
43643
|
async function changeV3WorkflowScope(input) {
|
|
42127
43644
|
const taskRef = parseTaskRefValue(input.taskRef);
|
|
42128
43645
|
if (taskRef.namespace !== "shared") {
|
|
@@ -42741,7 +44258,7 @@ function scopeSuccessorClaimId(operationId, predecessorClaimId, timestamp4) {
|
|
|
42741
44258
|
if (Number.isNaN(milliseconds)) {
|
|
42742
44259
|
throw new Error("MANCODE_SCOPE_TIMESTAMP_INVALID");
|
|
42743
44260
|
}
|
|
42744
|
-
const entropy =
|
|
44261
|
+
const entropy = createHash12("sha256").update(`scope-change:${operationId}:${predecessorClaimId}`, "utf8").digest().subarray(0, 10);
|
|
42745
44262
|
return createUlid(milliseconds, entropy);
|
|
42746
44263
|
}
|
|
42747
44264
|
function requireCodeHead3(context) {
|
|
@@ -42760,7 +44277,7 @@ function compareUtf815(left, right) {
|
|
|
42760
44277
|
// src/context/plan-revision.ts
|
|
42761
44278
|
async function reviseV3Plan(input) {
|
|
42762
44279
|
const taskRef = parseTaskRefValue(input.taskRef);
|
|
42763
|
-
|
|
44280
|
+
let plan = requirePlan(input.plan);
|
|
42764
44281
|
const planDecision = parsePlanDecision(input.planDecision);
|
|
42765
44282
|
const submittedScope = input.implementationScope === void 0 ? null : normalizeImplementationScope(input.implementationScope);
|
|
42766
44283
|
if (taskRef.namespace === "shared") {
|
|
@@ -42777,6 +44294,21 @@ async function reviseV3Plan(input) {
|
|
|
42777
44294
|
});
|
|
42778
44295
|
let journal = null;
|
|
42779
44296
|
try {
|
|
44297
|
+
if (isManDelivery(context.task.metadata)) {
|
|
44298
|
+
if (input.planSource) {
|
|
44299
|
+
plan = await bindManPlan(
|
|
44300
|
+
input.projectRoot,
|
|
44301
|
+
input.planSource,
|
|
44302
|
+
plan,
|
|
44303
|
+
context.task.plan?.content ?? null
|
|
44304
|
+
);
|
|
44305
|
+
} else {
|
|
44306
|
+
await readBoundManPlan(input.projectRoot, context.task);
|
|
44307
|
+
if (plan !== context.task.plan?.content)
|
|
44308
|
+
throw new Error("MANCODE_MAN_PLAN_SOURCE_REQUIRED");
|
|
44309
|
+
}
|
|
44310
|
+
} else if (input.planSource)
|
|
44311
|
+
throw new Error("MANCODE_MAN_DELIVERY_MODE_REQUIRED");
|
|
42780
44312
|
const planChanged = context.task.plan?.content !== plan;
|
|
42781
44313
|
const executionScopeBinding = assertExecutionScopeBindingAttempt({
|
|
42782
44314
|
metadata: context.task.metadata,
|
|
@@ -42795,6 +44327,17 @@ async function reviseV3Plan(input) {
|
|
|
42795
44327
|
const scopeChanged = submittedScope !== null && submittedScope.digest !== context.task.metadata.implementationScope.digest;
|
|
42796
44328
|
if (planDecision === "governed_execution") {
|
|
42797
44329
|
assertExecutableImplementationScope(implementationScope);
|
|
44330
|
+
if (isManDelivery(context.task.metadata)) {
|
|
44331
|
+
const source = parseManDeliveryPlan(plan)?.source;
|
|
44332
|
+
if (!source)
|
|
44333
|
+
throw new Error(
|
|
44334
|
+
"MANCODE_MAN_PLAN_OUTSIDE_SCOPE: bound plan source path is missing; provide a repo-relative plan path"
|
|
44335
|
+
);
|
|
44336
|
+
assertManPlanInScope(
|
|
44337
|
+
{ ...context.task.metadata, implementationScope },
|
|
44338
|
+
source.path
|
|
44339
|
+
);
|
|
44340
|
+
}
|
|
42798
44341
|
}
|
|
42799
44342
|
if (executionScopeBinding) {
|
|
42800
44343
|
assertExecutableImplementationScope(implementationScope);
|
|
@@ -43110,6 +44653,10 @@ async function reframeV3Workflow(input) {
|
|
|
43110
44653
|
try {
|
|
43111
44654
|
assertReframeEligible(context, activeClaims2, openHandoffs, activeChildren);
|
|
43112
44655
|
assertReframeClaimsFresh(context, activeClaims2);
|
|
44656
|
+
await assertTaskCheckpointIdAvailableAtRoot(
|
|
44657
|
+
context.task.location.taskRoot,
|
|
44658
|
+
input.checkpointId
|
|
44659
|
+
);
|
|
43113
44660
|
const timestamp4 = context.now.toISOString();
|
|
43114
44661
|
const archivedRequirementsContent = await readTaskAuthorityFileAtRoot(
|
|
43115
44662
|
context.task.location.taskRoot,
|
|
@@ -43681,6 +45228,9 @@ async function writeV3Requirements(input, action) {
|
|
|
43681
45228
|
let journal = null;
|
|
43682
45229
|
try {
|
|
43683
45230
|
assertRequirementsFinalizeEligible(context.task.metadata);
|
|
45231
|
+
if (action === "finalize" && context.task.metadata.workflowMode === "man" && context.task.metadata.governance.policyVersions.planning === 3 && context.task.metadata.governance.planDecision !== "solo_handoff") {
|
|
45232
|
+
assertManDeliveryVerificationSurfaces(submitted);
|
|
45233
|
+
}
|
|
43684
45234
|
const timestamp4 = context.now.toISOString();
|
|
43685
45235
|
const requirements = createReplacementRequirements(
|
|
43686
45236
|
context.task.requirements,
|
|
@@ -43901,6 +45451,12 @@ async function applyV3ReviewLedger(input) {
|
|
|
43901
45451
|
});
|
|
43902
45452
|
let journal = null;
|
|
43903
45453
|
try {
|
|
45454
|
+
const subject = isManDelivery(context.task.metadata) ? await captureManSubject(input.projectRoot, context.task) : null;
|
|
45455
|
+
assertManReviewCoverage(
|
|
45456
|
+
context.task,
|
|
45457
|
+
submitted,
|
|
45458
|
+
subject ?? { contentDigest: "", environment: "" }
|
|
45459
|
+
);
|
|
43904
45460
|
assertReviewEligible(context.task.metadata, context.task.plan !== null);
|
|
43905
45461
|
const timestamp4 = context.now.toISOString();
|
|
43906
45462
|
const review = createCurrentReview(
|
|
@@ -43910,11 +45466,34 @@ async function applyV3ReviewLedger(input) {
|
|
|
43910
45466
|
context.operationId,
|
|
43911
45467
|
timestamp4
|
|
43912
45468
|
);
|
|
43913
|
-
|
|
45469
|
+
let verification = markTaskVerificationStale(
|
|
43914
45470
|
context.task.verification,
|
|
43915
45471
|
context.operationId,
|
|
43916
45472
|
timestamp4
|
|
43917
45473
|
);
|
|
45474
|
+
const previous = context.task.verification;
|
|
45475
|
+
if (subject && previous.requirementsDigest === context.task.metadata.governance.requirementsDigest && previous.planVersion === context.task.metadata.governance.planVersion && previous.checks.every(
|
|
45476
|
+
(check) => [check.automated, check.manual].every(
|
|
45477
|
+
(item) => item?.status !== "passed" || item.subject?.contentDigest === subject.contentDigest && item.subject.environment === subject.environment
|
|
45478
|
+
)
|
|
45479
|
+
)) {
|
|
45480
|
+
const current = {
|
|
45481
|
+
...previous,
|
|
45482
|
+
revision: previous.revision + 1,
|
|
45483
|
+
remediationRound: review.remediationRound,
|
|
45484
|
+
lastOperationId: context.operationId,
|
|
45485
|
+
updatedAt: timestamp4
|
|
45486
|
+
};
|
|
45487
|
+
const draft = {
|
|
45488
|
+
...current,
|
|
45489
|
+
status: deriveVerificationLedgerStatus(current)
|
|
45490
|
+
};
|
|
45491
|
+
verification = parseVerificationLedger({
|
|
45492
|
+
...draft,
|
|
45493
|
+
contentDigest: verificationLedgerDigest(draft)
|
|
45494
|
+
});
|
|
45495
|
+
assertVerificationLedgerTransition(previous, verification);
|
|
45496
|
+
}
|
|
43918
45497
|
const metadata = updateMetadata3(
|
|
43919
45498
|
context.task.metadata,
|
|
43920
45499
|
review,
|
|
@@ -44495,6 +46074,7 @@ async function completeV3Task(input) {
|
|
|
44495
46074
|
throw new Error("MANCODE_GIT_REF_TRANSPORT_NOT_IMPLEMENTED");
|
|
44496
46075
|
}
|
|
44497
46076
|
const activeChildren = await context.store.listActiveChildTaskRefs(taskRef);
|
|
46077
|
+
await assertManDeliveryReady(input.projectRoot, context.task);
|
|
44498
46078
|
assertTaskCompletionGate(
|
|
44499
46079
|
{
|
|
44500
46080
|
metadata: context.task.metadata,
|
|
@@ -44816,6 +46396,12 @@ async function recordV3Verification(input) {
|
|
|
44816
46396
|
});
|
|
44817
46397
|
let journal = null;
|
|
44818
46398
|
try {
|
|
46399
|
+
const subject = isManDelivery(context.task.metadata) ? await captureManSubject(input.projectRoot, context.task) : null;
|
|
46400
|
+
assertManVerificationSubjects(
|
|
46401
|
+
context.task,
|
|
46402
|
+
submitted,
|
|
46403
|
+
subject ?? { contentDigest: "", environment: "" }
|
|
46404
|
+
);
|
|
44819
46405
|
assertVerificationEligible(
|
|
44820
46406
|
context.task.metadata,
|
|
44821
46407
|
context.task.plan !== null
|
|
@@ -44828,7 +46414,12 @@ async function recordV3Verification(input) {
|
|
|
44828
46414
|
context.task.review.remediationRound,
|
|
44829
46415
|
context.task.requirements,
|
|
44830
46416
|
context.operationId,
|
|
44831
|
-
timestamp4
|
|
46417
|
+
timestamp4,
|
|
46418
|
+
subject !== null && context.task.verification.checks.some(
|
|
46419
|
+
(check) => [check.automated, check.manual].some(
|
|
46420
|
+
(item) => item?.status === "passed" && (item.subject?.contentDigest !== subject.contentDigest || item.subject.environment !== subject.environment)
|
|
46421
|
+
)
|
|
46422
|
+
)
|
|
44832
46423
|
);
|
|
44833
46424
|
const metadata = updateMetadata4(
|
|
44834
46425
|
context.task.metadata,
|
|
@@ -44938,7 +46529,7 @@ function assertVerificationEligible(metadata, hasPlan) {
|
|
|
44938
46529
|
throw new Error("MANCODE_VERIFICATION_PLAN_GATE_REQUIRED");
|
|
44939
46530
|
}
|
|
44940
46531
|
}
|
|
44941
|
-
function createCurrentVerification(previous, submitted, metadata, remediationRound, requirements, operationId, updatedAt) {
|
|
46532
|
+
function createCurrentVerification(previous, submitted, metadata, remediationRound, requirements, operationId, updatedAt, contentInvalidated) {
|
|
44942
46533
|
if (submitted.requirementsDigest !== metadata.governance.requirementsDigest || submitted.planVersion !== metadata.governance.planVersion || submitted.remediationRound !== remediationRound) {
|
|
44943
46534
|
throw new Error("MANCODE_VERIFICATION_CONTEXT_STALE");
|
|
44944
46535
|
}
|
|
@@ -44957,7 +46548,7 @@ function createCurrentVerification(previous, submitted, metadata, remediationRou
|
|
|
44957
46548
|
},
|
|
44958
46549
|
requirements
|
|
44959
46550
|
);
|
|
44960
|
-
assertVerificationLedgerTransition(previous, next);
|
|
46551
|
+
assertVerificationLedgerTransition(previous, next, contentInvalidated);
|
|
44961
46552
|
assertVerificationLedgerRequirements(next, requirements);
|
|
44962
46553
|
assertVerificationLedgerAgainstContext(next, {
|
|
44963
46554
|
requirementsDigest: metadata.governance.requirementsDigest,
|
|
@@ -44985,14 +46576,14 @@ function updateMetadata4(previous, verification, operationId, updatedAt) {
|
|
|
44985
46576
|
|
|
44986
46577
|
// src/context/workflow-create.ts
|
|
44987
46578
|
import {
|
|
44988
|
-
lstat as
|
|
46579
|
+
lstat as lstat29,
|
|
44989
46580
|
mkdir as mkdir41,
|
|
44990
|
-
readFile as
|
|
46581
|
+
readFile as readFile49,
|
|
44991
46582
|
rename as rename15,
|
|
44992
|
-
rm as
|
|
44993
|
-
writeFile as
|
|
46583
|
+
rm as rm25,
|
|
46584
|
+
writeFile as writeFile52
|
|
44994
46585
|
} from "fs/promises";
|
|
44995
|
-
import
|
|
46586
|
+
import path72 from "path";
|
|
44996
46587
|
|
|
44997
46588
|
// src/context/creation-resolution.ts
|
|
44998
46589
|
function resolveWorkflowCreation(request) {
|
|
@@ -45102,13 +46693,16 @@ function rejectConflict(provided, expected, label) {
|
|
|
45102
46693
|
|
|
45103
46694
|
// src/context/workflow-create.ts
|
|
45104
46695
|
async function createV3Workflow(input) {
|
|
45105
|
-
const projectRoot =
|
|
46696
|
+
const projectRoot = path72.resolve(requireProjectRoot2(input.projectRoot));
|
|
45106
46697
|
const task = requireText2(input.task, "workflow task");
|
|
45107
46698
|
const client = requireText2(input.client, "workflow client");
|
|
45108
46699
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
45109
46700
|
const timestamp4 = now.toISOString();
|
|
45110
46701
|
assertUlid(input.sessionId, "workflow sessionId");
|
|
45111
46702
|
const workflowMode = parseWorkflowMode(input.workflowMode);
|
|
46703
|
+
if (input.delivery && (workflowMode !== "man" || input.parentTaskRef)) {
|
|
46704
|
+
throw new Error("MANCODE_MAN_DELIVERY_MODE_REQUIRED");
|
|
46705
|
+
}
|
|
45112
46706
|
const session = await readSession(projectRoot, input.sessionId);
|
|
45113
46707
|
if (session === null || session.status !== "active") {
|
|
45114
46708
|
throw new Error("MANCODE_SESSION_NOT_FOUND");
|
|
@@ -45189,7 +46783,7 @@ async function createV3Workflow(input) {
|
|
|
45189
46783
|
explicitScope: input.implementationScope,
|
|
45190
46784
|
ownershipEpoch: taskRef.namespace === "shared" && project.config.transport.mode === "git-ref" ? 0 : 1,
|
|
45191
46785
|
timestamp: timestamp4,
|
|
45192
|
-
planningPolicyVersion: parent === null ? project.manifest.manifestVersion === 2 && workflowMode === "man" ? project.manifest.workflowPolicyDefaults.planning : 1 : parent.metadata.governance.policyVersions.planning
|
|
46786
|
+
planningPolicyVersion: input.delivery ? 3 : parent === null ? project.manifest.manifestVersion === 2 && workflowMode === "man" ? project.manifest.workflowPolicyDefaults.planning : 1 : parent.metadata.governance.policyVersions.planning === 3 ? 2 : parent.metadata.governance.policyVersions.planning
|
|
45193
46787
|
});
|
|
45194
46788
|
const homeStore = resolveTaskEntityHomeStore(
|
|
45195
46789
|
runtime.entityHomeStoreContext,
|
|
@@ -45287,8 +46881,8 @@ async function createV3Workflow(input) {
|
|
|
45287
46881
|
throw new Error("MANCODE_REVISION_CONFLICT");
|
|
45288
46882
|
}
|
|
45289
46883
|
const taskParent = await ensureSafeTaskParent3(projectRoot, taskRef);
|
|
45290
|
-
const targetDirectory =
|
|
45291
|
-
stagingDirectory =
|
|
46884
|
+
const targetDirectory = path72.join(taskParent, taskRef.taskId);
|
|
46885
|
+
stagingDirectory = path72.join(
|
|
45292
46886
|
taskParent,
|
|
45293
46887
|
`.${taskRef.taskId}.${operationId}.staging`
|
|
45294
46888
|
);
|
|
@@ -45746,7 +47340,7 @@ async function ensureSafeTaskParent3(projectRoot, taskRef) {
|
|
|
45746
47340
|
const segments = [".mancode", taskRef.namespace, "workflows"];
|
|
45747
47341
|
let current = projectRoot;
|
|
45748
47342
|
for (const segment of segments) {
|
|
45749
|
-
current =
|
|
47343
|
+
current = path72.join(current, segment);
|
|
45750
47344
|
const existing = await lstatOrNull5(current);
|
|
45751
47345
|
if (existing === null) {
|
|
45752
47346
|
await mkdir41(current);
|
|
@@ -45782,13 +47376,13 @@ async function writeStagedEntities(stagingDirectory, entities) {
|
|
|
45782
47376
|
}
|
|
45783
47377
|
async function writeStagedJson(stagingDirectory, fileName, value) {
|
|
45784
47378
|
await assertDirectory(stagingDirectory);
|
|
45785
|
-
const target =
|
|
45786
|
-
await
|
|
47379
|
+
const target = path72.join(stagingDirectory, fileName);
|
|
47380
|
+
await writeFile52(target, `${JSON.stringify(value, null, 2)}
|
|
45787
47381
|
`, {
|
|
45788
47382
|
encoding: "utf8",
|
|
45789
47383
|
flag: "wx"
|
|
45790
47384
|
});
|
|
45791
|
-
const entry = await
|
|
47385
|
+
const entry = await lstat29(target);
|
|
45792
47386
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
45793
47387
|
throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
|
|
45794
47388
|
}
|
|
@@ -45820,13 +47414,13 @@ async function validateStagedEntities(stagingDirectory) {
|
|
|
45820
47414
|
});
|
|
45821
47415
|
}
|
|
45822
47416
|
async function readStagedJson(stagingDirectory, fileName, parser) {
|
|
45823
|
-
const target =
|
|
45824
|
-
const before = await
|
|
47417
|
+
const target = path72.join(stagingDirectory, fileName);
|
|
47418
|
+
const before = await lstat29(target);
|
|
45825
47419
|
if (!before.isFile() || before.isSymbolicLink()) {
|
|
45826
47420
|
throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
|
|
45827
47421
|
}
|
|
45828
|
-
const parsed = parser(JSON.parse(await
|
|
45829
|
-
const after = await
|
|
47422
|
+
const parsed = parser(JSON.parse(await readFile49(target, "utf8")));
|
|
47423
|
+
const after = await lstat29(target);
|
|
45830
47424
|
if (!after.isFile() || after.isSymbolicLink() || before.dev !== after.dev || before.ino !== after.ino) {
|
|
45831
47425
|
throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
|
|
45832
47426
|
}
|
|
@@ -45917,18 +47511,18 @@ async function abortPreparedCreate(store, journal, stagingDirectory, now) {
|
|
|
45917
47511
|
{ canAbort: true }
|
|
45918
47512
|
);
|
|
45919
47513
|
} finally {
|
|
45920
|
-
await
|
|
47514
|
+
await rm25(stagingDirectory, { recursive: true, force: true });
|
|
45921
47515
|
}
|
|
45922
47516
|
}
|
|
45923
47517
|
async function assertDirectory(target) {
|
|
45924
|
-
const entry = await
|
|
47518
|
+
const entry = await lstat29(target);
|
|
45925
47519
|
if (!entry.isDirectory() || entry.isSymbolicLink()) {
|
|
45926
47520
|
throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
|
|
45927
47521
|
}
|
|
45928
47522
|
}
|
|
45929
47523
|
async function lstatOrNull5(target) {
|
|
45930
47524
|
try {
|
|
45931
|
-
return await
|
|
47525
|
+
return await lstat29(target);
|
|
45932
47526
|
} catch (error) {
|
|
45933
47527
|
if (isNotFound34(error)) return null;
|
|
45934
47528
|
throw error;
|
|
@@ -46261,6 +47855,7 @@ async function completeGitRefTask(input) {
|
|
|
46261
47855
|
try {
|
|
46262
47856
|
assertNoPendingHandoff(opened.manifest, taskRef);
|
|
46263
47857
|
assertCompletionOutcome2(opened.context.task.metadata, input.outcome);
|
|
47858
|
+
await assertManDeliveryReady(input.projectRoot, opened.context.task);
|
|
46264
47859
|
const activeClaims2 = activeRemoteClaims(opened.manifest, taskRef);
|
|
46265
47860
|
const activeChildren = await opened.context.store.listActiveChildTaskRefs(taskRef);
|
|
46266
47861
|
assertTaskCompletionGate(
|
|
@@ -46870,6 +48465,360 @@ function compareUtf817(left, right) {
|
|
|
46870
48465
|
return Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"));
|
|
46871
48466
|
}
|
|
46872
48467
|
|
|
48468
|
+
// src/commands/man-delivery.ts
|
|
48469
|
+
import { execFile as execFileCallback8 } from "child_process";
|
|
48470
|
+
import { readFile as readFile50 } from "fs/promises";
|
|
48471
|
+
import path73 from "path";
|
|
48472
|
+
import { promisify as promisify10 } from "util";
|
|
48473
|
+
var execFile11 = promisify10(execFileCallback8);
|
|
48474
|
+
async function manDeliveryCommand(root, args, options) {
|
|
48475
|
+
try {
|
|
48476
|
+
const [ref, action] = args;
|
|
48477
|
+
if (!ref || args.length !== 2 || ![
|
|
48478
|
+
"inspect",
|
|
48479
|
+
"check",
|
|
48480
|
+
"publication",
|
|
48481
|
+
"sync",
|
|
48482
|
+
"verify",
|
|
48483
|
+
"confirm",
|
|
48484
|
+
"review"
|
|
48485
|
+
].includes(action ?? "")) {
|
|
48486
|
+
throw new Error(
|
|
48487
|
+
"MANCODE_MAN_DELIVERY_ARGUMENT_INVALID: delivery <TaskRef> <inspect|check|publication|sync|verify|confirm|review>"
|
|
48488
|
+
);
|
|
48489
|
+
}
|
|
48490
|
+
const project = await readV3CommandProject(root);
|
|
48491
|
+
const taskRef = parseTaskRef(ref);
|
|
48492
|
+
let task = await project.store.readTaskSnapshot(taskRef);
|
|
48493
|
+
if (!isManDelivery(task.metadata))
|
|
48494
|
+
throw new Error("MANCODE_MAN_DELIVERY_MODE_REQUIRED");
|
|
48495
|
+
if (action === "publication")
|
|
48496
|
+
return printV3Result(
|
|
48497
|
+
options.json,
|
|
48498
|
+
await inspectManPublication(project.projectRoot)
|
|
48499
|
+
);
|
|
48500
|
+
if (action === "inspect")
|
|
48501
|
+
return printV3Result(options.json, {
|
|
48502
|
+
...await inspectManDelivery(project.projectRoot, task),
|
|
48503
|
+
acceptanceCriteria: task.requirements.acceptanceCriteria,
|
|
48504
|
+
review: task.review,
|
|
48505
|
+
verification: task.verification
|
|
48506
|
+
});
|
|
48507
|
+
if (action === "check") {
|
|
48508
|
+
await assertManDeliveryReady(project.projectRoot, task);
|
|
48509
|
+
assertTaskCompletionGate(
|
|
48510
|
+
{ ...task, planDigest: task.plan?.digest ?? null },
|
|
48511
|
+
{
|
|
48512
|
+
activeChildTaskRefs: await project.store.listActiveChildTaskRefs(taskRef),
|
|
48513
|
+
hasPendingRepairOperation: false,
|
|
48514
|
+
activeClaimCount: 0
|
|
48515
|
+
}
|
|
48516
|
+
);
|
|
48517
|
+
return printV3Result(options.json, {
|
|
48518
|
+
delivery: "ready_for_complete",
|
|
48519
|
+
publication: "not_checked",
|
|
48520
|
+
note: "complete rechecks operation authority; upstream publication requires an actual push result"
|
|
48521
|
+
});
|
|
48522
|
+
}
|
|
48523
|
+
const expectedTaskRevision = Number(options.expectedRevision);
|
|
48524
|
+
if (!Number.isSafeInteger(expectedTaskRevision) || expectedTaskRevision < 1)
|
|
48525
|
+
throw new Error("MANCODE_EXPECTED_REVISION_REQUIRED");
|
|
48526
|
+
if (options.sync) throw new Error("MANCODE_GIT_REF_DEFERRED_SYNC_REQUIRED");
|
|
48527
|
+
const session = await resolveV3CommandSession(project, options);
|
|
48528
|
+
const context = await openV3TaskOperation({
|
|
48529
|
+
projectRoot: project.projectRoot,
|
|
48530
|
+
taskRef,
|
|
48531
|
+
sessionId: session.sessionId,
|
|
48532
|
+
expectedTaskRevision
|
|
48533
|
+
});
|
|
48534
|
+
const actorId = context.session.actorId;
|
|
48535
|
+
try {
|
|
48536
|
+
task = context.task;
|
|
48537
|
+
if (action === "sync") {
|
|
48538
|
+
const progress = await syncManDeliveryRecord(project.projectRoot, task);
|
|
48539
|
+
const synced = await project.store.readTaskSnapshot(taskRef);
|
|
48540
|
+
const finalization2 = await inspectFinalization(
|
|
48541
|
+
project.projectRoot,
|
|
48542
|
+
synced
|
|
48543
|
+
);
|
|
48544
|
+
return printV3Result(options.json, {
|
|
48545
|
+
deliveryRecord: "synced",
|
|
48546
|
+
progress,
|
|
48547
|
+
revision: synced.metadata.revision,
|
|
48548
|
+
finalization: finalization2
|
|
48549
|
+
});
|
|
48550
|
+
}
|
|
48551
|
+
if (task.metadata.governance.planDecision !== "governed_execution" || task.metadata.currentStep < 5 || task.metadata.status !== "in_progress") {
|
|
48552
|
+
throw new Error("MANCODE_MAN_DELIVERY_EXECUTION_REQUIRED");
|
|
48553
|
+
}
|
|
48554
|
+
} finally {
|
|
48555
|
+
await context.release();
|
|
48556
|
+
}
|
|
48557
|
+
if (!options.file) throw new Error("MANCODE_MAN_DELIVERY_INPUT_REQUIRED");
|
|
48558
|
+
const input = JSON.parse(
|
|
48559
|
+
await readFile50(path73.resolve(project.projectRoot, options.file), "utf8")
|
|
48560
|
+
);
|
|
48561
|
+
let output;
|
|
48562
|
+
if (action === "verify" || action === "confirm") {
|
|
48563
|
+
assertRecord(input, "verification command");
|
|
48564
|
+
assertKnownKeys(
|
|
48565
|
+
input,
|
|
48566
|
+
action === "verify" ? ["argv", "surface"] : ["confirmed", "summary", "surface"],
|
|
48567
|
+
"verification command"
|
|
48568
|
+
);
|
|
48569
|
+
if (action === "verify" && (!Array.isArray(input.argv) || !input.argv.length || input.argv.some(
|
|
48570
|
+
(arg) => typeof arg !== "string" || arg.includes("\0")
|
|
48571
|
+
) || !input.argv[0]))
|
|
48572
|
+
throw new Error("MANCODE_MAN_VERIFY_ARGV_INVALID");
|
|
48573
|
+
if (action === "confirm" && (input.confirmed !== true || typeof input.summary !== "string" || !input.summary.trim()))
|
|
48574
|
+
throw new Error("MANCODE_MAN_EXPLICIT_CONFIRMATION_REQUIRED");
|
|
48575
|
+
const surface = parseManVerificationSurface(input.surface);
|
|
48576
|
+
const checks = verificationChecks(task);
|
|
48577
|
+
const acceptanceIds = options.acceptance?.split(",").map((id) => id.trim()) ?? [];
|
|
48578
|
+
const component = action === "verify" ? "automated" : "manual";
|
|
48579
|
+
if (!acceptanceIds.length || new Set(acceptanceIds).size !== acceptanceIds.length || acceptanceIds.some(
|
|
48580
|
+
(id) => !checks.some(
|
|
48581
|
+
(check) => check.displayId === id && check[component] !== null
|
|
48582
|
+
)
|
|
48583
|
+
))
|
|
48584
|
+
throw new Error("MANCODE_MAN_ACCEPTANCE_SLOT_REQUIRED");
|
|
48585
|
+
const subject = await captureManSubject(project.projectRoot, task);
|
|
48586
|
+
let result2 = {
|
|
48587
|
+
stdout: "",
|
|
48588
|
+
stderr: "",
|
|
48589
|
+
exitCode: 0
|
|
48590
|
+
};
|
|
48591
|
+
if (action === "verify") {
|
|
48592
|
+
const argv = input.argv;
|
|
48593
|
+
try {
|
|
48594
|
+
const run = await execFile11(argv[0], argv.slice(1), {
|
|
48595
|
+
cwd: project.projectRoot,
|
|
48596
|
+
encoding: "utf8",
|
|
48597
|
+
maxBuffer: 8 * 1024 * 1024
|
|
48598
|
+
});
|
|
48599
|
+
result2 = { ...run, exitCode: 0 };
|
|
48600
|
+
} catch (error) {
|
|
48601
|
+
const failed = error;
|
|
48602
|
+
if (typeof failed.code !== "number") throw error;
|
|
48603
|
+
result2 = {
|
|
48604
|
+
exitCode: failed.code,
|
|
48605
|
+
stdout: failed.stdout ?? "",
|
|
48606
|
+
stderr: failed.stderr ?? ""
|
|
48607
|
+
};
|
|
48608
|
+
}
|
|
48609
|
+
}
|
|
48610
|
+
if ((await captureManSubject(project.projectRoot, task)).contentDigest !== subject.contentDigest)
|
|
48611
|
+
throw new Error("MANCODE_MAN_VERIFICATION_CHANGED_DURING_RUN");
|
|
48612
|
+
const timestamp4 = (/* @__PURE__ */ new Date()).toISOString();
|
|
48613
|
+
const invalidate = (evidence) => {
|
|
48614
|
+
if (!evidence || evidence.subject?.contentDigest === subject.contentDigest && evidence.subject.environment === subject.environment)
|
|
48615
|
+
return evidence;
|
|
48616
|
+
const {
|
|
48617
|
+
subject: _oldSubject,
|
|
48618
|
+
surface: _oldSurface,
|
|
48619
|
+
...rest
|
|
48620
|
+
} = evidence;
|
|
48621
|
+
return {
|
|
48622
|
+
...rest,
|
|
48623
|
+
status: "pending",
|
|
48624
|
+
summary: null,
|
|
48625
|
+
command: null,
|
|
48626
|
+
exitCode: null,
|
|
48627
|
+
artifactRef: null,
|
|
48628
|
+
confirmedByActorId: null,
|
|
48629
|
+
confirmationSource: null,
|
|
48630
|
+
updatedAt: timestamp4
|
|
48631
|
+
};
|
|
48632
|
+
};
|
|
48633
|
+
const draft = {
|
|
48634
|
+
...task.verification,
|
|
48635
|
+
requirementsDigest: task.requirements.contentDigest,
|
|
48636
|
+
planVersion: task.metadata.governance.planVersion,
|
|
48637
|
+
remediationRound: task.review.remediationRound,
|
|
48638
|
+
checks: checks.map((item) => {
|
|
48639
|
+
const next = {
|
|
48640
|
+
...item,
|
|
48641
|
+
automated: invalidate(item.automated),
|
|
48642
|
+
manual: invalidate(item.manual)
|
|
48643
|
+
};
|
|
48644
|
+
const slot = item[component];
|
|
48645
|
+
if (acceptanceIds.includes(item.displayId) && slot)
|
|
48646
|
+
next[component] = {
|
|
48647
|
+
...slot,
|
|
48648
|
+
subject,
|
|
48649
|
+
surface,
|
|
48650
|
+
status: result2.exitCode === 0 ? "passed" : "failed",
|
|
48651
|
+
command: action === "verify" ? JSON.stringify(input.argv) : null,
|
|
48652
|
+
exitCode: action === "verify" ? result2.exitCode : null,
|
|
48653
|
+
summary: action === "verify" ? `Executed argv in project root; captured exit code ${result2.exitCode}.` : input.summary,
|
|
48654
|
+
confirmedByActorId: action === "confirm" ? actorId : null,
|
|
48655
|
+
confirmationSource: action === "confirm" ? "actor" : null,
|
|
48656
|
+
updatedAt: timestamp4
|
|
48657
|
+
};
|
|
48658
|
+
return next;
|
|
48659
|
+
})
|
|
48660
|
+
};
|
|
48661
|
+
const current = {
|
|
48662
|
+
...draft,
|
|
48663
|
+
status: deriveVerificationLedgerStatus(draft)
|
|
48664
|
+
};
|
|
48665
|
+
output = {
|
|
48666
|
+
...await recordV3Verification({
|
|
48667
|
+
projectRoot: project.projectRoot,
|
|
48668
|
+
taskRef,
|
|
48669
|
+
sessionId: session.sessionId,
|
|
48670
|
+
expectedTaskRevision,
|
|
48671
|
+
verification: {
|
|
48672
|
+
...current,
|
|
48673
|
+
contentDigest: verificationLedgerDigest(current)
|
|
48674
|
+
}
|
|
48675
|
+
}),
|
|
48676
|
+
...action === "verify" ? { commandResult: result2 } : { manualConfirmation: { actorId, summary: input.summary } }
|
|
48677
|
+
};
|
|
48678
|
+
} else {
|
|
48679
|
+
output = await applyV3ReviewLedger({
|
|
48680
|
+
projectRoot: project.projectRoot,
|
|
48681
|
+
taskRef,
|
|
48682
|
+
sessionId: session.sessionId,
|
|
48683
|
+
expectedTaskRevision,
|
|
48684
|
+
review: reviewInput(task, input, options.reviewDepth)
|
|
48685
|
+
});
|
|
48686
|
+
}
|
|
48687
|
+
const updated = await project.store.readTaskSnapshot(taskRef);
|
|
48688
|
+
let deliveryRecord = { status: "synced" };
|
|
48689
|
+
try {
|
|
48690
|
+
deliveryRecord.progress = await syncManDeliveryRecord(
|
|
48691
|
+
project.projectRoot,
|
|
48692
|
+
updated
|
|
48693
|
+
);
|
|
48694
|
+
} catch (error) {
|
|
48695
|
+
deliveryRecord = {
|
|
48696
|
+
status: "pending",
|
|
48697
|
+
error: error instanceof Error ? error.message : String(error)
|
|
48698
|
+
};
|
|
48699
|
+
}
|
|
48700
|
+
const finalization = await inspectFinalization(
|
|
48701
|
+
project.projectRoot,
|
|
48702
|
+
updated
|
|
48703
|
+
);
|
|
48704
|
+
return printV3Result(options.json, {
|
|
48705
|
+
result: output,
|
|
48706
|
+
deliveryRecord,
|
|
48707
|
+
revision: updated.metadata.revision,
|
|
48708
|
+
finalization
|
|
48709
|
+
});
|
|
48710
|
+
} catch (error) {
|
|
48711
|
+
return printV3Error(
|
|
48712
|
+
options.json,
|
|
48713
|
+
v3ErrorCode(error, "MANCODE_MAN_DELIVERY_FAILED"),
|
|
48714
|
+
error instanceof Error ? error.message : String(error)
|
|
48715
|
+
);
|
|
48716
|
+
}
|
|
48717
|
+
}
|
|
48718
|
+
async function inspectFinalization(root, task) {
|
|
48719
|
+
try {
|
|
48720
|
+
return (await inspectManDelivery(root, task)).finalization;
|
|
48721
|
+
} catch (error) {
|
|
48722
|
+
return manInspectionFailureFinalization(error);
|
|
48723
|
+
}
|
|
48724
|
+
}
|
|
48725
|
+
function verificationChecks(task) {
|
|
48726
|
+
const emptySlot = () => ({
|
|
48727
|
+
evidenceId: createUlid(),
|
|
48728
|
+
status: "pending",
|
|
48729
|
+
summary: null,
|
|
48730
|
+
command: null,
|
|
48731
|
+
exitCode: null,
|
|
48732
|
+
artifactRef: null,
|
|
48733
|
+
confirmedByActorId: null,
|
|
48734
|
+
confirmationSource: null,
|
|
48735
|
+
updatedAt: null
|
|
48736
|
+
});
|
|
48737
|
+
return task.requirements.acceptanceCriteria.map((criterion) => {
|
|
48738
|
+
const previous = task.verification.checks.find(
|
|
48739
|
+
(check) => check.criterionId === criterion.criterionId && check.verificationRequirement === criterion.verificationRequirement
|
|
48740
|
+
);
|
|
48741
|
+
return previous ?? {
|
|
48742
|
+
displayId: criterion.displayId,
|
|
48743
|
+
legacyId: criterion.legacyId,
|
|
48744
|
+
checkId: createUlid(),
|
|
48745
|
+
criterionId: criterion.criterionId,
|
|
48746
|
+
required: criterion.required,
|
|
48747
|
+
verificationRequirement: criterion.verificationRequirement,
|
|
48748
|
+
automated: criterion.verificationRequirement === "manual" ? null : emptySlot(),
|
|
48749
|
+
manual: criterion.verificationRequirement === "automated" ? null : emptySlot()
|
|
48750
|
+
};
|
|
48751
|
+
});
|
|
48752
|
+
}
|
|
48753
|
+
function reviewInput(task, input, depth) {
|
|
48754
|
+
assertRecord(input, "module review");
|
|
48755
|
+
const { findings = [], resolved = [], ...report } = input;
|
|
48756
|
+
const delivery = parseManReviewEvidence(report);
|
|
48757
|
+
if (!Array.isArray(findings) || !Array.isArray(resolved) || resolved.some((id) => typeof id !== "string"))
|
|
48758
|
+
throw new Error("MANCODE_MAN_REVIEW_FINDINGS_INVALID");
|
|
48759
|
+
if (resolved.some(
|
|
48760
|
+
(id) => !task.review.blockers.some((blocker) => blocker.displayId === id)
|
|
48761
|
+
))
|
|
48762
|
+
throw new Error("MANCODE_MAN_REVIEW_UNKNOWN_FINDING");
|
|
48763
|
+
const blockers = task.review.blockers.map(
|
|
48764
|
+
(blocker) => resolved.includes(blocker.displayId) ? { ...blocker, status: "resolved" } : blocker
|
|
48765
|
+
);
|
|
48766
|
+
const ids = /* @__PURE__ */ new Set();
|
|
48767
|
+
for (const finding of findings) {
|
|
48768
|
+
assertRecord(finding, "module review finding");
|
|
48769
|
+
assertKnownKeys(
|
|
48770
|
+
finding,
|
|
48771
|
+
["id", "domain", "severity", "summary"],
|
|
48772
|
+
"module review finding"
|
|
48773
|
+
);
|
|
48774
|
+
if (typeof finding.id !== "string" || !finding.id.trim() || ids.has(finding.id) || typeof finding.summary !== "string" || !finding.summary.trim() || finding.domain !== "quality" && finding.domain !== "security" || !["p0", "p1", "p2"].includes(String(finding.severity)))
|
|
48775
|
+
throw new Error("MANCODE_MAN_REVIEW_FINDINGS_INVALID");
|
|
48776
|
+
ids.add(finding.id);
|
|
48777
|
+
const previous = blockers.find((item) => item.displayId === finding.id);
|
|
48778
|
+
const entry = {
|
|
48779
|
+
displayId: finding.id,
|
|
48780
|
+
legacyId: null,
|
|
48781
|
+
blockerId: previous?.blockerId ?? createUlid(),
|
|
48782
|
+
domain: finding.domain,
|
|
48783
|
+
severity: finding.severity,
|
|
48784
|
+
status: "open",
|
|
48785
|
+
summary: finding.summary,
|
|
48786
|
+
waiver: null
|
|
48787
|
+
};
|
|
48788
|
+
if (previous) blockers[blockers.indexOf(previous)] = entry;
|
|
48789
|
+
else blockers.push(entry);
|
|
48790
|
+
}
|
|
48791
|
+
if (depth !== void 0 && depth !== "targeted" && depth !== "full")
|
|
48792
|
+
throw new Error("MANCODE_MAN_REVIEW_DEPTH_INVALID");
|
|
48793
|
+
const full = depth === "full" || task.review.depth === "full" || blockers.some((item) => item.domain === "security");
|
|
48794
|
+
const requiredDomains = full ? ["quality", "security"] : ["quality"];
|
|
48795
|
+
const covered = task.requirements.acceptanceCriteria.filter((item) => item.required).every(
|
|
48796
|
+
(criterion) => delivery.coverage.some(
|
|
48797
|
+
(row) => row.acceptanceId === criterion.displayId && row.status === "met"
|
|
48798
|
+
)
|
|
48799
|
+
);
|
|
48800
|
+
const draft = {
|
|
48801
|
+
...task.review,
|
|
48802
|
+
delivery,
|
|
48803
|
+
depth: full ? "full" : "targeted",
|
|
48804
|
+
requiredDomains: [...requiredDomains],
|
|
48805
|
+
requirementsDigest: task.requirements.contentDigest,
|
|
48806
|
+
planVersion: task.metadata.governance.planVersion,
|
|
48807
|
+
domains: requiredDomains.map((domain) => ({
|
|
48808
|
+
domain,
|
|
48809
|
+
status: blockers.some(
|
|
48810
|
+
(item) => item.domain === domain && item.status === "open"
|
|
48811
|
+
) || !covered ? "blocked" : "passed",
|
|
48812
|
+
reportRef: null
|
|
48813
|
+
})),
|
|
48814
|
+
blockers,
|
|
48815
|
+
skip: null,
|
|
48816
|
+
remediationRound: task.review.remediationRound + (resolved.length ? 1 : 0)
|
|
48817
|
+
};
|
|
48818
|
+
const current = { ...draft, status: deriveReviewLedgerStatus(draft) };
|
|
48819
|
+
return { ...current, contentDigest: reviewLedgerDigest(current) };
|
|
48820
|
+
}
|
|
48821
|
+
|
|
46873
48822
|
// src/commands/requirements-input.ts
|
|
46874
48823
|
function normalizeRequirementsInput(value, requestedTaskRef, now = /* @__PURE__ */ new Date(), options = {}) {
|
|
46875
48824
|
if (isRecord14(value) && "schemaVersion" in value) {
|
|
@@ -46921,7 +48870,8 @@ function buildCanonicalRequirements(semantic, requestedTaskRef, now) {
|
|
|
46921
48870
|
requirementIds: [],
|
|
46922
48871
|
statement: item.description,
|
|
46923
48872
|
required: item.required,
|
|
46924
|
-
verificationRequirement: item.method
|
|
48873
|
+
verificationRequirement: item.method,
|
|
48874
|
+
...item.verificationSurfaces === void 0 ? {} : { verificationSurfaces: item.verificationSurfaces }
|
|
46925
48875
|
})),
|
|
46926
48876
|
blockingUnknowns: semantic.blockingUnknowns.map((statement, index) => ({
|
|
46927
48877
|
displayId: `U-${index + 1}`,
|
|
@@ -46953,7 +48903,14 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
|
46953
48903
|
if (v3Activation === "v3_active") {
|
|
46954
48904
|
return workflowV3(rootDir, subcommand, args, options);
|
|
46955
48905
|
}
|
|
46956
|
-
if (
|
|
48906
|
+
if (options.delivery || subcommand === "delivery") {
|
|
48907
|
+
return printV3Error(
|
|
48908
|
+
options.json,
|
|
48909
|
+
"MANCODE_MAN_DELIVERY_REQUIRES_CONTINUITY",
|
|
48910
|
+
"Man delivery requires Continuity; legacy workflows are unchanged."
|
|
48911
|
+
);
|
|
48912
|
+
}
|
|
48913
|
+
if (!await pathExists16(path74.join(rootDir, ".mancode", "state.json"))) {
|
|
46957
48914
|
if (v3Activation !== null) {
|
|
46958
48915
|
return printV3Error(
|
|
46959
48916
|
options.json,
|
|
@@ -47009,6 +48966,7 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
|
47009
48966
|
}
|
|
47010
48967
|
}
|
|
47011
48968
|
var WORKFLOW_V3_HANDLERS = {
|
|
48969
|
+
delivery: manDeliveryCommand,
|
|
47012
48970
|
create: workflowCreateV3,
|
|
47013
48971
|
list: workflowListV3,
|
|
47014
48972
|
show: workflowShowV3,
|
|
@@ -47913,6 +49871,7 @@ async function workflowPlanV3(rootDir, args, options) {
|
|
|
47913
49871
|
sessionId: session.sessionId,
|
|
47914
49872
|
expectedTaskRevision,
|
|
47915
49873
|
plan,
|
|
49874
|
+
planSource: action === "revise" && isManDelivery((await project.store.readTaskSnapshot(taskRef)).metadata) ? options.file : void 0,
|
|
47916
49875
|
implementationScope,
|
|
47917
49876
|
planDecision: parseV3PlanDecision(options.planDecision)
|
|
47918
49877
|
});
|
|
@@ -48036,8 +49995,8 @@ function parseV3ParticipantActorIds(values) {
|
|
|
48036
49995
|
return values;
|
|
48037
49996
|
}
|
|
48038
49997
|
async function readWorkflowInputFile(projectRoot, value) {
|
|
48039
|
-
const inputPath =
|
|
48040
|
-
return
|
|
49998
|
+
const inputPath = path74.isAbsolute(value) ? value : path74.resolve(projectRoot, value);
|
|
49999
|
+
return readFile51(inputPath, "utf8");
|
|
48041
50000
|
}
|
|
48042
50001
|
async function readWorkflowJsonInputFile(projectRoot, value) {
|
|
48043
50002
|
return JSON.parse(await readWorkflowInputFile(projectRoot, value));
|
|
@@ -48077,6 +50036,7 @@ async function workflowCreateV3(rootDir, args, options) {
|
|
|
48077
50036
|
evaluatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
48078
50037
|
});
|
|
48079
50038
|
const result2 = await createV3Workflow({
|
|
50039
|
+
delivery: options.delivery,
|
|
48080
50040
|
projectRoot: project.projectRoot,
|
|
48081
50041
|
task,
|
|
48082
50042
|
workflowMode: parsedWorkflowMode,
|
|
@@ -48110,7 +50070,7 @@ async function readV3ActivationState2(rootDir) {
|
|
|
48110
50070
|
try {
|
|
48111
50071
|
const manifest = parseSchemaManifest(
|
|
48112
50072
|
JSON.parse(
|
|
48113
|
-
await
|
|
50073
|
+
await readFile51(path74.join(rootDir, ".mancode", "schema.json"), "utf8")
|
|
48114
50074
|
)
|
|
48115
50075
|
);
|
|
48116
50076
|
return manifest.activationState === "v3_active" ? "v3_active" : "other";
|
|
@@ -48155,22 +50115,22 @@ async function workflowRequirements(rootDir, args, options) {
|
|
|
48155
50115
|
"requirements can only be finalized for an in-progress man or manteam workflow at step 1 or 2"
|
|
48156
50116
|
);
|
|
48157
50117
|
}
|
|
48158
|
-
const workflowPath =
|
|
48159
|
-
const jsonPath =
|
|
48160
|
-
const markdownPath =
|
|
48161
|
-
const metadataPath3 =
|
|
50118
|
+
const workflowPath = path74.join(rootDir, ".mancode", "workflows", taskId);
|
|
50119
|
+
const jsonPath = path74.join(workflowPath, "requirements.json");
|
|
50120
|
+
const markdownPath = path74.join(workflowPath, "requirements.md");
|
|
50121
|
+
const metadataPath3 = path74.join(workflowPath, "metadata.json");
|
|
48162
50122
|
let originalJson;
|
|
48163
50123
|
let originalMarkdown;
|
|
48164
50124
|
let originalMetadata;
|
|
48165
50125
|
try {
|
|
48166
|
-
const inputPath =
|
|
48167
|
-
const input = await
|
|
50126
|
+
const inputPath = path74.isAbsolute(options.file) ? options.file : path74.resolve(rootDir, options.file);
|
|
50127
|
+
const input = await readFile51(inputPath, "utf-8");
|
|
48168
50128
|
const requirements = parseRequirementsLedger2(input);
|
|
48169
50129
|
assertRequirementsScopeConsistent2(requirements);
|
|
48170
50130
|
[originalJson, originalMarkdown, originalMetadata] = await Promise.all([
|
|
48171
50131
|
readOptionalText2(jsonPath),
|
|
48172
50132
|
readOptionalText2(markdownPath),
|
|
48173
|
-
|
|
50133
|
+
readFile51(metadataPath3, "utf-8")
|
|
48174
50134
|
]);
|
|
48175
50135
|
await writeRequirementsArtifacts(rootDir, taskId, requirements);
|
|
48176
50136
|
await updateWorkflow(rootDir, taskId, {
|
|
@@ -48200,7 +50160,7 @@ async function workflowRequirements(rootDir, args, options) {
|
|
|
48200
50160
|
const rollback = await Promise.allSettled([
|
|
48201
50161
|
restoreOptionalText(jsonPath, originalJson),
|
|
48202
50162
|
restoreOptionalText(markdownPath, originalMarkdown),
|
|
48203
|
-
|
|
50163
|
+
writeFile53(metadataPath3, originalMetadata, "utf-8")
|
|
48204
50164
|
]);
|
|
48205
50165
|
rollbackIncomplete = rollback.some(
|
|
48206
50166
|
(result2) => result2.status === "rejected"
|
|
@@ -48288,7 +50248,7 @@ async function workflowVerify(rootDir, args, options) {
|
|
|
48288
50248
|
);
|
|
48289
50249
|
}
|
|
48290
50250
|
if (options.evidenceFile) {
|
|
48291
|
-
const evidencePath =
|
|
50251
|
+
const evidencePath = path74.isAbsolute(options.evidenceFile) ? options.evidenceFile : path74.resolve(rootDir, options.evidenceFile);
|
|
48292
50252
|
if (!await pathExists16(evidencePath)) {
|
|
48293
50253
|
return invalidArg(
|
|
48294
50254
|
options,
|
|
@@ -48348,17 +50308,17 @@ async function workflowVerify(rootDir, args, options) {
|
|
|
48348
50308
|
}
|
|
48349
50309
|
async function commitVerificationTransition(rootDir, meta, ledger) {
|
|
48350
50310
|
const ledgerPath = verificationLedgerPath(rootDir, meta.taskId);
|
|
48351
|
-
const metadataPath3 =
|
|
50311
|
+
const metadataPath3 = path74.join(
|
|
48352
50312
|
rootDir,
|
|
48353
50313
|
".mancode",
|
|
48354
50314
|
"workflows",
|
|
48355
50315
|
meta.taskId,
|
|
48356
50316
|
"metadata.json"
|
|
48357
50317
|
);
|
|
48358
|
-
const specPath =
|
|
50318
|
+
const specPath = path74.join(rootDir, ".mancode", "memory", "spec.md");
|
|
48359
50319
|
const [originalLedger, originalMetadata, originalSpec] = await Promise.all([
|
|
48360
50320
|
readOptionalText2(ledgerPath),
|
|
48361
|
-
|
|
50321
|
+
readFile51(metadataPath3, "utf-8"),
|
|
48362
50322
|
readOptionalText2(specPath)
|
|
48363
50323
|
]);
|
|
48364
50324
|
const wasVerificationBlocked = meta.status === "blocked" && meta.blockingReason?.startsWith("[verification]");
|
|
@@ -48387,7 +50347,7 @@ async function commitVerificationTransition(rootDir, meta, ledger) {
|
|
|
48387
50347
|
} catch (error) {
|
|
48388
50348
|
const rollback = await Promise.allSettled([
|
|
48389
50349
|
restoreOptionalText(ledgerPath, originalLedger),
|
|
48390
|
-
|
|
50350
|
+
writeFile53(metadataPath3, originalMetadata, "utf-8"),
|
|
48391
50351
|
restoreOptionalText(specPath, originalSpec)
|
|
48392
50352
|
]);
|
|
48393
50353
|
const message = error instanceof Error ? error.message : "verification transition failed";
|
|
@@ -48519,7 +50479,7 @@ async function workflowReview(rootDir, args, options) {
|
|
|
48519
50479
|
}
|
|
48520
50480
|
async function commitReviewSkip(rootDir, meta, reason) {
|
|
48521
50481
|
const ledgerPath = reviewLedgerPath(rootDir, meta.taskId);
|
|
48522
|
-
const metadataPath3 =
|
|
50482
|
+
const metadataPath3 = path74.join(
|
|
48523
50483
|
rootDir,
|
|
48524
50484
|
".mancode",
|
|
48525
50485
|
"workflows",
|
|
@@ -48528,7 +50488,7 @@ async function commitReviewSkip(rootDir, meta, reason) {
|
|
|
48528
50488
|
);
|
|
48529
50489
|
const [originalLedger, originalMetadata] = await Promise.all([
|
|
48530
50490
|
readOptionalText2(ledgerPath),
|
|
48531
|
-
|
|
50491
|
+
readFile51(metadataPath3, "utf-8")
|
|
48532
50492
|
]);
|
|
48533
50493
|
try {
|
|
48534
50494
|
await initializeSkippedReview(rootDir, meta.taskId, reason);
|
|
@@ -48539,7 +50499,7 @@ async function commitReviewSkip(rootDir, meta, reason) {
|
|
|
48539
50499
|
} catch (error) {
|
|
48540
50500
|
const rollback = await Promise.allSettled([
|
|
48541
50501
|
restoreOptionalText(ledgerPath, originalLedger),
|
|
48542
|
-
|
|
50502
|
+
writeFile53(metadataPath3, originalMetadata, "utf-8")
|
|
48543
50503
|
]);
|
|
48544
50504
|
const message = error instanceof Error ? error.message : "review skip failed";
|
|
48545
50505
|
return rollback.some((result2) => result2.status === "rejected") ? `${message}; rollback was incomplete` : message;
|
|
@@ -48759,18 +50719,18 @@ async function workflowHandoff(rootDir, taskId, options) {
|
|
|
48759
50719
|
"solo handoff requires an undecided in-progress workflow at step 4 with ready requirements"
|
|
48760
50720
|
);
|
|
48761
50721
|
}
|
|
48762
|
-
const workflowPath =
|
|
48763
|
-
if (!await pathExists16(
|
|
50722
|
+
const workflowPath = path74.join(rootDir, ".mancode", "workflows", taskId);
|
|
50723
|
+
if (!await pathExists16(path74.join(workflowPath, "requirements.md")) || !await pathExists16(path74.join(workflowPath, "plan.md"))) {
|
|
48764
50724
|
return invalidArg(
|
|
48765
50725
|
options,
|
|
48766
50726
|
"solo handoff requires requirements.md and plan.md"
|
|
48767
50727
|
);
|
|
48768
50728
|
}
|
|
48769
|
-
const statePath =
|
|
50729
|
+
const statePath = path74.join(rootDir, ".mancode", "state.json");
|
|
48770
50730
|
let originalState;
|
|
48771
50731
|
let state;
|
|
48772
50732
|
try {
|
|
48773
|
-
originalState = await
|
|
50733
|
+
originalState = await readFile51(statePath, "utf-8");
|
|
48774
50734
|
state = JSON.parse(originalState);
|
|
48775
50735
|
} catch {
|
|
48776
50736
|
return invalidArg(
|
|
@@ -48841,11 +50801,11 @@ async function workflowCompleteHandoff(rootDir, taskId, options) {
|
|
|
48841
50801
|
`workflow is not an active solo handoff: ${taskId}`
|
|
48842
50802
|
);
|
|
48843
50803
|
}
|
|
48844
|
-
const statePath =
|
|
50804
|
+
const statePath = path74.join(rootDir, ".mancode", "state.json");
|
|
48845
50805
|
let originalState;
|
|
48846
50806
|
let state;
|
|
48847
50807
|
try {
|
|
48848
|
-
originalState = await
|
|
50808
|
+
originalState = await readFile51(statePath, "utf-8");
|
|
48849
50809
|
state = JSON.parse(originalState);
|
|
48850
50810
|
} catch {
|
|
48851
50811
|
return invalidArg(
|
|
@@ -48908,18 +50868,18 @@ async function workflowDecide(rootDir, taskId, options) {
|
|
|
48908
50868
|
`workflow is not ready for a plan-only decision: ${taskId}`
|
|
48909
50869
|
);
|
|
48910
50870
|
}
|
|
48911
|
-
const workflowPath =
|
|
48912
|
-
if (!await pathExists16(
|
|
50871
|
+
const workflowPath = path74.join(rootDir, ".mancode", "workflows", taskId);
|
|
50872
|
+
if (!await pathExists16(path74.join(workflowPath, "requirements.md")) || !await pathExists16(path74.join(workflowPath, "plan.md"))) {
|
|
48913
50873
|
return invalidArg(
|
|
48914
50874
|
options,
|
|
48915
50875
|
"plan-only requires requirements.md and plan.md"
|
|
48916
50876
|
);
|
|
48917
50877
|
}
|
|
48918
|
-
const statePath =
|
|
50878
|
+
const statePath = path74.join(rootDir, ".mancode", "state.json");
|
|
48919
50879
|
let originalState;
|
|
48920
50880
|
let state;
|
|
48921
50881
|
try {
|
|
48922
|
-
originalState = await
|
|
50882
|
+
originalState = await readFile51(statePath, "utf-8");
|
|
48923
50883
|
state = JSON.parse(originalState);
|
|
48924
50884
|
} catch {
|
|
48925
50885
|
return invalidArg(
|
|
@@ -48971,25 +50931,25 @@ async function workflowDecide(rootDir, taskId, options) {
|
|
|
48971
50931
|
return EXIT_OK11;
|
|
48972
50932
|
}
|
|
48973
50933
|
async function commitPlanningTransition(args) {
|
|
48974
|
-
const metadataPath3 =
|
|
50934
|
+
const metadataPath3 = path74.join(
|
|
48975
50935
|
args.rootDir,
|
|
48976
50936
|
".mancode",
|
|
48977
50937
|
"workflows",
|
|
48978
50938
|
args.taskId,
|
|
48979
50939
|
"metadata.json"
|
|
48980
50940
|
);
|
|
48981
|
-
const specPath =
|
|
50941
|
+
const specPath = path74.join(args.rootDir, ".mancode", "memory", "spec.md");
|
|
48982
50942
|
let originalMetadata;
|
|
48983
50943
|
let originalSpec;
|
|
48984
50944
|
try {
|
|
48985
|
-
originalMetadata = await
|
|
50945
|
+
originalMetadata = await readFile51(metadataPath3, "utf-8");
|
|
48986
50946
|
originalSpec = await readOptionalText2(specPath);
|
|
48987
50947
|
} catch (error) {
|
|
48988
50948
|
return error instanceof Error ? `unable to prepare planning transition: ${error.message}` : "unable to prepare planning transition";
|
|
48989
50949
|
}
|
|
48990
50950
|
try {
|
|
48991
50951
|
await updateWorkflow(args.rootDir, args.taskId, args.workflowPatch);
|
|
48992
|
-
await
|
|
50952
|
+
await writeFile53(
|
|
48993
50953
|
args.statePath,
|
|
48994
50954
|
`${JSON.stringify(args.nextState, null, 2)}
|
|
48995
50955
|
`,
|
|
@@ -49003,9 +50963,9 @@ async function commitPlanningTransition(args) {
|
|
|
49003
50963
|
return null;
|
|
49004
50964
|
} catch (error) {
|
|
49005
50965
|
const rollback = await Promise.allSettled([
|
|
49006
|
-
|
|
49007
|
-
|
|
49008
|
-
originalSpec === null ?
|
|
50966
|
+
writeFile53(metadataPath3, originalMetadata, "utf-8"),
|
|
50967
|
+
writeFile53(args.statePath, args.originalState, "utf-8"),
|
|
50968
|
+
originalSpec === null ? rm26(specPath, { force: true }) : writeFile53(specPath, originalSpec, "utf-8")
|
|
49009
50969
|
]);
|
|
49010
50970
|
const message = error instanceof Error ? error.message : "planning transition failed";
|
|
49011
50971
|
return rollback.some((result2) => result2.status === "rejected") ? `${message}; rollback was incomplete` : message;
|
|
@@ -49013,7 +50973,7 @@ async function commitPlanningTransition(args) {
|
|
|
49013
50973
|
}
|
|
49014
50974
|
async function readOptionalText2(filePath) {
|
|
49015
50975
|
try {
|
|
49016
|
-
return await
|
|
50976
|
+
return await readFile51(filePath, "utf-8");
|
|
49017
50977
|
} catch (error) {
|
|
49018
50978
|
if (isNodeError11(error) && error.code === "ENOENT") return null;
|
|
49019
50979
|
throw error;
|
|
@@ -49021,10 +50981,10 @@ async function readOptionalText2(filePath) {
|
|
|
49021
50981
|
}
|
|
49022
50982
|
async function restoreOptionalText(filePath, content) {
|
|
49023
50983
|
if (content === null) {
|
|
49024
|
-
await
|
|
50984
|
+
await rm26(filePath, { force: true });
|
|
49025
50985
|
return;
|
|
49026
50986
|
}
|
|
49027
|
-
await
|
|
50987
|
+
await writeFile53(filePath, content, "utf-8");
|
|
49028
50988
|
}
|
|
49029
50989
|
function readActiveSoloPlan(value) {
|
|
49030
50990
|
if (!value || typeof value !== "object") return null;
|
|
@@ -49371,7 +51331,10 @@ Public Continuity subcommands:
|
|
|
49371
51331
|
).option("--to <mode>", "Workflow handoff target (solo)").option("--complete", "Complete an active solo handoff").option(
|
|
49372
51332
|
"--skipped <steps>",
|
|
49373
51333
|
"Policy v2: clarification only; use workflow review skip for review"
|
|
49374
|
-
).option("--review-depth <depth>", "Review depth: targeted or full").option(
|
|
51334
|
+
).option("--review-depth <depth>", "Review depth: targeted or full").option(
|
|
51335
|
+
"--delivery",
|
|
51336
|
+
"Opt in a new man task to document-bound module delivery"
|
|
51337
|
+
).option("--review-domain <domain>", "Review domain: quality or security").option(
|
|
49375
51338
|
"--report <path>",
|
|
49376
51339
|
"Relative Markdown report path for a review domain"
|
|
49377
51340
|
).option("--blockers <ids>", "Comma-separated blocker ids found by a review").option(
|
|
@@ -49520,7 +51483,10 @@ Public Continuity subcommands:
|
|
|
49520
51483
|
options
|
|
49521
51484
|
);
|
|
49522
51485
|
});
|
|
49523
|
-
operationProgram.command("repair <operationId>").description("Repair an operation using its original actor and session").option(
|
|
51486
|
+
operationProgram.command("repair <operationId>").description("Repair an operation using its original actor and session").option(
|
|
51487
|
+
"--replacement-checkpoint-id <id>",
|
|
51488
|
+
"Replace only a conflicted reframe checkpoint target"
|
|
51489
|
+
).option("--session <id>", "Session ID (otherwise MANCODE_SESSION_ID)").option("--client <name>", "Client identity (default: mancode-cli)").option("--json", "Output as JSON (for scripts)").action(async (operationId, options) => {
|
|
49524
51490
|
process.exitCode = await operationRepair(
|
|
49525
51491
|
process.cwd(),
|
|
49526
51492
|
operationId,
|