@lsctech/polaris 0.5.8 → 0.5.10
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/dist/autoresearch/sol-run-health-bridge.js +246 -0
- package/dist/cli/adopt-canon.js +16 -5
- package/dist/cli/adopt-cognition.js +23 -8
- package/dist/cli/medic.js +65 -0
- package/dist/cognition/closeout-librarian-types.js +53 -0
- package/dist/cognition/librarian-packet.js +25 -1
- package/dist/cognition/validate.js +85 -3
- package/dist/config/defaults.js +4 -0
- package/dist/config/validator.js +141 -0
- package/dist/finalize/index.js +224 -5
- package/dist/finalize/medic-gate.js +42 -0
- package/dist/loop/adapters/terminal-cli.js +3 -2
- package/dist/loop/parent.js +208 -1
- package/dist/loop/worker-packet.js +19 -1
- package/dist/medic/run-health-consult.js +381 -0
- package/dist/medic/treatment-packets.js +169 -0
- package/dist/qc/providers/coderabbit.js +90 -18
- package/dist/qc/repair-loop.js +108 -28
- package/dist/qc/runner.js +7 -1
- package/dist/run-health/foreman-symptoms.js +100 -0
- package/dist/run-health/index.js +301 -0
- package/dist/run-health/qc-escalation.js +155 -0
- package/dist/run-health/schema.js +123 -0
- package/dist/smartdocs-engine/validate-instructions.js +60 -2
- package/package.json +1 -1
package/dist/loop/parent.js
CHANGED
|
@@ -48,6 +48,11 @@ const child_selector_js_1 = require("../runtime/scheduling/child-selector.js");
|
|
|
48
48
|
const index_js_3 = require("../tracker/index.js");
|
|
49
49
|
const lifecycle_transition_js_1 = require("../tracker/lifecycle-transition.js");
|
|
50
50
|
const local_graph_js_1 = require("../tracker/local-graph.js");
|
|
51
|
+
const index_js_4 = require("../run-health/index.js");
|
|
52
|
+
const foreman_symptoms_js_1 = require("../run-health/foreman-symptoms.js");
|
|
53
|
+
const qc_escalation_js_1 = require("../run-health/qc-escalation.js");
|
|
54
|
+
const run_health_consult_js_1 = require("../medic/run-health-consult.js");
|
|
55
|
+
const treatment_packets_js_1 = require("../medic/treatment-packets.js");
|
|
51
56
|
const CLAIM_TTL_MS = 30 * 60 * 1000;
|
|
52
57
|
/**
|
|
53
58
|
* Returns the list of files touched by a git commit, or null when the commit
|
|
@@ -916,16 +921,35 @@ async function runParentLoop(options) {
|
|
|
916
921
|
error: msg,
|
|
917
922
|
timestamp: new Date().toISOString(),
|
|
918
923
|
});
|
|
924
|
+
(0, foreman_symptoms_js_1.appendForemanSymptom)({
|
|
925
|
+
runId: state.run_id,
|
|
926
|
+
clusterId: state.cluster_id,
|
|
927
|
+
code: "foreman-qc-runtime-failure",
|
|
928
|
+
message: `QC run at completed-cluster trigger threw a runtime error: ${msg}`,
|
|
929
|
+
evidenceRefs: [telemetryFile],
|
|
930
|
+
repoRoot,
|
|
931
|
+
config,
|
|
932
|
+
});
|
|
919
933
|
// Non-fatal: proceed to cluster-complete without repair loop.
|
|
920
934
|
initialQcResult = null;
|
|
921
935
|
}
|
|
922
936
|
const hasFindings = initialQcResult !== null &&
|
|
923
937
|
initialQcResult.results.some((r) => r.findings.length > 0 && r.status !== "passed" && r.status !== "skipped");
|
|
938
|
+
// Append QC escalation symptoms for initial findings (blocking / parse failures / etc.)
|
|
939
|
+
if (initialQcResult !== null && initialQcResult.results.length > 0) {
|
|
940
|
+
(0, qc_escalation_js_1.appendQcEscalationSymptoms)({
|
|
941
|
+
runId: state.run_id,
|
|
942
|
+
clusterId: state.cluster_id,
|
|
943
|
+
qcResults: initialQcResult.results,
|
|
944
|
+
afterRepair: false,
|
|
945
|
+
repoRoot,
|
|
946
|
+
});
|
|
947
|
+
}
|
|
924
948
|
if (initialQcResult !== null && hasFindings) {
|
|
925
949
|
const maxRepairRounds = config.qc.maxRepairRounds ?? repair_loop_js_1.DEFAULT_MAX_REPAIR_ROUNDS;
|
|
926
950
|
const priorLoopState = state.qc_repair_loop ?? null;
|
|
927
951
|
// Build the repair worker dispatcher using the existing adapter + provider.
|
|
928
|
-
const repairDispatcher = async (packet, round, manifest) => {
|
|
952
|
+
const repairDispatcher = async (packet, round, manifest, signal) => {
|
|
929
953
|
const repairPacketId = packet.packetId;
|
|
930
954
|
const dispatchId = (0, node_crypto_1.randomUUID)();
|
|
931
955
|
const repairWorkerId = `${state.run_id}:repair-${repairPacketId}:${Date.now()}`;
|
|
@@ -955,6 +979,12 @@ async function runParentLoop(options) {
|
|
|
955
979
|
timestamp: new Date().toISOString(),
|
|
956
980
|
});
|
|
957
981
|
try {
|
|
982
|
+
// TODO: Propagate AbortSignal to adapter.dispatch() once ExecutionAdapter
|
|
983
|
+
// interface supports cancellation. Current limitation: timeout abandons the
|
|
984
|
+
// Promise but doesn't terminate the spawned worker process.
|
|
985
|
+
if (signal?.aborted) {
|
|
986
|
+
throw signal.reason || new Error("Repair dispatch aborted");
|
|
987
|
+
}
|
|
958
988
|
const dispatchResult = await adapter.dispatch(workerPacket, { provider: providerName, dryRun });
|
|
959
989
|
const workerSummary = parseWorkerSummary(dispatchResult.summary);
|
|
960
990
|
const success = workerSummary?.status === "done";
|
|
@@ -1021,6 +1051,22 @@ async function runParentLoop(options) {
|
|
|
1021
1051
|
summary: repairLoopResult.summary,
|
|
1022
1052
|
timestamp: new Date().toISOString(),
|
|
1023
1053
|
});
|
|
1054
|
+
// Append QC escalation symptoms for non-passing outcomes and post-repair findings.
|
|
1055
|
+
(0, qc_escalation_js_1.appendRepairLoopOutcomeSymptom)({
|
|
1056
|
+
runId: state.run_id,
|
|
1057
|
+
clusterId: state.cluster_id,
|
|
1058
|
+
repairResult: repairLoopResult,
|
|
1059
|
+
repoRoot,
|
|
1060
|
+
});
|
|
1061
|
+
if (repairLoopResult.final_qc_results.length > 0 && repairLoopResult.rounds_completed > 0) {
|
|
1062
|
+
(0, qc_escalation_js_1.appendQcEscalationSymptoms)({
|
|
1063
|
+
runId: state.run_id,
|
|
1064
|
+
clusterId: state.cluster_id,
|
|
1065
|
+
qcResults: repairLoopResult.final_qc_results,
|
|
1066
|
+
afterRepair: true,
|
|
1067
|
+
repoRoot,
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1024
1070
|
}
|
|
1025
1071
|
catch (err) {
|
|
1026
1072
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1030,11 +1076,90 @@ async function runParentLoop(options) {
|
|
|
1030
1076
|
error: msg,
|
|
1031
1077
|
timestamp: new Date().toISOString(),
|
|
1032
1078
|
});
|
|
1079
|
+
(0, foreman_symptoms_js_1.appendForemanSymptom)({
|
|
1080
|
+
runId: state.run_id,
|
|
1081
|
+
clusterId: state.cluster_id,
|
|
1082
|
+
code: "foreman-qc-runtime-failure",
|
|
1083
|
+
message: `QC repair loop threw a runtime error: ${msg}`,
|
|
1084
|
+
evidenceRefs: [telemetryFile],
|
|
1085
|
+
repoRoot,
|
|
1086
|
+
config,
|
|
1087
|
+
});
|
|
1033
1088
|
// Non-fatal: proceed to cluster-complete.
|
|
1034
1089
|
}
|
|
1035
1090
|
}
|
|
1036
1091
|
}
|
|
1037
1092
|
// ── End QC repair loop ─────────────────────────────────────────────────
|
|
1093
|
+
// ── Run-health Medic consult ────────────────────────────────────────────
|
|
1094
|
+
// If the run recorded health symptoms and no Medic decision exists yet,
|
|
1095
|
+
// dispatch Medic to diagnose and optionally emit bounded treatment packets.
|
|
1096
|
+
if (!dryRun) {
|
|
1097
|
+
const runHealthReport = (0, index_js_4.readRunHealthReport)(state.run_id, repoRoot);
|
|
1098
|
+
if (runHealthReport && !(0, index_js_4.isMedicGateSatisfied)(runHealthReport)) {
|
|
1099
|
+
const maxTreatmentRounds = config.qc?.maxRepairRounds ?? repair_loop_js_1.DEFAULT_MAX_REPAIR_ROUNDS;
|
|
1100
|
+
const medicDispatchId = (0, node_crypto_1.randomUUID)();
|
|
1101
|
+
const medicResultPath = (0, node_path_1.join)(repoRoot, ".polaris", "clusters", state.cluster_id, "results", `medic-run-health-${medicDispatchId}.json`);
|
|
1102
|
+
const medicPacket = {
|
|
1103
|
+
role: "medic-run-health",
|
|
1104
|
+
run_id: state.run_id,
|
|
1105
|
+
dispatch_id: medicDispatchId,
|
|
1106
|
+
cluster_id: state.cluster_id,
|
|
1107
|
+
run_health_report_path: (0, index_js_4.getRunHealthReportPath)(state.run_id, repoRoot),
|
|
1108
|
+
qc_artifact_refs: Array.from(new Set([
|
|
1109
|
+
...runHealthReport.evidence_refs,
|
|
1110
|
+
...runHealthReport.symptoms.flatMap((s) => s.evidence_refs),
|
|
1111
|
+
])),
|
|
1112
|
+
telemetry_path: telemetryFile,
|
|
1113
|
+
cluster_state_path: (0, node_path_1.join)(repoRoot, ".polaris", "clusters", state.cluster_id, "state.json"),
|
|
1114
|
+
policy_limits: { max_treatment_rounds: maxTreatmentRounds },
|
|
1115
|
+
result_path: medicResultPath,
|
|
1116
|
+
allowed_write_paths: [
|
|
1117
|
+
"smartdocs/medic/charts",
|
|
1118
|
+
".polaris/runs",
|
|
1119
|
+
".polaris/clusters",
|
|
1120
|
+
".taskchain_artifacts",
|
|
1121
|
+
],
|
|
1122
|
+
prohibited_write_paths: [],
|
|
1123
|
+
};
|
|
1124
|
+
try {
|
|
1125
|
+
await (0, run_health_consult_js_1.runMedicRunHealthConsult)({
|
|
1126
|
+
packet: medicPacket,
|
|
1127
|
+
repoRoot,
|
|
1128
|
+
stateFile,
|
|
1129
|
+
telemetryFile,
|
|
1130
|
+
branch: state.branch ?? getCurrentBranch(repoRoot),
|
|
1131
|
+
validationCommands: ["npm run build", "npm test"],
|
|
1132
|
+
maxConcurrentWorkers,
|
|
1133
|
+
dryRun,
|
|
1134
|
+
dispatchTreatmentWorkerFn: (input) => (0, treatment_packets_js_1.dispatchTreatmentWorker)({
|
|
1135
|
+
...input,
|
|
1136
|
+
repoRoot,
|
|
1137
|
+
dispatch: (workerPacket) => adapter.dispatch(workerPacket, { provider: providerName }),
|
|
1138
|
+
}),
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
catch (err) {
|
|
1142
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1143
|
+
appendTelemetry(telemetryFile, {
|
|
1144
|
+
event: "medic-run-health-consult-error",
|
|
1145
|
+
run_id: state.run_id,
|
|
1146
|
+
cluster_id: state.cluster_id,
|
|
1147
|
+
error: msg,
|
|
1148
|
+
timestamp: new Date().toISOString(),
|
|
1149
|
+
});
|
|
1150
|
+
(0, foreman_symptoms_js_1.appendForemanSymptom)({
|
|
1151
|
+
runId: state.run_id,
|
|
1152
|
+
clusterId: state.cluster_id,
|
|
1153
|
+
code: "foreman-medic-runtime-failure",
|
|
1154
|
+
message: `Medic run-health consult threw a runtime error: ${msg}`,
|
|
1155
|
+
evidenceRefs: [telemetryFile],
|
|
1156
|
+
repoRoot,
|
|
1157
|
+
config,
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
// ── End run-health Medic consult ───────────────────────────────────────
|
|
1038
1163
|
// All children completed — write final state and halt
|
|
1039
1164
|
if (!dryRun) {
|
|
1040
1165
|
logStatus(notificationFormat, "COMPLETE");
|
|
@@ -1143,6 +1268,15 @@ async function runParentLoop(options) {
|
|
|
1143
1268
|
reason: msg,
|
|
1144
1269
|
timestamp: new Date().toISOString(),
|
|
1145
1270
|
});
|
|
1271
|
+
(0, foreman_symptoms_js_1.appendForemanSymptom)({
|
|
1272
|
+
runId: state.run_id,
|
|
1273
|
+
clusterId: state.cluster_id,
|
|
1274
|
+
code: "foreman-dispatch-boundary-repair",
|
|
1275
|
+
message: `Dispatch boundary violation: ${msg}`,
|
|
1276
|
+
evidenceRefs: [telemetryFile],
|
|
1277
|
+
repoRoot,
|
|
1278
|
+
config,
|
|
1279
|
+
});
|
|
1146
1280
|
}
|
|
1147
1281
|
return {
|
|
1148
1282
|
haltReason: 'worker-error',
|
|
@@ -1502,6 +1636,43 @@ async function runParentLoop(options) {
|
|
|
1502
1636
|
sealedResult['status'] = 'failed';
|
|
1503
1637
|
}
|
|
1504
1638
|
finalWorkerSummary = sealedResult;
|
|
1639
|
+
// ── Ingest worker-reported run-health symptoms ────────────────────────
|
|
1640
|
+
// Only process when worker reports at least one symptom. Errors are
|
|
1641
|
+
// logged to telemetry but never block the run — symptom reporting is
|
|
1642
|
+
// advisory, not a gate.
|
|
1643
|
+
const rawSymptoms = sealedResult['run_health_symptoms'];
|
|
1644
|
+
if (!dryRun && Array.isArray(rawSymptoms) && rawSymptoms.length > 0) {
|
|
1645
|
+
const dispatchRecord = state.open_children_meta?.[nextChild]?.dispatch_record;
|
|
1646
|
+
try {
|
|
1647
|
+
(0, index_js_4.upsertWorkerSymptoms)({
|
|
1648
|
+
runId: state.run_id,
|
|
1649
|
+
clusterId: state.cluster_id,
|
|
1650
|
+
childId: nextChild,
|
|
1651
|
+
workerId: dispatchRecord?.worker_id,
|
|
1652
|
+
provider: dispatchRecord?.provider,
|
|
1653
|
+
symptoms: rawSymptoms,
|
|
1654
|
+
repoRoot,
|
|
1655
|
+
});
|
|
1656
|
+
appendTelemetry(telemetryFile, {
|
|
1657
|
+
event: "run-health-symptoms-ingested",
|
|
1658
|
+
run_id: state.run_id,
|
|
1659
|
+
child_id: nextChild,
|
|
1660
|
+
symptom_count: rawSymptoms.length,
|
|
1661
|
+
timestamp: new Date().toISOString(),
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
catch (symptomErr) {
|
|
1665
|
+
const symptomMsg = symptomErr instanceof Error ? symptomErr.message : String(symptomErr);
|
|
1666
|
+
appendTelemetry(telemetryFile, {
|
|
1667
|
+
event: "run-health-ingest-error",
|
|
1668
|
+
run_id: state.run_id,
|
|
1669
|
+
child_id: nextChild,
|
|
1670
|
+
error: symptomMsg,
|
|
1671
|
+
timestamp: new Date().toISOString(),
|
|
1672
|
+
});
|
|
1673
|
+
// ponytail: consider surfacing ingest errors to operator via logStatus in a future pass
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1505
1676
|
}
|
|
1506
1677
|
catch (error) {
|
|
1507
1678
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -1514,6 +1685,15 @@ async function runParentLoop(options) {
|
|
|
1514
1685
|
result_file: packet.result_file_contract.result_file,
|
|
1515
1686
|
timestamp: new Date().toISOString(),
|
|
1516
1687
|
});
|
|
1688
|
+
(0, foreman_symptoms_js_1.appendForemanSymptom)({
|
|
1689
|
+
runId: state.run_id,
|
|
1690
|
+
clusterId: state.cluster_id,
|
|
1691
|
+
code: "foreman-packet-repair",
|
|
1692
|
+
message: `Failed to read sealed result file for ${nextChild}: ${msg}`,
|
|
1693
|
+
evidenceRefs: [telemetryFile],
|
|
1694
|
+
repoRoot,
|
|
1695
|
+
config,
|
|
1696
|
+
});
|
|
1517
1697
|
}
|
|
1518
1698
|
return {
|
|
1519
1699
|
haltReason: 'worker-error',
|
|
@@ -1540,6 +1720,15 @@ async function runParentLoop(options) {
|
|
|
1540
1720
|
error: errMsg,
|
|
1541
1721
|
timestamp: new Date().toISOString(),
|
|
1542
1722
|
});
|
|
1723
|
+
(0, foreman_symptoms_js_1.appendForemanSymptom)({
|
|
1724
|
+
runId: state.run_id,
|
|
1725
|
+
clusterId: state.cluster_id,
|
|
1726
|
+
code: "foreman-wrong-run-telemetry",
|
|
1727
|
+
message: errMsg,
|
|
1728
|
+
evidenceRefs: [telemetryFile],
|
|
1729
|
+
repoRoot,
|
|
1730
|
+
config,
|
|
1731
|
+
});
|
|
1543
1732
|
}
|
|
1544
1733
|
return {
|
|
1545
1734
|
haltReason: 'worker-error',
|
|
@@ -1709,6 +1898,15 @@ async function runParentLoop(options) {
|
|
|
1709
1898
|
error: errMsg,
|
|
1710
1899
|
timestamp: new Date().toISOString(),
|
|
1711
1900
|
});
|
|
1901
|
+
(0, foreman_symptoms_js_1.appendForemanSymptom)({
|
|
1902
|
+
runId: state.run_id,
|
|
1903
|
+
clusterId: state.cluster_id,
|
|
1904
|
+
code: "foreman-state-repair",
|
|
1905
|
+
message: `State file validation failed after worker execution; using pre-dispatch fallback: ${errMsg}`,
|
|
1906
|
+
evidenceRefs: [telemetryFile],
|
|
1907
|
+
repoRoot,
|
|
1908
|
+
config,
|
|
1909
|
+
});
|
|
1712
1910
|
state = stateBeforeDispatch;
|
|
1713
1911
|
}
|
|
1714
1912
|
else if (!dryRun) {
|
|
@@ -1747,6 +1945,15 @@ async function runParentLoop(options) {
|
|
|
1747
1945
|
error: errMsg,
|
|
1748
1946
|
timestamp: new Date().toISOString(),
|
|
1749
1947
|
});
|
|
1948
|
+
(0, foreman_symptoms_js_1.appendForemanSymptom)({
|
|
1949
|
+
runId: state.run_id,
|
|
1950
|
+
clusterId: state.cluster_id,
|
|
1951
|
+
code: "foreman-state-repair",
|
|
1952
|
+
message: `State reload threw after worker execution; using pre-dispatch fallback: ${errMsg}`,
|
|
1953
|
+
evidenceRefs: [telemetryFile],
|
|
1954
|
+
repoRoot,
|
|
1955
|
+
config,
|
|
1956
|
+
});
|
|
1750
1957
|
state = stateBeforeDispatch;
|
|
1751
1958
|
}
|
|
1752
1959
|
else if (!dryRun) {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* accept BootstrapPacket transparently accept WorkerPacket.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.REPAIR_RETURN_CONTRACT = exports.STARTUP_RETURN_CONTRACT = exports.PREFLIGHT_RETURN_CONTRACT = exports.WORKER_PROHIBITED_WRITE_PATHS = exports.FINALIZE_RETURN_CONTRACT = exports.IMPL_RETURN_CONTRACT = void 0;
|
|
14
|
+
exports.REPAIR_RETURN_CONTRACT = exports.STARTUP_RETURN_CONTRACT = exports.PREFLIGHT_RETURN_CONTRACT = exports.WORKER_SYMPTOM_CATEGORIES = exports.WORKER_PROHIBITED_WRITE_PATHS = exports.FINALIZE_RETURN_CONTRACT = exports.IMPL_RETURN_CONTRACT = void 0;
|
|
15
15
|
exports.roleContextForWorkerRole = roleContextForWorkerRole;
|
|
16
16
|
exports.isWorkerPacket = isWorkerPacket;
|
|
17
17
|
exports.getWorkerCommitPolicy = getWorkerCommitPolicy;
|
|
@@ -130,6 +130,21 @@ exports.WORKER_PROHIBITED_WRITE_PATHS = [
|
|
|
130
130
|
".polaris/runs/",
|
|
131
131
|
"**/telemetry.jsonl",
|
|
132
132
|
];
|
|
133
|
+
// ── Worker symptom reporting ──────────────────────────────────────────────────
|
|
134
|
+
/**
|
|
135
|
+
* Canonical symptom categories workers may use in their sealed result.
|
|
136
|
+
* Workers observe and report — they do NOT diagnose root cause.
|
|
137
|
+
*
|
|
138
|
+
* Include run_health_symptoms in the sealed result only when at least one
|
|
139
|
+
* symptom occurred. When no symptoms occurred, omit the field entirely.
|
|
140
|
+
*/
|
|
141
|
+
exports.WORKER_SYMPTOM_CATEGORIES = [
|
|
142
|
+
"worker-blocked", // Could not proceed (missing info, approval needed, out-of-scope)
|
|
143
|
+
"validation-failed", // Build / test / lint validation commands failed
|
|
144
|
+
"repeated-rework", // Same fix attempted multiple times without success
|
|
145
|
+
"unclear-requirements", // Requirements are contradictory or too ambiguous to act on
|
|
146
|
+
"unusual-assumption", // Had to assume something outside normal scope to proceed
|
|
147
|
+
];
|
|
133
148
|
function getWorkerCommitPolicy(packet) {
|
|
134
149
|
return {
|
|
135
150
|
allowedScope: [...packet.instructions.allowed_scope],
|
|
@@ -255,6 +270,9 @@ function compileImplPacket(input) {
|
|
|
255
270
|
`Create exactly ONE git commit: [${input.childId}] ${childTitle}.`,
|
|
256
271
|
`Do NOT modify open_children or completed_children in ${input.stateFile}. The parent runtime (loop continue) owns that state transition.`,
|
|
257
272
|
`Append a telemetry event to ${input.telemetryFile}.`,
|
|
273
|
+
`If you encountered any struggle signals during execution, include a "run_health_symptoms" array in your sealed result. ` +
|
|
274
|
+
`Use one of these categories: ${exports.WORKER_SYMPTOM_CATEGORIES.join(', ')}. ` +
|
|
275
|
+
`Report what you observed — do NOT diagnose root cause. Omit the field if no symptoms occurred.`,
|
|
258
276
|
`Write compact return JSON to stdout (fields: ${exports.IMPL_RETURN_CONTRACT.join(', ')}). next_recommended_action MUST be exactly "continue" on success, "stop" on unresolvable blocker, or "investigate" if manual review is needed.`,
|
|
259
277
|
`TERMINATE SESSION IMMEDIATELY. Do not select or execute the next child.`,
|
|
260
278
|
];
|