gentle-pi 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -30
- package/assets/agents/gentle-ai-worker.md +4 -0
- package/assets/agents/jd-fix-agent.md +18 -0
- package/assets/agents/jd-judge-a.md +1 -1
- package/assets/agents/jd-judge-b.md +1 -1
- package/assets/agents/sdd-apply.md +7 -5
- package/assets/agents/sdd-archive.md +5 -3
- package/assets/agents/sdd-design.md +4 -0
- package/assets/agents/sdd-explore.md +4 -0
- package/assets/agents/sdd-init.md +4 -0
- package/assets/agents/sdd-onboard.md +4 -0
- package/assets/agents/sdd-proposal.md +4 -0
- package/assets/agents/sdd-remediate.md +37 -0
- package/assets/agents/sdd-research.md +26 -3
- package/assets/agents/sdd-spec.md +4 -0
- package/assets/agents/sdd-status.md +9 -75
- package/assets/agents/sdd-sync.md +4 -0
- package/assets/agents/sdd-tasks.md +4 -0
- package/assets/agents/sdd-verify.md +5 -3
- package/assets/chains/sdd-full.chain.md +4 -0
- package/assets/chains/sdd-plan.chain.md +4 -0
- package/assets/chains/sdd-verify.chain.md +4 -0
- package/assets/migrations/managed-assets-v2.5.0.json +7 -0
- package/assets/orchestrator-delegation.md +21 -3
- package/assets/sdd-orchestrator-workflow.md +54 -21
- package/assets/support/sdd-status-contract.md +34 -90
- package/contracts/telemetry/runtime-aggregate-v1.schema.json +65 -0
- package/docs/telemetry.md +57 -1
- package/docs/windows-startup-console-visibility.md +18 -0
- package/extensions/ask-user-choice.ts +143 -15
- package/extensions/codegraph-tools.ts +1 -0
- package/extensions/gentle-agents.ts +799 -50
- package/extensions/gentle-ai.ts +2033 -322
- package/extensions/gentle-shell.ts +145 -42
- package/extensions/gentle-todo.ts +47 -12
- package/extensions/quiet-tools.ts +1 -0
- package/extensions/runtime-metrics.ts +130 -0
- package/extensions/sdd-init.ts +2 -2
- package/extensions/startup-banner.ts +52 -75
- package/lib/agent-profiles.ts +550 -0
- package/lib/agents-completion-delivery.ts +72 -0
- package/lib/agents-config.ts +7 -10
- package/lib/agents-history.ts +9 -1
- package/lib/agents-messaging.ts +187 -0
- package/lib/agents-protocol.ts +77 -5
- package/lib/agents-runner.ts +548 -26
- package/lib/agents-thread-view.ts +57 -0
- package/lib/agents-view-layout.ts +40 -0
- package/lib/agents-view.ts +548 -191
- package/lib/agents-widget.ts +33 -14
- package/lib/gentle-ai-binary.ts +3 -1
- package/lib/gentle-ai-renderer.ts +8 -6
- package/lib/native-review-cli.ts +283 -1
- package/lib/orchestrator-presence.ts +337 -0
- package/lib/profiles-orchestrator.ts +203 -0
- package/lib/review-candidate-view-owner.ts +296 -46
- package/lib/review-candidate-view.ts +30 -20
- package/lib/review-consent-component.ts +247 -0
- package/lib/review-consent-ui.ts +53 -8
- package/lib/review-host-relay.ts +28 -0
- package/lib/review-integration-v2.ts +187 -5
- package/lib/review-last-event-controller.ts +7 -4
- package/lib/review-reminder-receipt.ts +74 -0
- package/lib/review-session-standing-permission.ts +27 -6
- package/lib/runtime-metrics-children.ts +199 -0
- package/lib/runtime-metrics-delivery.ts +68 -0
- package/lib/runtime-metrics-native.ts +166 -0
- package/lib/runtime-metrics-pi-identity.ts +113 -0
- package/lib/runtime-metrics-policy.ts +51 -0
- package/lib/runtime-metrics.ts +255 -0
- package/lib/sdd-preflight.ts +362 -81
- package/lib/sdd-research-capabilities.ts +228 -0
- package/lib/sdd-status.ts +29 -7
- package/lib/session-worktree-registry.ts +118 -0
- package/lib/shell-bar.ts +47 -1
- package/lib/shell-card.ts +1 -4
- package/lib/shell-changes-view.ts +362 -37
- package/lib/shell-changes.ts +81 -1
- package/lib/shell-prompt.ts +11 -15
- package/lib/shell-sidebar-banner.ts +11 -0
- package/lib/shell-sidebar-layout.ts +213 -0
- package/lib/shell-sidebar.ts +41 -0
- package/lib/shell-todo.ts +28 -11
- package/lib/telemetry-trigger.ts +2 -0
- package/package.json +6 -3
- package/runtime/gentle-ai-binary.mjs +3 -1
- package/runtime/native-review-cli.mjs +283 -1
- package/runtime/review-integration-v2.mjs +187 -5
- package/runtime/telemetry-trigger.mjs +2 -0
- package/scripts/build-runtime-modules.mjs +9 -1
- package/scripts/check-types.mjs +125 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/install-gentle-ai.mjs +12 -0
- package/scripts/install-tui-mode-setting.mjs +114 -0
- package/scripts/test-packed-runner.mjs +16 -2
- package/scripts/types-baseline.json +99 -0
- package/scripts/verify-package-files.mjs +4 -2
- package/skills/_shared/review-ledger-contract.md +17 -1
- package/skills/issue-creation/SKILL.md +3 -3
- package/skills/judgment-day/SKILL.md +17 -3
- package/skills/judgment-day/references/prompts-and-formats.md +14 -3
- package/tests/agent-profiles.test.ts +722 -0
- package/tests/agents-completion-delivery.test.ts +94 -0
- package/tests/agents-config.test.ts +62 -0
- package/tests/agents-fake-child.ts +15 -1
- package/tests/agents-grouping.test.ts +179 -0
- package/tests/agents-integration.test.ts +100 -0
- package/tests/agents-messaging.test.ts +94 -0
- package/tests/agents-protocol.test.ts +45 -0
- package/tests/agents-queries.test.ts +190 -0
- package/tests/agents-responsive.test.ts +43 -0
- package/tests/agents-runner.test.ts +571 -14
- package/tests/agents-thread-view.test.ts +45 -0
- package/tests/agents-view.test.ts +476 -65
- package/tests/agents-widget.test.ts +31 -1
- package/tests/artifact-language.test.ts +25 -2
- package/tests/ask-user-choice.test.ts +169 -3
- package/tests/asset-installation-runtime.test.ts +108 -0
- package/tests/autonomous-guard.test.ts +116 -1
- package/tests/codegraph-tools.test.ts +2 -1
- package/tests/delegated-key-learnings-contract.test.ts +1 -1
- package/tests/devbinary/native-review-parity.devtest.ts +2 -0
- package/tests/feature-request-form.test.ts +67 -0
- package/tests/fixtures/agents-messaging-child.mjs +5 -0
- package/tests/fixtures/runtime-metrics-native-batches.json +6 -0
- package/tests/gentle-agents.test.ts +1460 -33
- package/tests/gentle-ai-binary.test.ts +7 -2
- package/tests/gentle-ai-installer.test.ts +47 -47
- package/tests/gentle-ai-renderer.test.ts +38 -0
- package/tests/gentle-ai.test.ts +944 -4
- package/tests/gentle-shell.test.ts +303 -12
- package/tests/gentle-todo.test.ts +54 -10
- package/tests/install-tui-mode-setting.test.ts +324 -0
- package/tests/issue-creation-skill.test.ts +22 -0
- package/tests/model-routing-authority.test.ts +12 -0
- package/tests/native-review-capability-contract.test.ts +12 -1
- package/tests/native-review-cli.test.ts +277 -3
- package/tests/native-review-parity.test.ts +14 -7
- package/tests/native-sdd-attempt-authority.test.ts +7 -2
- package/tests/orchestrator-presence.test.ts +389 -0
- package/tests/package-manifest.test.ts +232 -7
- package/tests/profiles-orchestrator.test.ts +208 -0
- package/tests/quiet-tool-rendering.test.ts +1 -0
- package/tests/rdd-aware-verification-contract.test.ts +10 -0
- package/tests/review-agent-end-preflight.test.ts +332 -24
- package/tests/review-candidate-view.test.ts +304 -6
- package/tests/review-consent-ui.test.ts +352 -0
- package/tests/review-contract-prompt.test.ts +14 -0
- package/tests/review-controller-native-routing.test.ts +563 -3
- package/tests/review-controller.test.ts +1 -1
- package/tests/review-host-relay-restart-parity.test.ts +142 -1
- package/tests/review-host-relay-routing.test.ts +364 -4
- package/tests/review-host-relay.test.ts +29 -0
- package/tests/review-integration-v2-forward.test.ts +44 -0
- package/tests/review-integration-v2.test.ts +164 -0
- package/tests/review-last-event-closure.test.ts +105 -1
- package/tests/review-ledger-contract.test.ts +61 -6
- package/tests/review-reminder-receipt.test.ts +62 -0
- package/tests/review-session-standing-permission-controller.test.ts +52 -4
- package/tests/review-session-standing-permission.test.ts +30 -0
- package/tests/runtime-harness.mjs +447 -39
- package/tests/runtime-metrics-children.test.ts +206 -0
- package/tests/runtime-metrics-delivery.test.ts +85 -0
- package/tests/runtime-metrics-extension.test.ts +187 -0
- package/tests/runtime-metrics-native.test.ts +209 -0
- package/tests/runtime-metrics-pi-identity.test.ts +113 -0
- package/tests/runtime-metrics-policy.test.ts +62 -0
- package/tests/runtime-metrics.test.ts +184 -0
- package/tests/sdd-agent-tools.test.ts +10 -1
- package/tests/sdd-execution-routing-contract.test.ts +28 -0
- package/tests/sdd-managed-runtime-settlement.test.ts +331 -0
- package/tests/sdd-native-managed-uptake.test.ts +253 -0
- package/tests/sdd-planning-routing-contract.test.ts +45 -0
- package/tests/sdd-preflight.test.ts +252 -8
- package/tests/sdd-research-capabilities.test.ts +256 -0
- package/tests/sdd-research-live.test.ts +241 -0
- package/tests/sdd-selection-transport.test.ts +504 -0
- package/tests/sdd-status.test.ts +51 -0
- package/tests/session-worktree-registry.test.ts +135 -0
- package/tests/shell-card.test.ts +24 -3
- package/tests/shell-changes-view.test.ts +471 -8
- package/tests/shell-changes.test.ts +168 -0
- package/tests/shell-prompt.test.ts +28 -6
- package/tests/shell-sidebar-banner.test.ts +23 -0
- package/tests/shell-sidebar-layout.test.ts +387 -0
- package/tests/shell-sidebar.test.ts +50 -0
- package/tests/shell-todo.test.ts +100 -11
- package/tests/startup-banner.test.ts +126 -0
- package/tests/telemetry-trigger.test.ts +3 -1
|
@@ -910,6 +910,78 @@ test("ordinary START binds the native workspace candidate and returns the native
|
|
|
910
910
|
assert.equal(startCalls, 1);
|
|
911
911
|
});
|
|
912
912
|
|
|
913
|
+
test("ordinary START preserves sanitized foreign diagnostics through one ambiguous reconciliation", async (t) => {
|
|
914
|
+
const cwd = repository(t);
|
|
915
|
+
const target = startStatus(cwd);
|
|
916
|
+
let targetCalls = 0;
|
|
917
|
+
let startCalls = 0;
|
|
918
|
+
const native = {
|
|
919
|
+
targetStatus: async () => {
|
|
920
|
+
targetCalls += 1;
|
|
921
|
+
return target;
|
|
922
|
+
},
|
|
923
|
+
start: async () => {
|
|
924
|
+
startCalls += 1;
|
|
925
|
+
throw {
|
|
926
|
+
name: "NativeReviewCliError",
|
|
927
|
+
code: NATIVE_REVIEW_ERROR_CODE.NON_ZERO,
|
|
928
|
+
mutationOutcome: "unknown",
|
|
929
|
+
nextAction: "review.status",
|
|
930
|
+
diagnostics: {
|
|
931
|
+
operation: "review/start",
|
|
932
|
+
error_code: NATIVE_REVIEW_ERROR_CODE.NON_ZERO,
|
|
933
|
+
exit_code: 1,
|
|
934
|
+
timed_out: false,
|
|
935
|
+
output_limit_exceeded: false,
|
|
936
|
+
stderr: "token=secret",
|
|
937
|
+
},
|
|
938
|
+
};
|
|
939
|
+
},
|
|
940
|
+
} as unknown as NativeReviewCli;
|
|
941
|
+
|
|
942
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "start", input: JSON.stringify({ mode: "ordinary" }) }, cwd, native);
|
|
943
|
+
assert.equal(result.outcome, "native-mutation-status-reconciled");
|
|
944
|
+
assert.equal(result.mutation_outcome, "unknown");
|
|
945
|
+
assert.deepEqual(result.diagnostics, {
|
|
946
|
+
operation: "review/start",
|
|
947
|
+
error_code: NATIVE_REVIEW_ERROR_CODE.NON_ZERO,
|
|
948
|
+
exit_code: 1,
|
|
949
|
+
timed_out: false,
|
|
950
|
+
output_limit_exceeded: false,
|
|
951
|
+
stderr: "token=[REDACTED]",
|
|
952
|
+
});
|
|
953
|
+
assert.match(String(result.required_status_action), /Run target-scoped review\.status/);
|
|
954
|
+
assert.equal(result.next_action, "start");
|
|
955
|
+
assert.equal(targetCalls, 2, "START failure reconciles STATUS exactly once");
|
|
956
|
+
assert.equal(startCalls, 1, "reconciliation never replays START");
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
test("ambiguous START reconciliation preserves the provider-selected recovery directive", async (t) => {
|
|
960
|
+
const cwd = repository(t), recoveryStatus = status("recovery-lineage");
|
|
961
|
+
recoveryStatus.action = "recover";
|
|
962
|
+
recoveryStatus.actionDisposition = "escalated";
|
|
963
|
+
recoveryStatus.nextTransition = { kind: "stop", reasonCode: "manual_intervention_required" };
|
|
964
|
+
let targetCalls = 0, startCalls = 0;
|
|
965
|
+
const native = {
|
|
966
|
+
targetStatus: async () => {
|
|
967
|
+
targetCalls += 1;
|
|
968
|
+
return targetCalls === 1 ? startStatus(cwd) : recoveryStatus;
|
|
969
|
+
},
|
|
970
|
+
start: async () => {
|
|
971
|
+
startCalls += 1;
|
|
972
|
+
throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.NON_ZERO, "review/start", true, true, "unknown mutation");
|
|
973
|
+
},
|
|
974
|
+
} as unknown as NativeReviewCli;
|
|
975
|
+
|
|
976
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "start", input: JSON.stringify({ mode: "ordinary" }) }, cwd, native);
|
|
977
|
+
assert.equal(result.outcome, "native-mutation-status-reconciled");
|
|
978
|
+
assert.equal(result.mutation_outcome, "unknown");
|
|
979
|
+
assert.equal(result.next_action, "recover-with-provider-disposition");
|
|
980
|
+
assert.equal(result.required_status_action, "Use only the provider-selected recovery disposition; do not substitute scope_changed, invalidated, or escalated.");
|
|
981
|
+
assert.equal(targetCalls, 2, "START failure reconciles STATUS exactly once");
|
|
982
|
+
assert.equal(startCalls, 1, "reconciliation never replays START");
|
|
983
|
+
});
|
|
984
|
+
|
|
913
985
|
for (const statusSchema of ["gentle-ai.review-integration.status/v6", "gentle-ai.review-integration.status/v7"]) test(`pre-lineage intended-untracked selection revalidates and starts at an explicit workspace root (${statusSchema})`, async (t) => {
|
|
914
986
|
const cwd = realpathSync(repository(t)), sessionCwd = repository(t), eligible = "selected.md";
|
|
915
987
|
writeFileSync(join(cwd, eligible), "selected\n");
|
|
@@ -953,6 +1025,470 @@ for (const statusSchema of ["gentle-ai.review-integration.status/v6", "gentle-ai
|
|
|
953
1025
|
assert.equal(requests.every((request) => !("lineageId" in request)), true);
|
|
954
1026
|
});
|
|
955
1027
|
|
|
1028
|
+
// gentle-pi#706: inspect names the exact continuation for the intended-untracked
|
|
1029
|
+
// stop and can resolve it in one call through top-level untrackedScope.
|
|
1030
|
+
function untrackedStopFixture(
|
|
1031
|
+
t: test.TestContext,
|
|
1032
|
+
statusSchema = "gentle-ai.review-integration.status/v7",
|
|
1033
|
+
): {
|
|
1034
|
+
cwd: string;
|
|
1035
|
+
eligible: string;
|
|
1036
|
+
initial: ReviewStatusV3;
|
|
1037
|
+
target: ReviewStatusV3;
|
|
1038
|
+
selection: ReviewCollectInputV3;
|
|
1039
|
+
} {
|
|
1040
|
+
const cwd = repository(t),
|
|
1041
|
+
eligible = "selected.md";
|
|
1042
|
+
writeFileSync(join(cwd, eligible), "selected\n");
|
|
1043
|
+
const initialTarget = startStatus(cwd),
|
|
1044
|
+
target = startStatus(cwd, undefined, [eligible]);
|
|
1045
|
+
const selection: ReviewCollectInputV3 = {
|
|
1046
|
+
name: "intended_untracked_selection",
|
|
1047
|
+
schema: "gentle-ai.review-intended-untracked-selection/v1",
|
|
1048
|
+
captureOperation: "external.select_intended_untracked",
|
|
1049
|
+
arguments: [
|
|
1050
|
+
{ name: "target_identity", value: SHA },
|
|
1051
|
+
{ name: "projection", value: "workspace" },
|
|
1052
|
+
{ name: "base_tree", value: initialTarget.projection.baseTree },
|
|
1053
|
+
{
|
|
1054
|
+
name: "candidate_tree",
|
|
1055
|
+
value: initialTarget.projection.currentCandidateTree,
|
|
1056
|
+
},
|
|
1057
|
+
{ name: "eligible_paths_json", value: JSON.stringify([eligible]) },
|
|
1058
|
+
{ name: "expected_untracked_inventory", value: SHA },
|
|
1059
|
+
],
|
|
1060
|
+
submission: {
|
|
1061
|
+
operationToken: "status",
|
|
1062
|
+
argumentTokens: [
|
|
1063
|
+
"--contract=gentle-ai.review-integration/v2",
|
|
1064
|
+
"--next-transition=true",
|
|
1065
|
+
"--agent=pi",
|
|
1066
|
+
"--projection=workspace",
|
|
1067
|
+
"--intended-untracked-selection={{value}}",
|
|
1068
|
+
],
|
|
1069
|
+
values: [
|
|
1070
|
+
{
|
|
1071
|
+
slot: "intended_untracked_selection",
|
|
1072
|
+
domain: "schema_bound_json",
|
|
1073
|
+
schema: "gentle-ai.review-intended-untracked-selection/v1",
|
|
1074
|
+
substitutionLocation: 4,
|
|
1075
|
+
},
|
|
1076
|
+
],
|
|
1077
|
+
},
|
|
1078
|
+
};
|
|
1079
|
+
const initial = {
|
|
1080
|
+
...initialTarget,
|
|
1081
|
+
nextTransition: {
|
|
1082
|
+
kind: "collect",
|
|
1083
|
+
reasonCode: "intended_untracked_selection_required",
|
|
1084
|
+
collect: { inputs: [selection] },
|
|
1085
|
+
},
|
|
1086
|
+
raw: { schema: statusSchema },
|
|
1087
|
+
} as ReviewStatusV3;
|
|
1088
|
+
return { cwd, eligible, initial, target, selection };
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
for (const statusSchema of [
|
|
1092
|
+
"gentle-ai.review-integration.status/v6",
|
|
1093
|
+
"gentle-ai.review-integration.status/v7",
|
|
1094
|
+
])
|
|
1095
|
+
test(`inspect on the intended-untracked stop returns nextStep and the selection binding (${statusSchema})`, async (t) => {
|
|
1096
|
+
const { cwd, initial } = untrackedStopFixture(t, statusSchema);
|
|
1097
|
+
let targetCalls = 0;
|
|
1098
|
+
const native = {
|
|
1099
|
+
targetStatus: async () => {
|
|
1100
|
+
targetCalls += 1;
|
|
1101
|
+
return initial;
|
|
1102
|
+
},
|
|
1103
|
+
} as unknown as NativeReviewCli;
|
|
1104
|
+
const result = await __testing.executeReviewControllerOperation(
|
|
1105
|
+
{ operation: "inspect" },
|
|
1106
|
+
cwd,
|
|
1107
|
+
native,
|
|
1108
|
+
);
|
|
1109
|
+
assert.equal(result.status, "blocked");
|
|
1110
|
+
assert.equal(typeof result.selectionBinding, "string");
|
|
1111
|
+
assert.equal(typeof result.nextStep, "string");
|
|
1112
|
+
for (const fragment of [
|
|
1113
|
+
"select-intended-untracked",
|
|
1114
|
+
"untrackedScope",
|
|
1115
|
+
".gitignore",
|
|
1116
|
+
"path names",
|
|
1117
|
+
]) {
|
|
1118
|
+
assert.equal((result.nextStep as string).includes(fragment), true, fragment);
|
|
1119
|
+
}
|
|
1120
|
+
assert.equal(targetCalls, 1);
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
test("inspect with untrackedScope exclude resolves the intended-untracked stop in one round trip", async (t) => {
|
|
1124
|
+
const { cwd, initial, target, selection } = untrackedStopFixture(t);
|
|
1125
|
+
const requests: Array<Record<string, unknown>> = [],
|
|
1126
|
+
retained = new Map();
|
|
1127
|
+
const native = {
|
|
1128
|
+
targetStatus: async (request: Record<string, unknown>) => {
|
|
1129
|
+
requests.push(request);
|
|
1130
|
+
return "intendedUntrackedSelection" in request ? target : initial;
|
|
1131
|
+
},
|
|
1132
|
+
} as unknown as NativeReviewCli;
|
|
1133
|
+
const result = await __testing.executeReviewControllerOperation(
|
|
1134
|
+
{ operation: "inspect", untrackedScope: "exclude" },
|
|
1135
|
+
cwd,
|
|
1136
|
+
native,
|
|
1137
|
+
undefined,
|
|
1138
|
+
undefined,
|
|
1139
|
+
undefined,
|
|
1140
|
+
retained,
|
|
1141
|
+
);
|
|
1142
|
+
assert.equal(result.status, "ready");
|
|
1143
|
+
assert.equal("selectionBinding" in result, false);
|
|
1144
|
+
assert.equal(requests.length, 2);
|
|
1145
|
+
assert.equal(requests[1]!.untrackedScope, "exclude");
|
|
1146
|
+
assert.equal(requests[1]!.expectedUntrackedInventory, SHA);
|
|
1147
|
+
assert.deepEqual(requests[1]!.intendedUntracked, []);
|
|
1148
|
+
assert.deepEqual(requests[1]!.intendedUntrackedSelection, {
|
|
1149
|
+
argumentTokens: selection.submission!.argumentTokens,
|
|
1150
|
+
value: JSON.stringify({
|
|
1151
|
+
schema: "gentle-ai.review-intended-untracked-selection/v1",
|
|
1152
|
+
untracked_scope: "exclude",
|
|
1153
|
+
expected_untracked_inventory: SHA,
|
|
1154
|
+
intended_untracked: [],
|
|
1155
|
+
}),
|
|
1156
|
+
});
|
|
1157
|
+
const retainedEntry = retained.get(`${cwd}\u0000`) as
|
|
1158
|
+
| { untrackedScope: string; submission?: unknown }
|
|
1159
|
+
| undefined;
|
|
1160
|
+
assert.notEqual(retainedEntry, undefined);
|
|
1161
|
+
assert.equal(retainedEntry!.untrackedScope, "exclude");
|
|
1162
|
+
assert.notEqual(retainedEntry!.submission, undefined);
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
test("inspect with untrackedScope select resolves the stop with exactly the selected paths", async (t) => {
|
|
1166
|
+
const { cwd, eligible, initial, target } = untrackedStopFixture(t);
|
|
1167
|
+
const requests: Array<Record<string, unknown>> = [];
|
|
1168
|
+
const native = {
|
|
1169
|
+
targetStatus: async (request: Record<string, unknown>) => {
|
|
1170
|
+
requests.push(request);
|
|
1171
|
+
return "intendedUntrackedSelection" in request ? target : initial;
|
|
1172
|
+
},
|
|
1173
|
+
} as unknown as NativeReviewCli;
|
|
1174
|
+
const result = await __testing.executeReviewControllerOperation(
|
|
1175
|
+
{
|
|
1176
|
+
operation: "inspect",
|
|
1177
|
+
untrackedScope: "select",
|
|
1178
|
+
intendedUntracked: [eligible],
|
|
1179
|
+
},
|
|
1180
|
+
cwd,
|
|
1181
|
+
native,
|
|
1182
|
+
);
|
|
1183
|
+
assert.equal(result.status, "ready");
|
|
1184
|
+
assert.equal(requests.length, 2);
|
|
1185
|
+
assert.equal(requests[1]!.untrackedScope, "select");
|
|
1186
|
+
assert.deepEqual(requests[1]!.intendedUntracked, [eligible]);
|
|
1187
|
+
const submission = requests[1]!.intendedUntrackedSelection as {
|
|
1188
|
+
value: string;
|
|
1189
|
+
};
|
|
1190
|
+
assert.deepEqual(JSON.parse(submission.value), {
|
|
1191
|
+
schema: "gentle-ai.review-intended-untracked-selection/v1",
|
|
1192
|
+
untracked_scope: "select",
|
|
1193
|
+
expected_untracked_inventory: SHA,
|
|
1194
|
+
intended_untracked: [eligible],
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
test("inspect with untrackedScope select rejects a path outside the eligible inventory", async (t) => {
|
|
1199
|
+
const { cwd, initial } = untrackedStopFixture(t);
|
|
1200
|
+
let targetCalls = 0;
|
|
1201
|
+
const native = {
|
|
1202
|
+
targetStatus: async () => {
|
|
1203
|
+
targetCalls += 1;
|
|
1204
|
+
return initial;
|
|
1205
|
+
},
|
|
1206
|
+
} as unknown as NativeReviewCli;
|
|
1207
|
+
const result = await __testing.executeReviewControllerOperation(
|
|
1208
|
+
{
|
|
1209
|
+
operation: "inspect",
|
|
1210
|
+
untrackedScope: "select",
|
|
1211
|
+
intendedUntracked: ["docs/unknown.md"],
|
|
1212
|
+
},
|
|
1213
|
+
cwd,
|
|
1214
|
+
native,
|
|
1215
|
+
);
|
|
1216
|
+
assert.equal(result.status, "blocked");
|
|
1217
|
+
assert.equal(result.outcome, "inspect-untracked-scope-invalid");
|
|
1218
|
+
assert.equal(result.mutation_performed, false);
|
|
1219
|
+
assert.equal(result.mutation_outcome, "none");
|
|
1220
|
+
assert.equal(targetCalls, 1);
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
for (const invalid of [
|
|
1224
|
+
{ untrackedScope: "select" },
|
|
1225
|
+
{ untrackedScope: "exclude", intendedUntracked: ["selected.md"] },
|
|
1226
|
+
] as const)
|
|
1227
|
+
test(`inspect rejects an inconsistent untracked scope combination (${JSON.stringify(invalid)})`, async (t) => {
|
|
1228
|
+
const { cwd, initial } = untrackedStopFixture(t);
|
|
1229
|
+
let targetCalls = 0;
|
|
1230
|
+
const native = {
|
|
1231
|
+
targetStatus: async () => {
|
|
1232
|
+
targetCalls += 1;
|
|
1233
|
+
return initial;
|
|
1234
|
+
},
|
|
1235
|
+
} as unknown as NativeReviewCli;
|
|
1236
|
+
if (invalid.untrackedScope === "exclude") {
|
|
1237
|
+
await assert.rejects(
|
|
1238
|
+
() => __testing.executeReviewControllerOperation({ operation: "inspect", ...invalid }, cwd, native),
|
|
1239
|
+
/requires untrackedScope select/,
|
|
1240
|
+
);
|
|
1241
|
+
assert.equal(targetCalls, 0);
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
const result = await __testing.executeReviewControllerOperation(
|
|
1245
|
+
{ operation: "inspect", ...invalid },
|
|
1246
|
+
cwd,
|
|
1247
|
+
native,
|
|
1248
|
+
);
|
|
1249
|
+
assert.equal(result.status, "blocked");
|
|
1250
|
+
assert.equal(result.outcome, "inspect-untracked-scope-invalid");
|
|
1251
|
+
assert.equal(result.mutation_performed, false);
|
|
1252
|
+
assert.equal(result.mutation_outcome, "none");
|
|
1253
|
+
assert.equal(targetCalls, 1);
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
test("inspect with untrackedScope and no intended-untracked stop reports not-required", async (t) => {
|
|
1257
|
+
const { cwd, target } = untrackedStopFixture(t);
|
|
1258
|
+
let targetCalls = 0;
|
|
1259
|
+
const native = {
|
|
1260
|
+
targetStatus: async () => {
|
|
1261
|
+
targetCalls += 1;
|
|
1262
|
+
return target;
|
|
1263
|
+
},
|
|
1264
|
+
} as unknown as NativeReviewCli;
|
|
1265
|
+
const result = await __testing.executeReviewControllerOperation(
|
|
1266
|
+
{ operation: "inspect", untrackedScope: "exclude" },
|
|
1267
|
+
cwd,
|
|
1268
|
+
native,
|
|
1269
|
+
);
|
|
1270
|
+
assert.equal(result.status, "ready");
|
|
1271
|
+
assert.equal(result.untracked_selection, "not-required");
|
|
1272
|
+
assert.equal(targetCalls, 1);
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1275
|
+
test("plain START adopts the pre-lineage selection retained by inspect and deletes it after success", async (t) => {
|
|
1276
|
+
const { cwd, eligible, initial, target, selection } = untrackedStopFixture(t);
|
|
1277
|
+
const requests: Array<Record<string, unknown>> = [],
|
|
1278
|
+
starts: Array<Record<string, unknown>> = [],
|
|
1279
|
+
retained = new Map();
|
|
1280
|
+
const native = {
|
|
1281
|
+
targetStatus: async (request: Record<string, unknown>) => {
|
|
1282
|
+
requests.push(request);
|
|
1283
|
+
return "intendedUntrackedSelection" in request ? target : initial;
|
|
1284
|
+
},
|
|
1285
|
+
start: async (request: Record<string, unknown>) => {
|
|
1286
|
+
starts.push(request);
|
|
1287
|
+
return {
|
|
1288
|
+
lineageId: "review-started",
|
|
1289
|
+
state: "reviewing",
|
|
1290
|
+
riskLevel: "low",
|
|
1291
|
+
selectedLenses: [],
|
|
1292
|
+
changedFiles: 1,
|
|
1293
|
+
changedLines: 1,
|
|
1294
|
+
correctionBudget: 1,
|
|
1295
|
+
action: "created",
|
|
1296
|
+
lensesRequired: false,
|
|
1297
|
+
riskReasons: [],
|
|
1298
|
+
raw: {},
|
|
1299
|
+
};
|
|
1300
|
+
},
|
|
1301
|
+
} as unknown as NativeReviewCli;
|
|
1302
|
+
const resolved = await __testing.executeReviewControllerOperation(
|
|
1303
|
+
{
|
|
1304
|
+
operation: "inspect",
|
|
1305
|
+
untrackedScope: "select",
|
|
1306
|
+
intendedUntracked: [eligible],
|
|
1307
|
+
},
|
|
1308
|
+
cwd,
|
|
1309
|
+
native,
|
|
1310
|
+
undefined,
|
|
1311
|
+
undefined,
|
|
1312
|
+
undefined,
|
|
1313
|
+
retained,
|
|
1314
|
+
);
|
|
1315
|
+
assert.equal(resolved.status, "ready");
|
|
1316
|
+
const started = await __testing.executeReviewControllerOperation(
|
|
1317
|
+
{ operation: "start", input: JSON.stringify({ mode: "ordinary" }) },
|
|
1318
|
+
cwd,
|
|
1319
|
+
native,
|
|
1320
|
+
undefined,
|
|
1321
|
+
undefined,
|
|
1322
|
+
undefined,
|
|
1323
|
+
retained,
|
|
1324
|
+
);
|
|
1325
|
+
assert.equal(started.operation, "start");
|
|
1326
|
+
assert.equal(started.result.lineage_id, "review-started");
|
|
1327
|
+
assert.equal(starts.length, 1);
|
|
1328
|
+
assert.equal(requests.length, 3);
|
|
1329
|
+
const startStatusRequest = requests[2]!;
|
|
1330
|
+
assert.equal(startStatusRequest.untrackedScope, "select");
|
|
1331
|
+
assert.equal(startStatusRequest.expectedUntrackedInventory, SHA);
|
|
1332
|
+
assert.deepEqual(startStatusRequest.intendedUntracked, [eligible]);
|
|
1333
|
+
assert.deepEqual(startStatusRequest.intendedUntrackedSelection, {
|
|
1334
|
+
argumentTokens: selection.submission!.argumentTokens,
|
|
1335
|
+
value: JSON.stringify({
|
|
1336
|
+
schema: "gentle-ai.review-intended-untracked-selection/v1",
|
|
1337
|
+
untracked_scope: "select",
|
|
1338
|
+
expected_untracked_inventory: SHA,
|
|
1339
|
+
intended_untracked: [eligible],
|
|
1340
|
+
}),
|
|
1341
|
+
});
|
|
1342
|
+
assert.equal(starts[0]!.untrackedScope, "select");
|
|
1343
|
+
assert.equal(retained.has(`${cwd}\u0000`), false);
|
|
1344
|
+
const lineageEntry = retained.get(`${cwd}\u0000review-started`) as
|
|
1345
|
+
| { untrackedScope: string; submission?: unknown }
|
|
1346
|
+
| undefined;
|
|
1347
|
+
assert.notEqual(lineageEntry, undefined);
|
|
1348
|
+
assert.equal(lineageEntry!.untrackedScope, "select");
|
|
1349
|
+
assert.notEqual(lineageEntry!.submission, undefined);
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
test("untrackedScope is accepted only by the inspect operation", async (t) => {
|
|
1353
|
+
const { cwd, initial } = untrackedStopFixture(t);
|
|
1354
|
+
const native = {
|
|
1355
|
+
targetStatus: async () => initial,
|
|
1356
|
+
} as unknown as NativeReviewCli;
|
|
1357
|
+
await assert.rejects(
|
|
1358
|
+
() =>
|
|
1359
|
+
__testing.executeReviewControllerOperation(
|
|
1360
|
+
{ operation: "status", untrackedScope: "exclude" } as never,
|
|
1361
|
+
cwd,
|
|
1362
|
+
native,
|
|
1363
|
+
),
|
|
1364
|
+
/does not accept untrackedScope/,
|
|
1365
|
+
);
|
|
1366
|
+
await assert.rejects(
|
|
1367
|
+
() =>
|
|
1368
|
+
__testing.executeReviewControllerOperation(
|
|
1369
|
+
{
|
|
1370
|
+
operation: "start",
|
|
1371
|
+
input: JSON.stringify({ mode: "ordinary" }),
|
|
1372
|
+
untrackedScope: "exclude",
|
|
1373
|
+
} as never,
|
|
1374
|
+
cwd,
|
|
1375
|
+
native,
|
|
1376
|
+
),
|
|
1377
|
+
/does not accept untrackedScope/,
|
|
1378
|
+
);
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
test("top-level intendedUntracked is accepted only by inspect select", async (t) => {
|
|
1382
|
+
const { cwd, initial } = untrackedStopFixture(t);
|
|
1383
|
+
const native = { targetStatus: async () => initial } as unknown as NativeReviewCli;
|
|
1384
|
+
for (const parameters of [
|
|
1385
|
+
{ operation: "inspect", intendedUntracked: [] },
|
|
1386
|
+
{ operation: "inspect", untrackedScope: "exclude", intendedUntracked: [] },
|
|
1387
|
+
{ operation: "status", intendedUntracked: [] },
|
|
1388
|
+
{ operation: "start", input: JSON.stringify({ mode: "ordinary" }), intendedUntracked: [] },
|
|
1389
|
+
] as const) {
|
|
1390
|
+
await assert.rejects(
|
|
1391
|
+
() => __testing.executeReviewControllerOperation(parameters as never, cwd, native),
|
|
1392
|
+
/intendedUntracked/,
|
|
1393
|
+
JSON.stringify(parameters),
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
test("a fresh unsuccessful inspect invalidates its prior pre-lineage selection", async (t) => {
|
|
1399
|
+
const { cwd, eligible, initial, target } = untrackedStopFixture(t);
|
|
1400
|
+
const retained = new Map();
|
|
1401
|
+
let failInspect = false;
|
|
1402
|
+
const native = {
|
|
1403
|
+
targetStatus: async (request: Record<string, unknown>) => {
|
|
1404
|
+
if (failInspect) throw new Error("inspect unavailable");
|
|
1405
|
+
return "intendedUntrackedSelection" in request ? target : initial;
|
|
1406
|
+
},
|
|
1407
|
+
} as unknown as NativeReviewCli;
|
|
1408
|
+
await __testing.executeReviewControllerOperation(
|
|
1409
|
+
{ operation: "inspect", untrackedScope: "select", intendedUntracked: [eligible] },
|
|
1410
|
+
cwd, native, undefined, undefined, undefined, retained,
|
|
1411
|
+
);
|
|
1412
|
+
assert.equal(retained.has(`${cwd}\u0000`), true);
|
|
1413
|
+
failInspect = true;
|
|
1414
|
+
const failed = await __testing.executeReviewControllerOperation(
|
|
1415
|
+
{ operation: "inspect" }, cwd, native, undefined, undefined, undefined, retained,
|
|
1416
|
+
);
|
|
1417
|
+
assert.equal(failed.outcome, "native-status-unavailable");
|
|
1418
|
+
assert.equal(retained.has(`${cwd}\u0000`), false);
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
test("START rejects a retained pre-lineage selection when fresh STATUS identifies a changed candidate", async (t) => {
|
|
1422
|
+
const { cwd, eligible, initial, target } = untrackedStopFixture(t);
|
|
1423
|
+
const retained = new Map();
|
|
1424
|
+
const candidateA = {
|
|
1425
|
+
...target,
|
|
1426
|
+
targetIdentity: "target-a",
|
|
1427
|
+
projection: { ...target.projection, currentCandidateTree: "candidate-a" },
|
|
1428
|
+
} as ReviewStatusV3;
|
|
1429
|
+
const candidateB = {
|
|
1430
|
+
...target,
|
|
1431
|
+
targetIdentity: "target-b",
|
|
1432
|
+
projection: { ...target.projection, currentCandidateTree: "candidate-b" },
|
|
1433
|
+
} as ReviewStatusV3;
|
|
1434
|
+
let resolved = false;
|
|
1435
|
+
let starts = 0;
|
|
1436
|
+
const native = {
|
|
1437
|
+
targetStatus: async (request: Record<string, unknown>) => {
|
|
1438
|
+
if (!("intendedUntrackedSelection" in request)) return initial;
|
|
1439
|
+
if (!resolved) { resolved = true; return candidateA; }
|
|
1440
|
+
return candidateB;
|
|
1441
|
+
},
|
|
1442
|
+
start: async () => {
|
|
1443
|
+
starts += 1;
|
|
1444
|
+
return { lineageId: "unexpected", state: "reviewing", riskLevel: "low", selectedLenses: [], changedFiles: 1, changedLines: 1, correctionBudget: 1, action: "created", lensesRequired: false, riskReasons: [], raw: {} };
|
|
1445
|
+
},
|
|
1446
|
+
} as unknown as NativeReviewCli;
|
|
1447
|
+
await __testing.executeReviewControllerOperation(
|
|
1448
|
+
{ operation: "inspect", untrackedScope: "select", intendedUntracked: [eligible] },
|
|
1449
|
+
cwd, native, undefined, null, undefined, retained,
|
|
1450
|
+
);
|
|
1451
|
+
const rejected = await __testing.executeReviewControllerOperation(
|
|
1452
|
+
{ operation: "start", input: JSON.stringify({ mode: "ordinary" }) },
|
|
1453
|
+
cwd, native, undefined, null, undefined, retained,
|
|
1454
|
+
);
|
|
1455
|
+
assert.equal(rejected.outcome, "native-start-retained-selection-candidate-mismatch");
|
|
1456
|
+
assert.equal(starts, 0);
|
|
1457
|
+
assert.equal(retained.has(`${cwd}\u0000`), false);
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
test("a failed START retains a pre-lineage selection for a same-candidate retry", async (t) => {
|
|
1461
|
+
const { cwd, eligible, initial, target } = untrackedStopFixture(t);
|
|
1462
|
+
const retained = new Map();
|
|
1463
|
+
let starts = 0;
|
|
1464
|
+
const native = {
|
|
1465
|
+
targetStatus: async (request: Record<string, unknown>) =>
|
|
1466
|
+
"intendedUntrackedSelection" in request ? target : initial,
|
|
1467
|
+
start: async () => {
|
|
1468
|
+
starts += 1;
|
|
1469
|
+
if (starts === 1) throw Object.assign(new Error("retryable failure"), { mutationOutcome: "none" });
|
|
1470
|
+
return { lineageId: "retried", state: "reviewing", riskLevel: "low", selectedLenses: [], changedFiles: 1, changedLines: 1, correctionBudget: 1, action: "created", lensesRequired: false, riskReasons: [], raw: {} };
|
|
1471
|
+
},
|
|
1472
|
+
} as unknown as NativeReviewCli;
|
|
1473
|
+
await __testing.executeReviewControllerOperation(
|
|
1474
|
+
{ operation: "inspect", untrackedScope: "select", intendedUntracked: [eligible] },
|
|
1475
|
+
cwd, native, undefined, null, undefined, retained,
|
|
1476
|
+
);
|
|
1477
|
+
const failed = await __testing.executeReviewControllerOperation(
|
|
1478
|
+
{ operation: "start", input: JSON.stringify({ mode: "ordinary" }) },
|
|
1479
|
+
cwd, native, undefined, null, undefined, retained,
|
|
1480
|
+
);
|
|
1481
|
+
assert.equal(failed.mutation_outcome, "none");
|
|
1482
|
+
assert.equal(retained.has(`${cwd}\u0000`), true);
|
|
1483
|
+
const retried = await __testing.executeReviewControllerOperation(
|
|
1484
|
+
{ operation: "start", input: JSON.stringify({ mode: "ordinary" }) },
|
|
1485
|
+
cwd, native, undefined, null, undefined, retained,
|
|
1486
|
+
);
|
|
1487
|
+
assert.equal(retried.result.lineage_id, "retried");
|
|
1488
|
+
assert.equal(starts, 2);
|
|
1489
|
+
assert.equal(retained.has(`${cwd}\u0000`), false);
|
|
1490
|
+
});
|
|
1491
|
+
|
|
956
1492
|
test("ordinary START relays native consent without authoring or advancing it", async (t) => {
|
|
957
1493
|
const cwd = repository(t);
|
|
958
1494
|
const consent = decodeReviewConsentV3(JSON.parse(readFileSync(join(process.cwd(), "tests", "fixtures", "devbinary", "consent-v3.captured.json"), "utf8")));
|
|
@@ -1003,6 +1539,24 @@ test("STATUS preserves a native process failure without inventing authority reco
|
|
|
1003
1539
|
assert.equal(result.mutation_outcome, "none");
|
|
1004
1540
|
});
|
|
1005
1541
|
|
|
1542
|
+
test("foreign native errors with malformed diagnostics remain untrusted", async () => {
|
|
1543
|
+
const native = {
|
|
1544
|
+
targetStatus: async () => { throw {
|
|
1545
|
+
name: "NativeReviewCliError",
|
|
1546
|
+
code: NATIVE_REVIEW_ERROR_CODE.NON_ZERO,
|
|
1547
|
+
diagnostics: {
|
|
1548
|
+
operation: "review/status",
|
|
1549
|
+
error_code: "forged-code",
|
|
1550
|
+
timed_out: false,
|
|
1551
|
+
output_limit_exceeded: false,
|
|
1552
|
+
},
|
|
1553
|
+
}; },
|
|
1554
|
+
} as unknown as NativeReviewCli;
|
|
1555
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "status" }, process.cwd(), native);
|
|
1556
|
+
assert.equal(result.outcome, "native-operation-failed");
|
|
1557
|
+
assert.equal("diagnostics" in result, false);
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1006
1560
|
test("STATUS preserves ambiguous native status as read-only provider-owned state", async () => {
|
|
1007
1561
|
const lineageId = "ambiguous-lineage";
|
|
1008
1562
|
const native = {
|
|
@@ -1382,13 +1936,17 @@ test("START and consent ambiguity reconciliation register their returned committ
|
|
|
1382
1936
|
const lineageId = "reconciled-collect", input = correctionPlanInput(lineageId), unknown = () => { throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.NON_ZERO, "review/start", true, true, "unknown mutation"); };
|
|
1383
1937
|
const selectors = (requests: readonly Record<string, unknown>[]) => requests.map(({ baseRef: base, committedOnly }) => ({ baseRef: base, committedOnly }));
|
|
1384
1938
|
const directRequests: Array<Record<string, unknown>> = [], directRoutes = new Map();
|
|
1939
|
+
let directStartCalls = 0;
|
|
1385
1940
|
const directNative = {
|
|
1386
1941
|
targetStatus: async (request: Record<string, unknown>) => { directRequests.push(request); return directRequests.length === 1 ? startStatus(cwd, baseRef) : status(lineageId, [input]); },
|
|
1387
|
-
start: unknown,
|
|
1942
|
+
start: () => { directStartCalls += 1; return unknown(); },
|
|
1388
1943
|
captureCorrectionPlan: async () => ({ schema: "gentle-ai.review-last-event-closure/v1", operation: "review.capture-correction-plan", lineageId, state: "correction_required", storeRevision: SHA }),
|
|
1389
1944
|
} as unknown as NativeReviewCli;
|
|
1390
|
-
await __testing.executeReviewControllerOperation({ operation: "start", input: JSON.stringify({ mode: "ordinary", baseRef, committedOnly: true }) }, cwd, directNative, undefined, undefined, undefined, directRoutes);
|
|
1391
|
-
|
|
1945
|
+
const directStart = await __testing.executeReviewControllerOperation({ operation: "start", input: JSON.stringify({ mode: "ordinary", baseRef, committedOnly: true }) }, cwd, directNative, undefined, undefined, undefined, directRoutes);
|
|
1946
|
+
assert.deepEqual({ outcome: directStart.outcome, status: directStart.status, mutationOutcome: directStart.mutation_outcome, startCalls: directStartCalls, statusCalls: directRequests.length }, { outcome: "native-mutation-status-reconciled", status: "blocked", mutationOutcome: "unknown", startCalls: 1, statusCalls: 2 });
|
|
1947
|
+
assert.equal((directStart.diagnostics as { error_code?: string }).error_code, NATIVE_REVIEW_ERROR_CODE.NON_ZERO);
|
|
1948
|
+
assert.equal("next_action" in directStart, false);
|
|
1949
|
+
const directCapture = await __testing.executeReviewCaptureOperation({ lineageId, collectBinding: bindingOf(directStart), correctionLines: 1 }, cwd, directNative, undefined, undefined, directRoutes, true);
|
|
1392
1950
|
const consent = decodeReviewConsentV3(JSON.parse(readFileSync(join(process.cwd(), "tests", "fixtures", "devbinary", "consent-v3.captured.json"), "utf8")));
|
|
1393
1951
|
const consentRequests: Array<Record<string, unknown>> = [], consentRoutes = new Map(), registry = new PendingReviewConsentRegistry(), session = Symbol("consent-session");
|
|
1394
1952
|
const consentNative = {
|
|
@@ -1403,9 +1961,11 @@ test("START and consent ambiguity reconciliation register their returned committ
|
|
|
1403
1961
|
const consentCapture = await __testing.executeReviewCaptureOperation({ lineageId, collectBinding: JSON.stringify(input), correctionLines: 1 }, cwd, consentNative, undefined, undefined, consentRoutes, true);
|
|
1404
1962
|
assert.deepEqual({
|
|
1405
1963
|
outcomes: [directCapture.outcome, consentCapture.outcome],
|
|
1964
|
+
directStartCalls,
|
|
1406
1965
|
selectors: [selectors(directRequests), selectors(consentRequests)],
|
|
1407
1966
|
}, {
|
|
1408
1967
|
outcomes: ["native-last-event-closure", "native-last-event-closure"],
|
|
1968
|
+
directStartCalls: 1,
|
|
1409
1969
|
selectors: [Array.from({ length: 3 }, () => ({ baseRef, committedOnly: true })), Array.from({ length: 3 }, () => ({ baseRef, committedOnly: true }))],
|
|
1410
1970
|
});
|
|
1411
1971
|
});
|
|
@@ -208,7 +208,7 @@ test("controller SDD status treats removed OpenSpec recovery authority and delet
|
|
|
208
208
|
const status = await __testing.resolveControllerSddStatus(fixture.repository, changeName, false, "openspec");
|
|
209
209
|
|
|
210
210
|
assert.equal(status.dependencies.archive, "blocked");
|
|
211
|
-
assert.equal(status.nextRecommended, "
|
|
211
|
+
assert.equal(status.nextRecommended, "blocked");
|
|
212
212
|
assert.match(status.blockedReasons.join("\n"), /active change not found/i);
|
|
213
213
|
});
|
|
214
214
|
|