@mstar-harness/dsh 3.9.3 → 3.10.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/dist/gates/dispatch.d.ts +9 -3
- package/dist/index.js +734 -351
- package/harness-commands/iteration-drive.md +16 -2
- package/harness-skills/mstar-artifacts/SKILL.md +10 -6
- package/harness-skills/mstar-artifacts/references/plan-files-and-reports.md +1 -1
- package/harness-skills/mstar-artifacts/references/plan-quality-bar.md +10 -0
- package/harness-skills/mstar-artifacts/references/plan-workflow-lifecycle-contract.md +115 -0
- package/harness-skills/mstar-artifacts/references/status-and-residuals.md +85 -12
- package/harness-skills/mstar-artifacts/templates/plan.main.md +6 -1
- package/harness-skills/mstar-branch-worktree/SKILL.md +5 -4
- package/harness-skills/mstar-compound/SKILL.md +4 -4
- package/harness-skills/mstar-conventions/SKILL.md +1 -0
- package/harness-skills/mstar-conventions/references/effort-estimation.md +2 -0
- package/harness-skills/mstar-dispatch-gates/SKILL.md +11 -2
- package/harness-skills/mstar-harness-core/SKILL.md +2 -2
- package/harness-skills/mstar-host/SKILL.md +9 -2
- package/harness-skills/mstar-host/references/_shared/plan-mode-bridge-core.md +2 -0
- package/harness-skills/mstar-host/references/codex.md +1 -1
- package/harness-skills/mstar-host/references/dsh.md +14 -4
- package/harness-skills/mstar-host/references/omp.md +62 -2
- package/harness-skills/mstar-iteration/SKILL.md +13 -1
- package/harness-skills/mstar-iteration/references/command-shared-invariants.md +6 -0
- package/harness-skills/mstar-iteration/references/phase-1-prepare.md +2 -0
- package/harness-skills/mstar-iteration/references/phase-2-worktree-lease.md +121 -15
- package/harness-skills/mstar-iteration/references/phase-3-iteration-close.md +3 -3
- package/harness-skills/mstar-iteration/references/phase-4-5-pr-delivery.md +3 -2
- package/harness-skills/mstar-iteration/references/phase-6-post-merge-close.md +5 -2
- package/harness-skills/mstar-iteration/references/plan-scoped-pm.md +181 -0
- package/harness-skills/mstar-phase-gates/SKILL.md +7 -5
- package/harness-skills/mstar-project-governance/SKILL.md +3 -3
- package/harness-skills/mstar-review-qc/SKILL.md +4 -4
- package/harness-skills/mstar-review-qc/references/review-responsibility-boundaries.md +1 -1
- package/harness-skills/mstar-roles/references/_shared/leaf-executor-core.md +1 -0
- package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +12 -2
- package/harness-skills/mstar-roles/references/project-manager/plan-management.md +15 -0
- package/harness-skills/mstar-roles/references/project-manager/qa-trigger-matrix.md +3 -3
- package/harness-skills/mstar-roles/references/project-manager/qc-and-residuals.md +7 -5
- package/harness-skills/mstar-roles/references/project-manager.md +12 -4
- package/harness-skills/mstar-sdd/SKILL.md +8 -3
- package/harness-skills/mstar-sdd/references/file-handoffs.md +1 -0
- package/harness-skills/mstar-sdd/references/implementer-continuation-prompt.md +4 -1
- package/harness-skills/mstar-sdd/references/implementer-prompt.md +4 -0
- package/harness-skills/pm/SKILL.md +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -75,7 +75,7 @@ var require_lib = __commonJS(function(exports, module) {
|
|
|
75
75
|
is: () => is,
|
|
76
76
|
isNonNullable: () => isNonNullable,
|
|
77
77
|
isNullable: () => isNullable,
|
|
78
|
-
isPlainObject: () =>
|
|
78
|
+
isPlainObject: () => isPlainObject4,
|
|
79
79
|
makeArray: () => makeArray,
|
|
80
80
|
mapValues: () => mapValues,
|
|
81
81
|
noop: () => noop,
|
|
@@ -98,7 +98,7 @@ var require_lib = __commonJS(function(exports, module) {
|
|
|
98
98
|
function isNonNullable(value) {
|
|
99
99
|
return !isNullable(value);
|
|
100
100
|
}
|
|
101
|
-
function
|
|
101
|
+
function isPlainObject4(data) {
|
|
102
102
|
return data && typeof data === "object" && !Array.isArray(data);
|
|
103
103
|
}
|
|
104
104
|
function filterKeys(object, filter) {
|
|
@@ -679,7 +679,7 @@ var require_lib2 = __commonJS(function(exports, module) {
|
|
|
679
679
|
Schema.extend = /* @__PURE__ */ __name(function extend(type, resolve22) {
|
|
680
680
|
resolvers[type] = resolve22;
|
|
681
681
|
}, "extend");
|
|
682
|
-
Schema.resolve = /* @__PURE__ */ __name(function
|
|
682
|
+
Schema.resolve = /* @__PURE__ */ __name(function resolve5(data, schema, options = {}, strict = false) {
|
|
683
683
|
if (!schema)
|
|
684
684
|
return [data];
|
|
685
685
|
if (options.ignore?.(data, schema))
|
|
@@ -1122,8 +1122,8 @@ var require_lib2 = __commonJS(function(exports, module) {
|
|
|
1122
1122
|
});
|
|
1123
1123
|
|
|
1124
1124
|
// src/index.ts
|
|
1125
|
-
import { existsSync as existsSync24, readFileSync as readFileSync19, readdirSync as
|
|
1126
|
-
import { join as
|
|
1125
|
+
import { existsSync as existsSync24, readFileSync as readFileSync19, readdirSync as readdirSync15 } from "node:fs";
|
|
1126
|
+
import { join as join29 } from "node:path";
|
|
1127
1127
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
1128
1128
|
import {
|
|
1129
1129
|
apply as applySkillLocal,
|
|
@@ -1138,28 +1138,30 @@ import { basename, dirname, join, resolve } from "node:path";
|
|
|
1138
1138
|
import { fileURLToPath } from "node:url";
|
|
1139
1139
|
import { readFileSync as readFileSync2, statSync } from "node:fs";
|
|
1140
1140
|
import { dirname as dirname2, isAbsolute, join as join2, relative, resolve as resolve2 } from "node:path";
|
|
1141
|
-
import { existsSync as
|
|
1141
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1142
1142
|
import { execFileSync } from "node:child_process";
|
|
1143
|
-
import { basename as
|
|
1144
|
-
import { existsSync as
|
|
1145
|
-
import { existsSync as
|
|
1146
|
-
import { dirname as
|
|
1143
|
+
import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute6, join as join10, relative as relative2, resolve as resolve9 } from "node:path";
|
|
1144
|
+
import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "node:fs";
|
|
1145
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
1146
|
+
import { dirname as dirname5, join as join7, resolve as resolve7, sep as sep2 } from "node:path";
|
|
1147
1147
|
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve3 } from "node:path";
|
|
1148
1148
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1149
|
-
import {
|
|
1150
|
-
import { isAbsolute as
|
|
1149
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
1150
|
+
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute3, join as join4, resolve as resolve4 } from "node:path";
|
|
1151
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync as readdirSync2, rmdirSync as rmdirSync2 } from "node:fs";
|
|
1152
|
+
import { isAbsolute as isAbsolute5, join as join6, resolve as resolve6 } from "node:path";
|
|
1151
1153
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
1152
|
-
import { existsSync as
|
|
1153
|
-
import { isAbsolute as
|
|
1154
|
-
import { existsSync as
|
|
1155
|
-
import { join as
|
|
1154
|
+
import { existsSync as existsSync9, realpathSync as realpathSync4 } from "node:fs";
|
|
1155
|
+
import { isAbsolute as isAbsolute7, join as join11, resolve as resolve10 } from "node:path";
|
|
1156
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
1157
|
+
import { join as join12, dirname as dirname8 } from "node:path";
|
|
1156
1158
|
import { execFileSync as execFileSync3, spawn } from "node:child_process";
|
|
1157
|
-
import { mkdirSync as mkdirSync6, readdirSync as
|
|
1158
|
-
import { basename as
|
|
1159
|
-
import { existsSync as
|
|
1160
|
-
import { basename as
|
|
1161
|
-
import { existsSync as
|
|
1162
|
-
import { join as
|
|
1159
|
+
import { mkdirSync as mkdirSync6, readdirSync as readdirSync7, readFileSync as readFileSync9, realpathSync as realpathSync5, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1160
|
+
import { basename as basename7, dirname as dirname9, isAbsolute as isAbsolute8, join as join13, relative as relative3, resolve as resolve11 } from "node:path";
|
|
1161
|
+
import { existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync12 } from "node:fs";
|
|
1162
|
+
import { basename as basename11, isAbsolute as isAbsolute10, join as join17, relative as relative6, resolve as resolve15, sep as sep5 } from "node:path";
|
|
1163
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
1164
|
+
import { join as join18 } from "node:path";
|
|
1163
1165
|
var SEVERITY_ORDER = ["critical", "high", "medium", "low", "nit"];
|
|
1164
1166
|
function applyEnforcement(gate, opts) {
|
|
1165
1167
|
return { ...gate, hardBlocked: opts.hard && gate.violations.length > 0 };
|
|
@@ -1395,6 +1397,302 @@ function verifyPlanExecutionLease(row, planId) {
|
|
|
1395
1397
|
return { ok: violations.length === 0, violations, lease };
|
|
1396
1398
|
}
|
|
1397
1399
|
var heldLockDirs = new AsyncLocalStorage;
|
|
1400
|
+
function isPlainObject2(value) {
|
|
1401
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1402
|
+
}
|
|
1403
|
+
function isNonEmptyString(value) {
|
|
1404
|
+
return typeof value === "string" && value.trim() !== "";
|
|
1405
|
+
}
|
|
1406
|
+
var writeAuthorizations = new AsyncLocalStorage2;
|
|
1407
|
+
var HANDOFF_STATES = [
|
|
1408
|
+
"submitted",
|
|
1409
|
+
"accepted",
|
|
1410
|
+
"returned",
|
|
1411
|
+
"integrating",
|
|
1412
|
+
"merged",
|
|
1413
|
+
"completed"
|
|
1414
|
+
];
|
|
1415
|
+
var PLAN_PROGRESS_STATUSES = ["InProgress", "InReview", "Blocked"];
|
|
1416
|
+
var SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
1417
|
+
var GIT_SHA = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
1418
|
+
function invalid(code, message) {
|
|
1419
|
+
return { ok: false, severity: "high", code, message };
|
|
1420
|
+
}
|
|
1421
|
+
function validateBinding(value, what) {
|
|
1422
|
+
if (!isPlainObject2(value))
|
|
1423
|
+
return [invalid("coordination.row.binding-shape", `${what} must be an object`)];
|
|
1424
|
+
const violations = [];
|
|
1425
|
+
const extra = Object.keys(value).filter((key) => !["session_id", "session_file", "bound_at"].includes(key));
|
|
1426
|
+
if (extra.length > 0) {
|
|
1427
|
+
violations.push(invalid("coordination.row.binding-field", `${what} has unexpected key(s): ${extra.join(", ")}`));
|
|
1428
|
+
}
|
|
1429
|
+
if (!isNonEmptyString(value.session_id)) {
|
|
1430
|
+
violations.push(invalid("coordination.row.binding-field", `${what}.session_id must be a non-empty string`));
|
|
1431
|
+
}
|
|
1432
|
+
if (!isNonEmptyString(value.session_file) || !isAbsolute3(String(value.session_file))) {
|
|
1433
|
+
violations.push(invalid("coordination.row.binding-field", `${what}.session_file must be an absolute path`));
|
|
1434
|
+
}
|
|
1435
|
+
if (!isNonEmptyString(value.bound_at)) {
|
|
1436
|
+
violations.push(invalid("coordination.row.binding-field", `${what}.bound_at must be a timestamp`));
|
|
1437
|
+
}
|
|
1438
|
+
return violations;
|
|
1439
|
+
}
|
|
1440
|
+
function validateEvidenceRef(value, what) {
|
|
1441
|
+
if (!isPlainObject2(value))
|
|
1442
|
+
return [invalid("coordination.row.evidence-shape", `${what} must be a hash-pinned reference`)];
|
|
1443
|
+
const violations = [];
|
|
1444
|
+
const extra = Object.keys(value).filter((key) => !["path", "sha256"].includes(key));
|
|
1445
|
+
if (extra.length > 0) {
|
|
1446
|
+
violations.push(invalid("coordination.row.evidence-field", `${what} has unexpected key(s): ${extra.join(", ")}`));
|
|
1447
|
+
}
|
|
1448
|
+
if (!isNonEmptyString(value.path) || !isAbsolute3(String(value.path))) {
|
|
1449
|
+
violations.push(invalid("coordination.row.evidence-field", `${what}.path must be an absolute path`));
|
|
1450
|
+
}
|
|
1451
|
+
if (!isNonEmptyString(value.sha256) || !SHA256_HEX.test(String(value.sha256))) {
|
|
1452
|
+
violations.push(invalid("coordination.row.evidence-field", `${what}.sha256 must be 64 lowercase hex`));
|
|
1453
|
+
}
|
|
1454
|
+
return violations;
|
|
1455
|
+
}
|
|
1456
|
+
function validatePlanProgress(value, what = "coordination.progress") {
|
|
1457
|
+
if (!isPlainObject2(value))
|
|
1458
|
+
return [invalid("coordination.row.progress-shape", `${what} must be an object`)];
|
|
1459
|
+
const violations = [];
|
|
1460
|
+
const extra = Object.keys(value).filter((key) => !["status", "summary", "evidence_paths", "track_branches"].includes(key));
|
|
1461
|
+
if (extra.length > 0) {
|
|
1462
|
+
violations.push(invalid("coordination.row.progress-field", `${what} has unexpected key(s): ${extra.join(", ")}`));
|
|
1463
|
+
}
|
|
1464
|
+
if (!PLAN_PROGRESS_STATUSES.includes(value.status)) {
|
|
1465
|
+
violations.push(invalid("coordination.row.progress-field", `${what}.status must be one of ${PLAN_PROGRESS_STATUSES.join(", ")}`));
|
|
1466
|
+
}
|
|
1467
|
+
if (!isNonEmptyString(value.summary)) {
|
|
1468
|
+
violations.push(invalid("coordination.row.progress-field", `${what}.summary must be a non-empty string`));
|
|
1469
|
+
}
|
|
1470
|
+
if (!Array.isArray(value.evidence_paths) || !value.evidence_paths.every(isNonEmptyString)) {
|
|
1471
|
+
violations.push(invalid("coordination.row.progress-field", `${what}.evidence_paths must be an array of paths`));
|
|
1472
|
+
}
|
|
1473
|
+
if (value.track_branches !== undefined) {
|
|
1474
|
+
if (!Array.isArray(value.track_branches) || !value.track_branches.every(isNonEmptyString)) {
|
|
1475
|
+
violations.push(invalid("coordination.row.progress-field", `${what}.track_branches must be an array of branch names`));
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
return violations;
|
|
1479
|
+
}
|
|
1480
|
+
function validatePlanHandoff(value, what = "coordination.handoff") {
|
|
1481
|
+
if (!isPlainObject2(value))
|
|
1482
|
+
return [invalid("coordination.row.handoff-shape", `${what} must be an object`)];
|
|
1483
|
+
const allowed = [
|
|
1484
|
+
"id",
|
|
1485
|
+
"attempt",
|
|
1486
|
+
"state",
|
|
1487
|
+
"submitted_by",
|
|
1488
|
+
"submitted_at",
|
|
1489
|
+
"source_branch",
|
|
1490
|
+
"source_sha",
|
|
1491
|
+
"worktree_path",
|
|
1492
|
+
"review_base",
|
|
1493
|
+
"review_head",
|
|
1494
|
+
"qc",
|
|
1495
|
+
"qa",
|
|
1496
|
+
"accepted_by",
|
|
1497
|
+
"accepted_at",
|
|
1498
|
+
"returned_at",
|
|
1499
|
+
"return_reason",
|
|
1500
|
+
"integration",
|
|
1501
|
+
"completed_at"
|
|
1502
|
+
];
|
|
1503
|
+
const violations = [];
|
|
1504
|
+
const extra = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
1505
|
+
if (extra.length > 0) {
|
|
1506
|
+
violations.push(invalid("coordination.row.handoff-field", `${what} has unexpected key(s): ${extra.join(", ")}`));
|
|
1507
|
+
}
|
|
1508
|
+
const required = [
|
|
1509
|
+
"id",
|
|
1510
|
+
"attempt",
|
|
1511
|
+
"state",
|
|
1512
|
+
"submitted_by",
|
|
1513
|
+
"submitted_at",
|
|
1514
|
+
"source_branch",
|
|
1515
|
+
"source_sha",
|
|
1516
|
+
"worktree_path",
|
|
1517
|
+
"review_base",
|
|
1518
|
+
"review_head"
|
|
1519
|
+
];
|
|
1520
|
+
for (const key of required) {
|
|
1521
|
+
if (value[key] === undefined) {
|
|
1522
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.${key} is required`));
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
if (!Number.isInteger(value.attempt) || value.attempt < 1) {
|
|
1526
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.attempt must be a positive integer`));
|
|
1527
|
+
}
|
|
1528
|
+
if (!HANDOFF_STATES.includes(value.state)) {
|
|
1529
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.state must be one of ${HANDOFF_STATES.join(", ")}`));
|
|
1530
|
+
}
|
|
1531
|
+
if (value.id !== undefined && !isNonEmptyString(value.id)) {
|
|
1532
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.id must be a non-empty string`));
|
|
1533
|
+
}
|
|
1534
|
+
for (const key of ["source_sha", "review_base", "review_head"]) {
|
|
1535
|
+
if (value[key] !== undefined && !GIT_SHA.test(String(value[key]))) {
|
|
1536
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.${key} must be a 40-hex git object id`));
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
for (const key of ["submitted_at", "accepted_at", "returned_at", "completed_at"]) {
|
|
1540
|
+
if (value[key] !== undefined && !isNonEmptyString(value[key])) {
|
|
1541
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.${key} must be a timestamp`));
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
if (value.worktree_path !== undefined && (!isNonEmptyString(value.worktree_path) || !isAbsolute3(String(value.worktree_path)))) {
|
|
1545
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.worktree_path must be an absolute path`));
|
|
1546
|
+
}
|
|
1547
|
+
if (value.qc !== undefined) {
|
|
1548
|
+
if (!isPlainObject2(value.qc)) {
|
|
1549
|
+
violations.push(invalid("coordination.row.handoff-shape", `${what}.qc must be an object`));
|
|
1550
|
+
} else {
|
|
1551
|
+
const qc = value.qc;
|
|
1552
|
+
const qcExtra = Object.keys(qc).filter((key) => !["decision", "reports", "consolidated"].includes(key));
|
|
1553
|
+
if (qcExtra.length > 0) {
|
|
1554
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.qc has unexpected key(s): ${qcExtra.join(", ")}`));
|
|
1555
|
+
}
|
|
1556
|
+
if (!isNonEmptyString(qc.decision)) {
|
|
1557
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.qc.decision must be a non-empty string`));
|
|
1558
|
+
}
|
|
1559
|
+
if (!Array.isArray(qc.reports) || qc.reports.length === 0) {
|
|
1560
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.qc.reports must be a non-empty array`));
|
|
1561
|
+
} else {
|
|
1562
|
+
qc.reports.forEach((ref, index) => {
|
|
1563
|
+
violations.push(...validateEvidenceRef(ref, `${what}.qc.reports[${index}]`));
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
violations.push(...validateEvidenceRef(qc.consolidated, `${what}.qc.consolidated`));
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
if (value.qa !== undefined) {
|
|
1570
|
+
if (!isPlainObject2(value.qa)) {
|
|
1571
|
+
violations.push(invalid("coordination.row.handoff-shape", `${what}.qa must be an object`));
|
|
1572
|
+
} else {
|
|
1573
|
+
const qa = value.qa;
|
|
1574
|
+
const qaExtra = Object.keys(qa).filter((key) => !["gate", "decision", "report"].includes(key));
|
|
1575
|
+
if (qaExtra.length > 0) {
|
|
1576
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.qa has unexpected key(s): ${qaExtra.join(", ")}`));
|
|
1577
|
+
}
|
|
1578
|
+
if (!isNonEmptyString(qa.gate)) {
|
|
1579
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.qa.gate must be a non-empty string`));
|
|
1580
|
+
}
|
|
1581
|
+
if (!isNonEmptyString(qa.decision)) {
|
|
1582
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.qa.decision must be a non-empty string`));
|
|
1583
|
+
}
|
|
1584
|
+
violations.push(...validateEvidenceRef(qa.report, `${what}.qa.report`));
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
if (value.integration !== undefined) {
|
|
1588
|
+
if (!isPlainObject2(value.integration)) {
|
|
1589
|
+
violations.push(invalid("coordination.row.handoff-shape", `${what}.integration must be an object`));
|
|
1590
|
+
} else {
|
|
1591
|
+
const integration = value.integration;
|
|
1592
|
+
const integrationAllowed = [
|
|
1593
|
+
"target_branch",
|
|
1594
|
+
"worktree_path",
|
|
1595
|
+
"base_sha",
|
|
1596
|
+
"started_at",
|
|
1597
|
+
"result_sha",
|
|
1598
|
+
"verified_at"
|
|
1599
|
+
];
|
|
1600
|
+
const integrationExtra = Object.keys(integration).filter((key) => !integrationAllowed.includes(key));
|
|
1601
|
+
if (integrationExtra.length > 0) {
|
|
1602
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.integration has unexpected key(s): ${integrationExtra.join(", ")}`));
|
|
1603
|
+
}
|
|
1604
|
+
for (const key of ["target_branch", "worktree_path", "base_sha", "started_at"]) {
|
|
1605
|
+
if (!isNonEmptyString(integration[key])) {
|
|
1606
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.integration.${key} is required`));
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
for (const key of ["base_sha", "result_sha"]) {
|
|
1610
|
+
if (integration[key] !== undefined && !GIT_SHA.test(String(integration[key]))) {
|
|
1611
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.integration.${key} must be a 40-hex git object id`));
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
if (integration.result_sha !== undefined && integration.verified_at === undefined) {
|
|
1615
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.integration.result_sha requires verified_at`));
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
if ((value.state === "integrating" || value.state === "merged" || value.state === "completed") && value.integration === undefined) {
|
|
1620
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.state ${String(value.state)} requires integration`));
|
|
1621
|
+
}
|
|
1622
|
+
return violations;
|
|
1623
|
+
}
|
|
1624
|
+
function validatePreparedCoordination(value, what = "coordination.prepared") {
|
|
1625
|
+
if (!isPlainObject2(value))
|
|
1626
|
+
return [invalid("coordination.row.prepared-shape", `${what} must be an object`)];
|
|
1627
|
+
const allowed = [
|
|
1628
|
+
"assignment_path",
|
|
1629
|
+
"assignment_sha256",
|
|
1630
|
+
"plan_sha256",
|
|
1631
|
+
"qa_gate",
|
|
1632
|
+
"findings_cleanup",
|
|
1633
|
+
"prepared_by",
|
|
1634
|
+
"prepared_at"
|
|
1635
|
+
];
|
|
1636
|
+
const violations = [];
|
|
1637
|
+
const extra = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
1638
|
+
if (extra.length > 0) {
|
|
1639
|
+
violations.push(invalid("coordination.row.prepared-field", `${what} has unexpected key(s): ${extra.join(", ")}`));
|
|
1640
|
+
}
|
|
1641
|
+
for (const key of allowed) {
|
|
1642
|
+
if (!isNonEmptyString(value[key])) {
|
|
1643
|
+
violations.push(invalid("coordination.row.prepared-field", `${what}.${key} is required`));
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
if (value.assignment_path !== undefined && !isAbsolute3(String(value.assignment_path))) {
|
|
1647
|
+
violations.push(invalid("coordination.row.prepared-field", `${what}.assignment_path must be absolute`));
|
|
1648
|
+
}
|
|
1649
|
+
for (const key of ["assignment_sha256", "plan_sha256"]) {
|
|
1650
|
+
if (value[key] !== undefined && !SHA256_HEX.test(String(value[key]))) {
|
|
1651
|
+
violations.push(invalid("coordination.row.prepared-field", `${what}.${key} must be 64 lowercase hex`));
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return violations;
|
|
1655
|
+
}
|
|
1656
|
+
function validateRowCoordination(value, what = "coordination") {
|
|
1657
|
+
if (!isPlainObject2(value))
|
|
1658
|
+
return [invalid("coordination.row.shape", `${what} must be an object`)];
|
|
1659
|
+
const allowed = ["revision", "prepared", "session", "progress", "handoff"];
|
|
1660
|
+
const violations = [];
|
|
1661
|
+
const extra = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
1662
|
+
if (extra.length > 0) {
|
|
1663
|
+
violations.push(invalid("coordination.row.field", `${what} has unexpected key(s): ${extra.join(", ")}`));
|
|
1664
|
+
}
|
|
1665
|
+
if (!Number.isInteger(value.revision) || value.revision < 0) {
|
|
1666
|
+
violations.push(invalid("coordination.row.revision", `${what}.revision must be a non-negative integer`));
|
|
1667
|
+
}
|
|
1668
|
+
if (value.prepared !== undefined)
|
|
1669
|
+
violations.push(...validatePreparedCoordination(value.prepared, `${what}.prepared`));
|
|
1670
|
+
if (value.session !== undefined)
|
|
1671
|
+
violations.push(...validateBinding(value.session, `${what}.session`));
|
|
1672
|
+
if (value.progress !== undefined)
|
|
1673
|
+
violations.push(...validatePlanProgress(value.progress, `${what}.progress`));
|
|
1674
|
+
if (value.handoff !== undefined)
|
|
1675
|
+
violations.push(...validatePlanHandoff(value.handoff, `${what}.handoff`));
|
|
1676
|
+
if (value.handoff !== undefined && value.session === undefined) {
|
|
1677
|
+
violations.push(invalid("coordination.row.handoff-field", `${what}.handoff requires a bound plan session`));
|
|
1678
|
+
}
|
|
1679
|
+
return violations;
|
|
1680
|
+
}
|
|
1681
|
+
function validateSnapshotCoordination(value, what = "coordination") {
|
|
1682
|
+
if (!isPlainObject2(value))
|
|
1683
|
+
return [invalid("coordination.snapshot.shape", `${what} must be an object`)];
|
|
1684
|
+
const violations = [];
|
|
1685
|
+
const extra = Object.keys(value).filter((key) => key !== "coordinator");
|
|
1686
|
+
if (extra.length > 0) {
|
|
1687
|
+
violations.push(invalid("coordination.snapshot.field", `${what} has unexpected key(s): ${extra.join(", ")}`));
|
|
1688
|
+
}
|
|
1689
|
+
if (value.coordinator === undefined) {
|
|
1690
|
+
violations.push(invalid("coordination.snapshot.field", `${what}.coordinator is required`));
|
|
1691
|
+
} else {
|
|
1692
|
+
violations.push(...validateBinding(value.coordinator, `${what}.coordinator`));
|
|
1693
|
+
}
|
|
1694
|
+
return violations;
|
|
1695
|
+
}
|
|
1398
1696
|
var BRANCH_FORMS_HINT = '"Working branch: <existing>" | "Working branch: create <new> from <base>" | "Branch policy: direct on <branch> — <reason>"';
|
|
1399
1697
|
var REQUIRED_FIELDS = [
|
|
1400
1698
|
{ key: "executeAs", label: "Execute as", code: "execute-as" },
|
|
@@ -1404,6 +1702,7 @@ var REQUIRED_FIELDS = [
|
|
|
1404
1702
|
var REVIEW_SEAT_ROLES = ["qc-specialist", "qc-specialist-2", "qc-specialist-3", "code-reviewer", "qa-engineer"];
|
|
1405
1703
|
var BUDGET_LABELS = ["Budget (review / QC seats)", "Budget"];
|
|
1406
1704
|
var RETURN_SHAPE_LABELS = ["Return shape (review / QC seats)", "Return shape"];
|
|
1705
|
+
var TASK_BUDGET_LABELS = ["Task budget (implement / ops rounds)", "Task budget"];
|
|
1407
1706
|
function violation2(severity, code, message, fix) {
|
|
1408
1707
|
return { ok: false, severity, code, message, fix };
|
|
1409
1708
|
}
|
|
@@ -1431,6 +1730,8 @@ function parseAssignmentFields(assignmentText) {
|
|
|
1431
1730
|
fields.budget = value;
|
|
1432
1731
|
else if (RETURN_SHAPE_LABELS.includes(label))
|
|
1433
1732
|
fields.returnShape = value;
|
|
1733
|
+
else if (TASK_BUDGET_LABELS.includes(label))
|
|
1734
|
+
fields.taskBudget = value;
|
|
1434
1735
|
}
|
|
1435
1736
|
return fields;
|
|
1436
1737
|
}
|
|
@@ -1545,6 +1846,12 @@ function validateAssignmentFields(assignmentText, opts = {}) {
|
|
|
1545
1846
|
violations.push(violation2("high", field.code, `${subject} must declare a round ${field.label} — ${missing}`, field.fix));
|
|
1546
1847
|
}
|
|
1547
1848
|
}
|
|
1849
|
+
if (!reviewSeat && !auditRound) {
|
|
1850
|
+
const missing = describeAbsence(fields.taskBudget);
|
|
1851
|
+
if (missing !== undefined) {
|
|
1852
|
+
violations.push(violation2("high", "assignment.field.task-budget-missing", `implement/ops round "${role}" must declare a Task budget — ${missing}`, `add "**Task budget (implement / ops rounds)**: <one implementer round closing the task's declared Files list and verification gates> — capacity contract spec § A1/A2"`));
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1548
1855
|
if (writable) {
|
|
1549
1856
|
const workingPresent = fields.workingBranch !== undefined && fields.workingBranch !== "";
|
|
1550
1857
|
const policyPresent = fields.branchPolicy !== undefined && fields.branchPolicy !== "";
|
|
@@ -1665,9 +1972,8 @@ var WORKFLOW_SNAPSHOT_FILE = "snapshot.json";
|
|
|
1665
1972
|
var WORKFLOW_LIFECYCLE_STATUSES = ["running", "paused", "completed", "failed", "stopped"];
|
|
1666
1973
|
var WORKFLOW_TERMINAL_STATUSES = ["completed", "failed", "stopped"];
|
|
1667
1974
|
var WORKFLOW_LIFECYCLE_TYPES = ["plan", "iteration"];
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
}
|
|
1975
|
+
var WORKFLOW_DELIVERY_KINDS = ["development", "verification/report-only"];
|
|
1976
|
+
var WORKFLOW_COMPOUND_OUTCOMES = ["created", "updated", "skipped"];
|
|
1671
1977
|
function violation3(severity, code, message, fix) {
|
|
1672
1978
|
return { ok: false, severity, code, message, fix };
|
|
1673
1979
|
}
|
|
@@ -1679,10 +1985,65 @@ function validateNonEmptyString2(violations, value, field, missingCode, invalidC
|
|
|
1679
1985
|
}
|
|
1680
1986
|
}
|
|
1681
1987
|
function validateWorktreePathValue(violations, value, field) {
|
|
1682
|
-
if (typeof value !== "string" || value.trim() === "" || !
|
|
1988
|
+
if (typeof value !== "string" || value.trim() === "" || !isAbsolute5(value)) {
|
|
1683
1989
|
violations.push(violation3("high", "workflow.snapshot.invalid-integration-worktree-path", `${field} must be a non-empty absolute path — got ${JSON.stringify(value)}`, "record the absolute integration checkout path (integration_worktree_path)"));
|
|
1684
1990
|
}
|
|
1685
1991
|
}
|
|
1992
|
+
function deliveryEvidenceViolations(value, what) {
|
|
1993
|
+
const violations = [];
|
|
1994
|
+
const invalid2 = (message) => {
|
|
1995
|
+
violations.push(violation3("medium", "workflow.snapshot.invalid-delivery-evidence", `${what}: ${message}`));
|
|
1996
|
+
};
|
|
1997
|
+
if (!isPlainObject2(value)) {
|
|
1998
|
+
invalid2("must be an object");
|
|
1999
|
+
return violations;
|
|
2000
|
+
}
|
|
2001
|
+
const members = ["compound", "pr", "merge", "completion"];
|
|
2002
|
+
const unknownMembers = Object.keys(value).filter((key) => !members.includes(key));
|
|
2003
|
+
if (unknownMembers.length > 0)
|
|
2004
|
+
invalid2(`unknown member(s) ${unknownMembers.join(", ")} — expected ${members.join(" | ")}`);
|
|
2005
|
+
const compound = value.compound;
|
|
2006
|
+
if (compound !== undefined) {
|
|
2007
|
+
if (!isPlainObject2(compound))
|
|
2008
|
+
invalid2("compound must be an object");
|
|
2009
|
+
else {
|
|
2010
|
+
const unknown = Object.keys(compound).filter((key) => key !== "outcome" && key !== "reason");
|
|
2011
|
+
if (unknown.length > 0)
|
|
2012
|
+
invalid2(`compound has unknown key(s) ${unknown.join(", ")}`);
|
|
2013
|
+
if (typeof compound.outcome !== "string" || !WORKFLOW_COMPOUND_OUTCOMES.includes(compound.outcome)) {
|
|
2014
|
+
invalid2(`compound.outcome must be one of ${WORKFLOW_COMPOUND_OUTCOMES.join(" | ")} — got ${JSON.stringify(compound.outcome)}`);
|
|
2015
|
+
} else if (compound.outcome === "skipped" && (typeof compound.reason !== "string" || compound.reason.trim() === "")) {
|
|
2016
|
+
invalid2("compound reason is required when the disposition outcome is 'skipped' (contract §4c)");
|
|
2017
|
+
} else if (compound.reason !== undefined && (typeof compound.reason !== "string" || compound.reason.trim() === "")) {
|
|
2018
|
+
invalid2("compound.reason must be a non-empty string when given");
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
const stringMembers = {
|
|
2023
|
+
pr: ["repo", "head", "target"],
|
|
2024
|
+
merge: ["provider", "evidence"],
|
|
2025
|
+
completion: ["policy", "evidence"]
|
|
2026
|
+
};
|
|
2027
|
+
for (const member of ["pr", "merge", "completion"]) {
|
|
2028
|
+
const block = value[member];
|
|
2029
|
+
if (block === undefined)
|
|
2030
|
+
continue;
|
|
2031
|
+
if (!isPlainObject2(block)) {
|
|
2032
|
+
invalid2(`${member} must be an object`);
|
|
2033
|
+
continue;
|
|
2034
|
+
}
|
|
2035
|
+
const fields = stringMembers[member];
|
|
2036
|
+
const unknown = Object.keys(block).filter((key) => !fields.includes(key));
|
|
2037
|
+
if (unknown.length > 0)
|
|
2038
|
+
invalid2(`${member} has unknown key(s) ${unknown.join(", ")}`);
|
|
2039
|
+
for (const field of fields) {
|
|
2040
|
+
if (typeof block[field] !== "string" || block[field].trim() === "") {
|
|
2041
|
+
invalid2(`${member}.${field} must be a non-empty string`);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
return violations;
|
|
2046
|
+
}
|
|
1686
2047
|
function validateWorkflowSnapshot(doc) {
|
|
1687
2048
|
const violations = [];
|
|
1688
2049
|
if (!isPlainObject2(doc)) {
|
|
@@ -1728,8 +2089,14 @@ function validateWorkflowSnapshot(doc) {
|
|
|
1728
2089
|
if (isPlainObject2(row) && row.execution_lease !== undefined) {
|
|
1729
2090
|
violations.push(...validateExecutionLease(row.execution_lease).violations);
|
|
1730
2091
|
}
|
|
2092
|
+
if (isPlainObject2(row) && row.coordination !== undefined) {
|
|
2093
|
+
violations.push(...validateRowCoordination(row.coordination, `plans[${String(row.id)}].coordination`));
|
|
2094
|
+
}
|
|
1731
2095
|
}
|
|
1732
2096
|
}
|
|
2097
|
+
if (doc.coordination !== undefined) {
|
|
2098
|
+
violations.push(...validateSnapshotCoordination(doc.coordination));
|
|
2099
|
+
}
|
|
1733
2100
|
if (doc.execution_policy !== undefined) {
|
|
1734
2101
|
if (!isPlainObject2(doc.execution_policy)) {
|
|
1735
2102
|
violations.push(violation3("medium", "workflow.snapshot.invalid-execution-policy", "execution_policy must be an object"));
|
|
@@ -1742,7 +2109,7 @@ function validateWorkflowSnapshot(doc) {
|
|
|
1742
2109
|
if (!isPlainObject2(doc.branch)) {
|
|
1743
2110
|
violations.push(violation3("medium", "workflow.snapshot.invalid-branch", "branch must be an object"));
|
|
1744
2111
|
} else {
|
|
1745
|
-
for (const key of ["base", "integration", "target"]) {
|
|
2112
|
+
for (const key of ["base", "source", "integration", "target"]) {
|
|
1746
2113
|
if (doc.branch[key] !== undefined && (typeof doc.branch[key] !== "string" || doc.branch[key].trim() === "")) {
|
|
1747
2114
|
violations.push(violation3("medium", "workflow.snapshot.invalid-branch", `branch.${key} must be a non-empty string`));
|
|
1748
2115
|
}
|
|
@@ -1768,6 +2135,20 @@ function validateWorkflowSnapshot(doc) {
|
|
|
1768
2135
|
if (doc.compass_ref !== undefined) {
|
|
1769
2136
|
validateNonEmptyString2(violations, doc.compass_ref, "compass_ref", "workflow.snapshot.missing-compass-ref", "workflow.snapshot.invalid-compass-ref");
|
|
1770
2137
|
}
|
|
2138
|
+
if (doc.delivery_kind !== undefined) {
|
|
2139
|
+
if (typeof doc.delivery_kind !== "string" || !WORKFLOW_DELIVERY_KINDS.includes(doc.delivery_kind)) {
|
|
2140
|
+
violations.push(violation3("medium", "workflow.snapshot.invalid-delivery-kind", `delivery_kind must be one of ${WORKFLOW_DELIVERY_KINDS.join(" | ")} — got ${JSON.stringify(doc.delivery_kind)}`));
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
if (doc.project !== undefined) {
|
|
2144
|
+
validateNonEmptyString2(violations, doc.project, "project", "workflow.snapshot.missing-project", "workflow.snapshot.invalid-project");
|
|
2145
|
+
}
|
|
2146
|
+
if (doc.completion_policy !== undefined) {
|
|
2147
|
+
validateNonEmptyString2(violations, doc.completion_policy, "completion_policy", "workflow.snapshot.missing-completion-policy", "workflow.snapshot.invalid-completion-policy");
|
|
2148
|
+
}
|
|
2149
|
+
if (doc.delivery !== undefined) {
|
|
2150
|
+
violations.push(...deliveryEvidenceViolations(doc.delivery, "delivery"));
|
|
2151
|
+
}
|
|
1771
2152
|
const terminal = typeof doc.status === "string" && WORKFLOW_TERMINAL_STATUSES.includes(doc.status);
|
|
1772
2153
|
if (terminal) {
|
|
1773
2154
|
if (doc.ended_at === undefined) {
|
|
@@ -1796,13 +2177,13 @@ class WorkflowSnapshotValidationError extends Error {
|
|
|
1796
2177
|
}
|
|
1797
2178
|
}
|
|
1798
2179
|
function readWorkflowSnapshot(dir) {
|
|
1799
|
-
const snapshotPath =
|
|
1800
|
-
if (!
|
|
2180
|
+
const snapshotPath = join6(dir, WORKFLOW_SNAPSHOT_FILE);
|
|
2181
|
+
if (!existsSync4(snapshotPath)) {
|
|
1801
2182
|
throw new Error(`workflow snapshot not found: ${snapshotPath}`);
|
|
1802
2183
|
}
|
|
1803
2184
|
let doc;
|
|
1804
2185
|
try {
|
|
1805
|
-
doc = JSON.parse(
|
|
2186
|
+
doc = JSON.parse(readFileSync4(snapshotPath, "utf8"));
|
|
1806
2187
|
} catch (error) {
|
|
1807
2188
|
throw new Error(`Invalid JSON in ${snapshotPath}: ${error.message}`);
|
|
1808
2189
|
}
|
|
@@ -1833,9 +2214,6 @@ var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
|
1833
2214
|
var PLAN_STATUSES = ["Todo", "InProgress", "InReview", "Blocked", "Done"];
|
|
1834
2215
|
var RESIDUAL_DECISIONS = ["defer", "accept", "risk-accepted"];
|
|
1835
2216
|
var RESIDUAL_LIFECYCLES = ["open", "resolved", "waived", "superseded", "duplicate"];
|
|
1836
|
-
function isPlainObject3(value) {
|
|
1837
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1838
|
-
}
|
|
1839
2217
|
function violation4(severity, code, message, fix) {
|
|
1840
2218
|
return { ok: false, severity, code, message, fix };
|
|
1841
2219
|
}
|
|
@@ -1860,7 +2238,7 @@ function validateNonEmptyString3(violations, value, field, missingCode, invalidC
|
|
|
1860
2238
|
}
|
|
1861
2239
|
function validatePlanRow(row) {
|
|
1862
2240
|
const violations = [];
|
|
1863
|
-
if (!
|
|
2241
|
+
if (!isPlainObject2(row)) {
|
|
1864
2242
|
return { ok: false, violations: [violation4("high", "status.plan-row.invalid", "plan row must be an object")] };
|
|
1865
2243
|
}
|
|
1866
2244
|
const { id, plan_id: planId, title, file, status, metadata, execution_lease } = row;
|
|
@@ -1884,10 +2262,10 @@ function validatePlanRow(row) {
|
|
|
1884
2262
|
} else if (typeof status !== "string" || !PLAN_STATUSES.includes(status)) {
|
|
1885
2263
|
violations.push(violation4("medium", "status.plan-row.invalid-status", `status must be one of ${PLAN_STATUSES.join(" | ")} — got ${JSON.stringify(status)}`));
|
|
1886
2264
|
}
|
|
1887
|
-
if (metadata !== undefined && !
|
|
2265
|
+
if (metadata !== undefined && !isPlainObject2(metadata)) {
|
|
1888
2266
|
violations.push(violation4("medium", "status.plan-row.invalid-metadata", "metadata must be an object"));
|
|
1889
2267
|
}
|
|
1890
|
-
if (execution_lease !== undefined && !
|
|
2268
|
+
if (execution_lease !== undefined && !isPlainObject2(execution_lease)) {
|
|
1891
2269
|
violations.push(violation4("medium", "status.plan-row.invalid-execution-lease", "execution_lease must be an object"));
|
|
1892
2270
|
}
|
|
1893
2271
|
if (status === "Done" && execution_lease !== undefined) {
|
|
@@ -1897,7 +2275,7 @@ function validatePlanRow(row) {
|
|
|
1897
2275
|
}
|
|
1898
2276
|
function validateResidual(entry) {
|
|
1899
2277
|
const violations = [];
|
|
1900
|
-
if (!
|
|
2278
|
+
if (!isPlainObject2(entry)) {
|
|
1901
2279
|
return { ok: false, violations: [violation4("high", "status.residual.invalid", "residual entry must be an object")] };
|
|
1902
2280
|
}
|
|
1903
2281
|
const { id, title, severity, source, scope, decision, owner, target, tracking, detail_doc, lifecycle, closed_at } = entry;
|
|
@@ -1957,7 +2335,7 @@ function isHarnessRelativePath(dir) {
|
|
|
1957
2335
|
}
|
|
1958
2336
|
function validateWorkflowEntry(entry) {
|
|
1959
2337
|
const violations = [];
|
|
1960
|
-
if (!
|
|
2338
|
+
if (!isPlainObject2(entry)) {
|
|
1961
2339
|
return {
|
|
1962
2340
|
ok: false,
|
|
1963
2341
|
violations: [violation4("high", "status.workflow.invalid", "workflow entry must be an object")]
|
|
@@ -1985,7 +2363,7 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
1985
2363
|
if (typeof docOrPath === "string") {
|
|
1986
2364
|
try {
|
|
1987
2365
|
doc = readJson(docOrPath);
|
|
1988
|
-
harnessDir =
|
|
2366
|
+
harnessDir = dirname5(resolve7(docOrPath));
|
|
1989
2367
|
} catch (error) {
|
|
1990
2368
|
return {
|
|
1991
2369
|
ok: false,
|
|
@@ -1995,7 +2373,7 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
1995
2373
|
} else {
|
|
1996
2374
|
doc = docOrPath;
|
|
1997
2375
|
}
|
|
1998
|
-
if (!
|
|
2376
|
+
if (!isPlainObject2(doc)) {
|
|
1999
2377
|
return { ok: false, violations: [violation4("high", "status.invalid-doc", "status document must be an object")] };
|
|
2000
2378
|
}
|
|
2001
2379
|
if (doc.version !== 2) {
|
|
@@ -2036,7 +2414,7 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
2036
2414
|
const seen = new Set;
|
|
2037
2415
|
for (const entry of doc.workflows) {
|
|
2038
2416
|
violations.push(...validateWorkflowEntry(entry).violations);
|
|
2039
|
-
if (
|
|
2417
|
+
if (isPlainObject2(entry) && typeof entry.id === "string") {
|
|
2040
2418
|
if (seen.has(entry.id)) {
|
|
2041
2419
|
violations.push(violation4("medium", "status.workflow.duplicate-id", `duplicate workflow id in workflows[]: ${JSON.stringify(entry.id)}`));
|
|
2042
2420
|
}
|
|
@@ -2047,22 +2425,22 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
2047
2425
|
if (harnessDir !== undefined && Array.isArray(doc.workflows)) {
|
|
2048
2426
|
let realHarnessDir = null;
|
|
2049
2427
|
try {
|
|
2050
|
-
realHarnessDir =
|
|
2428
|
+
realHarnessDir = realpathSync2(harnessDir);
|
|
2051
2429
|
} catch {}
|
|
2052
2430
|
for (const entry of doc.workflows) {
|
|
2053
|
-
if (!
|
|
2431
|
+
if (!isPlainObject2(entry) || typeof entry.dir !== "string")
|
|
2054
2432
|
continue;
|
|
2055
|
-
const relSnapshot =
|
|
2056
|
-
const snapshotPath =
|
|
2433
|
+
const relSnapshot = join7(entry.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
2434
|
+
const snapshotPath = join7(harnessDir, relSnapshot);
|
|
2057
2435
|
const label = typeof entry.id === "string" ? entry.id : relSnapshot;
|
|
2058
2436
|
let physical;
|
|
2059
2437
|
try {
|
|
2060
|
-
physical =
|
|
2438
|
+
physical = realpathSync2(snapshotPath);
|
|
2061
2439
|
} catch {
|
|
2062
2440
|
violations.push(violation4("high", "status.workflow.snapshot-missing", `workflows[] lists ${JSON.stringify(label)} but its snapshot does not exist at ${JSON.stringify(relSnapshot)} — the root holds active lifecycles only; unregister the id when its snapshot is removed`));
|
|
2063
2441
|
continue;
|
|
2064
2442
|
}
|
|
2065
|
-
if (realHarnessDir !== null && physical !== realHarnessDir && !physical.startsWith(`${realHarnessDir}${
|
|
2443
|
+
if (realHarnessDir !== null && physical !== realHarnessDir && !physical.startsWith(`${realHarnessDir}${sep2}`)) {
|
|
2066
2444
|
violations.push(violation4("high", "status.workflow.snapshot-outside-harness", `workflows[] lists ${JSON.stringify(label)} but its snapshot resolves outside the harness dir (${JSON.stringify(physical)}) — symlinked snapshot paths are rejected; the snapshot must physically live under ${JSON.stringify(harnessDir)}`));
|
|
2067
2445
|
continue;
|
|
2068
2446
|
}
|
|
@@ -2089,23 +2467,23 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
2089
2467
|
var validateStatus = validateStatusV2;
|
|
2090
2468
|
function resolveCompassEnforcement(harnessDir) {
|
|
2091
2469
|
const iterationsDir = resolveIterationDir(harnessDir);
|
|
2092
|
-
if (!
|
|
2470
|
+
if (!existsSync5(iterationsDir))
|
|
2093
2471
|
return { hard: false, source: "none" };
|
|
2094
2472
|
let entries;
|
|
2095
2473
|
try {
|
|
2096
|
-
entries =
|
|
2474
|
+
entries = readdirSync3(iterationsDir, { withFileTypes: true });
|
|
2097
2475
|
} catch {
|
|
2098
2476
|
return { hard: false, source: "none" };
|
|
2099
2477
|
}
|
|
2100
2478
|
for (const entry of entries) {
|
|
2101
2479
|
if (!entry.isDirectory())
|
|
2102
2480
|
continue;
|
|
2103
|
-
const compassPath =
|
|
2104
|
-
if (!
|
|
2481
|
+
const compassPath = join7(iterationsDir, entry.name, "delivery-compass.md");
|
|
2482
|
+
if (!existsSync5(compassPath))
|
|
2105
2483
|
continue;
|
|
2106
2484
|
let content;
|
|
2107
2485
|
try {
|
|
2108
|
-
content =
|
|
2486
|
+
content = readFileSync5(compassPath, "utf8");
|
|
2109
2487
|
} catch {
|
|
2110
2488
|
continue;
|
|
2111
2489
|
}
|
|
@@ -2120,8 +2498,8 @@ function resolveCompassEnforcement(harnessDir) {
|
|
|
2120
2498
|
return { hard: false, source: "none" };
|
|
2121
2499
|
}
|
|
2122
2500
|
function resolveMstarcEnforcement(harnessDir) {
|
|
2123
|
-
const dir =
|
|
2124
|
-
const rc = loadMstarc(dir,
|
|
2501
|
+
const dir = resolve7(harnessDir);
|
|
2502
|
+
const rc = loadMstarc(dir, dirname5(dir));
|
|
2125
2503
|
const value = rc?.config.enforcement;
|
|
2126
2504
|
if (value === "hard")
|
|
2127
2505
|
return { hard: true, source: "mstarc" };
|
|
@@ -2212,11 +2590,11 @@ function validateCompassShape(doc) {
|
|
|
2212
2590
|
function violation5(severity, code, message, fix) {
|
|
2213
2591
|
return { ok: false, severity, code, message, fix };
|
|
2214
2592
|
}
|
|
2215
|
-
function
|
|
2593
|
+
function isPlainObject3(value) {
|
|
2216
2594
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2217
2595
|
}
|
|
2218
2596
|
function validateCompassFrontmatter(doc) {
|
|
2219
|
-
if (!
|
|
2597
|
+
if (!isPlainObject3(doc)) {
|
|
2220
2598
|
return {
|
|
2221
2599
|
ok: false,
|
|
2222
2600
|
violations: [
|
|
@@ -2253,7 +2631,7 @@ function findPlanRow(snapshotDoc, planId) {
|
|
|
2253
2631
|
if (!Array.isArray(snapshotDoc.plans))
|
|
2254
2632
|
return null;
|
|
2255
2633
|
for (const row of snapshotDoc.plans) {
|
|
2256
|
-
if (!
|
|
2634
|
+
if (!isPlainObject3(row))
|
|
2257
2635
|
continue;
|
|
2258
2636
|
const rowId = typeof row.id === "string" ? row.id : typeof row.plan_id === "string" ? row.plan_id : null;
|
|
2259
2637
|
if (rowId === planId)
|
|
@@ -2349,7 +2727,7 @@ function evaluatePhaseGate(snapshotDoc, compassDoc, opts = {}) {
|
|
|
2349
2727
|
};
|
|
2350
2728
|
}
|
|
2351
2729
|
function parseCompassFrontmatter(filePath) {
|
|
2352
|
-
return parseCompassFrontmatterText(
|
|
2730
|
+
return parseCompassFrontmatterText(readFileSync6(filePath, "utf8"), filePath);
|
|
2353
2731
|
}
|
|
2354
2732
|
function parseCompassFrontmatterText(content, filePath) {
|
|
2355
2733
|
const lines = content.split(/\r?\n/);
|
|
@@ -2416,9 +2794,6 @@ var PROJECT_ROADMAP_FILE = "roadmap.md";
|
|
|
2416
2794
|
var PROJECT_REGISTER_FILE = "residuals.json";
|
|
2417
2795
|
var _DEFAULT_PROJECT = "_default";
|
|
2418
2796
|
var DATE_RE3 = /^\d{4}-\d{2}-\d{2}$/;
|
|
2419
|
-
function isPlainObject5(value) {
|
|
2420
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2421
|
-
}
|
|
2422
2797
|
function violation6(severity, code, message, fix) {
|
|
2423
2798
|
return { ok: false, severity, code, message, fix };
|
|
2424
2799
|
}
|
|
@@ -2431,7 +2806,7 @@ function validateNonEmptyString4(violations, value, field, missingCode, invalidC
|
|
|
2431
2806
|
}
|
|
2432
2807
|
function validateProjectRegister(doc) {
|
|
2433
2808
|
const violations = [];
|
|
2434
|
-
if (!
|
|
2809
|
+
if (!isPlainObject2(doc)) {
|
|
2435
2810
|
return {
|
|
2436
2811
|
ok: false,
|
|
2437
2812
|
violations: [violation6("high", "project.register.invalid", "project register must be an object")]
|
|
@@ -2439,7 +2814,7 @@ function validateProjectRegister(doc) {
|
|
|
2439
2814
|
}
|
|
2440
2815
|
if (doc.entries === undefined) {
|
|
2441
2816
|
violations.push(violation6("high", "project.register.missing-entries", "missing required field: entries"));
|
|
2442
|
-
} else if (!
|
|
2817
|
+
} else if (!isPlainObject2(doc.entries)) {
|
|
2443
2818
|
violations.push(violation6("high", "project.register.invalid-entries", "entries must be an object keyed by plan id"));
|
|
2444
2819
|
} else {
|
|
2445
2820
|
for (const [key, entries] of Object.entries(doc.entries)) {
|
|
@@ -2452,7 +2827,7 @@ function validateProjectRegister(doc) {
|
|
|
2452
2827
|
}
|
|
2453
2828
|
for (const entry of entries) {
|
|
2454
2829
|
violations.push(...validateResidual(entry).violations);
|
|
2455
|
-
if (!
|
|
2830
|
+
if (!isPlainObject2(entry))
|
|
2456
2831
|
continue;
|
|
2457
2832
|
validateNonEmptyString4(violations, entry.source_plan, "source_plan", "project.register.missing-source-plan", "project.register.invalid-source-plan");
|
|
2458
2833
|
if (entry.registered_at === undefined) {
|
|
@@ -2474,7 +2849,7 @@ function validateProjectRegister(doc) {
|
|
|
2474
2849
|
function findingsCleanupGate(register, planId, opts) {
|
|
2475
2850
|
const mode = opts?.mode ?? "allow-residual";
|
|
2476
2851
|
const violations = [];
|
|
2477
|
-
const entries =
|
|
2852
|
+
const entries = isPlainObject2(register.entries) ? register.entries[planId] : undefined;
|
|
2478
2853
|
if (entries === undefined) {
|
|
2479
2854
|
return { ok: true, violations };
|
|
2480
2855
|
}
|
|
@@ -2511,25 +2886,25 @@ function findingsCleanupGate(register, planId, opts) {
|
|
|
2511
2886
|
return { ok: violations.length === 0, violations };
|
|
2512
2887
|
}
|
|
2513
2888
|
function resolveHarnessDir(startDir = process.cwd(), opts = {}) {
|
|
2514
|
-
const start =
|
|
2889
|
+
const start = resolve9(startDir);
|
|
2515
2890
|
const explicit = opts.harnessDir ?? process.env.MSTAR_HARNESS_DIR;
|
|
2516
2891
|
if (explicit)
|
|
2517
|
-
return
|
|
2518
|
-
const boundary =
|
|
2892
|
+
return resolve9(start, explicit);
|
|
2893
|
+
const boundary = resolve9(start, opts.workspaceRoot ?? defaultWorkspaceRoot(start));
|
|
2519
2894
|
const rc = loadMstarc(start, boundary);
|
|
2520
2895
|
if (rc !== null && rc.config.harnessDir)
|
|
2521
|
-
return
|
|
2896
|
+
return resolve9(rc.dir, rc.config.harnessDir);
|
|
2522
2897
|
let dir = start;
|
|
2523
2898
|
for (;; ) {
|
|
2524
2899
|
if (!isAtOrBelow2(dir, boundary))
|
|
2525
2900
|
return null;
|
|
2526
|
-
for (const candidate of [
|
|
2901
|
+
for (const candidate of [join10(dir, ".mstar"), join10(dir, ".agents"), join10(dir, ".plans"), join10(dir, "plans")]) {
|
|
2527
2902
|
if (isDirectory(candidate))
|
|
2528
2903
|
return candidate;
|
|
2529
2904
|
}
|
|
2530
2905
|
if (dir === boundary)
|
|
2531
2906
|
return null;
|
|
2532
|
-
const parent =
|
|
2907
|
+
const parent = dirname6(dir);
|
|
2533
2908
|
if (parent === dir)
|
|
2534
2909
|
return null;
|
|
2535
2910
|
dir = parent;
|
|
@@ -2547,21 +2922,21 @@ function defaultWorkspaceRoot(startDir) {
|
|
|
2547
2922
|
let boundary = startDir;
|
|
2548
2923
|
for (const segment of cdup.split(/[\\/]/)) {
|
|
2549
2924
|
if (segment && segment !== ".")
|
|
2550
|
-
boundary =
|
|
2925
|
+
boundary = dirname6(boundary);
|
|
2551
2926
|
}
|
|
2552
|
-
return
|
|
2927
|
+
return resolve9(boundary);
|
|
2553
2928
|
} catch {}
|
|
2554
2929
|
return startDir;
|
|
2555
2930
|
}
|
|
2556
2931
|
function isAtOrBelow2(dir, root) {
|
|
2557
2932
|
const rel = relative2(root, dir);
|
|
2558
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
2933
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
|
|
2559
2934
|
}
|
|
2560
2935
|
function mstarcDirOverride(harnessDir, key) {
|
|
2561
|
-
const dir =
|
|
2562
|
-
const rc = loadMstarc(dir,
|
|
2936
|
+
const dir = resolve9(harnessDir);
|
|
2937
|
+
const rc = loadMstarc(dir, dirname6(dir));
|
|
2563
2938
|
const declared = rc?.config[key];
|
|
2564
|
-
return declared ?
|
|
2939
|
+
return declared ? resolve9(rc.dir, declared) : null;
|
|
2565
2940
|
}
|
|
2566
2941
|
function assertSafePathComponent(value, what) {
|
|
2567
2942
|
if (value === "" || value === "." || value === ".." || !/^[A-Za-z0-9._-]+$/.test(value)) {
|
|
@@ -2569,44 +2944,44 @@ function assertSafePathComponent(value, what) {
|
|
|
2569
2944
|
}
|
|
2570
2945
|
}
|
|
2571
2946
|
function canonicalizeNearestExisting(path) {
|
|
2572
|
-
const abs =
|
|
2947
|
+
const abs = resolve9(path);
|
|
2573
2948
|
let dir = abs;
|
|
2574
2949
|
const tail = [];
|
|
2575
2950
|
for (;; ) {
|
|
2576
|
-
if (
|
|
2951
|
+
if (existsSync8(dir)) {
|
|
2577
2952
|
try {
|
|
2578
|
-
return
|
|
2953
|
+
return join10(realpathSync3(dir), ...tail);
|
|
2579
2954
|
} catch {
|
|
2580
2955
|
return abs;
|
|
2581
2956
|
}
|
|
2582
2957
|
}
|
|
2583
|
-
const parent =
|
|
2958
|
+
const parent = dirname6(dir);
|
|
2584
2959
|
if (parent === dir)
|
|
2585
2960
|
return abs;
|
|
2586
|
-
tail.unshift(
|
|
2961
|
+
tail.unshift(basename5(dir));
|
|
2587
2962
|
dir = parent;
|
|
2588
2963
|
}
|
|
2589
2964
|
}
|
|
2590
2965
|
function resolveSddDir(harnessDir, planId) {
|
|
2591
2966
|
assertSafePathComponent(planId, "planId");
|
|
2592
|
-
const base =
|
|
2967
|
+
const base = resolve9(harnessDir);
|
|
2593
2968
|
const declared = mstarcDirOverride(base, "sddDir");
|
|
2594
|
-
const sddBase = declared !== null ? declared :
|
|
2595
|
-
return
|
|
2969
|
+
const sddBase = declared !== null ? declared : join10(base, "sdd");
|
|
2970
|
+
return join10(sddBase, planId);
|
|
2596
2971
|
}
|
|
2597
2972
|
function resolveIterationDir(harnessDir) {
|
|
2598
2973
|
const declared = mstarcDirOverride(harnessDir, "iterationDir");
|
|
2599
2974
|
if (declared !== null)
|
|
2600
2975
|
return declared;
|
|
2601
|
-
return
|
|
2976
|
+
return join10(resolve9(harnessDir), "iterations");
|
|
2602
2977
|
}
|
|
2603
2978
|
function resolveHarnessSubdir(startDir, opts, key, fallback) {
|
|
2604
2979
|
const harness = resolveHarnessDir(startDir, opts);
|
|
2605
2980
|
if (harness === null) {
|
|
2606
|
-
throw new Error(`harness dir not found from ${
|
|
2981
|
+
throw new Error(`harness dir not found from ${resolve9(startDir)} — cannot resolve the ${fallback} dir (run \`mstar harness scaffold\`, pass opts.harnessDir, or set MSTAR_HARNESS_DIR)`);
|
|
2607
2982
|
}
|
|
2608
2983
|
const declared = mstarcDirOverride(harness, key);
|
|
2609
|
-
return declared !== null ? declared :
|
|
2984
|
+
return declared !== null ? declared : join10(resolve9(harness), fallback);
|
|
2610
2985
|
}
|
|
2611
2986
|
function resolveWorkflowDir(startDir = process.cwd(), opts = {}) {
|
|
2612
2987
|
return resolveHarnessSubdir(startDir, opts, "workflowDir", "workflows");
|
|
@@ -2686,7 +3061,7 @@ function parseMainWorktree(out) {
|
|
|
2686
3061
|
if (branch === null)
|
|
2687
3062
|
return null;
|
|
2688
3063
|
try {
|
|
2689
|
-
return { root:
|
|
3064
|
+
return { root: realpathSync4(rawPath), branch };
|
|
2690
3065
|
} catch {
|
|
2691
3066
|
return null;
|
|
2692
3067
|
}
|
|
@@ -2754,8 +3129,8 @@ function probeCheckout(worktreePath, opts) {
|
|
|
2754
3129
|
const raw = stdout.trim();
|
|
2755
3130
|
if (raw === "")
|
|
2756
3131
|
return { error: `no git dir reported at "${worktreePath}"` };
|
|
2757
|
-
const abs =
|
|
2758
|
-
return { gitDir:
|
|
3132
|
+
const abs = isAbsolute7(raw) ? raw : join11(worktreePath, raw);
|
|
3133
|
+
return { gitDir: realpathSync4(abs) };
|
|
2759
3134
|
} catch (err) {
|
|
2760
3135
|
const e = err;
|
|
2761
3136
|
if (e.killed === true || e.signal !== undefined) {
|
|
@@ -2776,7 +3151,7 @@ function probeCheckoutRoot(path, opts = {}) {
|
|
|
2776
3151
|
const raw = stdout.trim();
|
|
2777
3152
|
if (raw === "")
|
|
2778
3153
|
return null;
|
|
2779
|
-
return
|
|
3154
|
+
return realpathSync4(raw);
|
|
2780
3155
|
} catch {
|
|
2781
3156
|
return null;
|
|
2782
3157
|
}
|
|
@@ -2785,7 +3160,7 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
2785
3160
|
const violations = [];
|
|
2786
3161
|
const checkoutCache = new Map;
|
|
2787
3162
|
const checkout = (path) => {
|
|
2788
|
-
const key =
|
|
3163
|
+
const key = resolve10(path);
|
|
2789
3164
|
if (!checkoutCache.has(key))
|
|
2790
3165
|
checkoutCache.set(key, probeCheckout(path, opts));
|
|
2791
3166
|
return checkoutCache.get(key);
|
|
@@ -2824,7 +3199,7 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
2824
3199
|
violations.push(violation8("high", "worktree.l1.lease-branch-missing", `execution_lease.working_branch is empty for plan "${planId}"`, "record the lease working_branch before dispatch"));
|
|
2825
3200
|
}
|
|
2826
3201
|
if (integrationRequired && integrationWorktreePath.trim() !== "" && integrationBranch.trim() !== "") {
|
|
2827
|
-
if (!
|
|
3202
|
+
if (!existsSync9(integrationWorktreePath)) {
|
|
2828
3203
|
violations.push(violation8("high", "worktree.l1.integration-missing", `integration worktree "${integrationWorktreePath}" does not exist for plan "${planId}" — the integration checkout must exist before dispatch`, `create it before dispatch: git worktree add ${shellQuote(integrationWorktreePath)} ${shellQuote(integrationBranch)}`));
|
|
2829
3204
|
} else {
|
|
2830
3205
|
const probe = probeBranch(integrationWorktreePath, opts);
|
|
@@ -2836,10 +3211,10 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
2836
3211
|
}
|
|
2837
3212
|
}
|
|
2838
3213
|
const identityViolation = (aPath, bPath, code, describe, fix) => {
|
|
2839
|
-
if (
|
|
3214
|
+
if (resolve10(aPath) === resolve10(bPath)) {
|
|
2840
3215
|
return violation8("critical", code, `${describe} — the same checkout (normalized path equality) is not isolation`, fix);
|
|
2841
3216
|
}
|
|
2842
|
-
if (!
|
|
3217
|
+
if (!existsSync9(aPath) || !existsSync9(bPath))
|
|
2843
3218
|
return null;
|
|
2844
3219
|
const a = checkout(aPath);
|
|
2845
3220
|
const b = checkout(bPath);
|
|
@@ -2869,7 +3244,7 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
2869
3244
|
if (v !== null)
|
|
2870
3245
|
violations.push(v);
|
|
2871
3246
|
}
|
|
2872
|
-
if (leaseWorktreePath.trim() !== "" && !
|
|
3247
|
+
if (leaseWorktreePath.trim() !== "" && !existsSync9(leaseWorktreePath)) {
|
|
2873
3248
|
violations.push(violation8("high", "worktree.l1.feature-missing", `feature worktree directory "${leaseWorktreePath}" does not exist for plan "${planId}"`, `create it before dispatch: git worktree add ${shellQuote(leaseWorktreePath)} <working-branch>`));
|
|
2874
3249
|
} else if (leaseWorktreePath.trim() !== "" && leaseWorkingBranch.trim() !== "") {
|
|
2875
3250
|
const probe = probeBranch(leaseWorktreePath, opts);
|
|
@@ -2893,17 +3268,17 @@ function l2PreDispatchCheck(input, opts = {}) {
|
|
|
2893
3268
|
violations.push(violation8("high", "worktree.l2.track-invalid", `track ${index + 1} is missing worktreePath and/or workingBranch`, "fill both fields for every track"));
|
|
2894
3269
|
return;
|
|
2895
3270
|
}
|
|
2896
|
-
if (!
|
|
3271
|
+
if (!isAbsolute7(track.worktreePath)) {
|
|
2897
3272
|
violations.push(violation8("high", "worktree.l2.track-path-relative", `track ${index + 1} worktreePath "${track.worktreePath}" is not an absolute path — L2 tracks MUST use absolute worktree checkout paths (consistent with the lease validator's absolute worktree_path enforcement)`, `use an absolute path for track ${index + 1} (e.g. /Users/<you>/worktrees/<branch>)`));
|
|
2898
3273
|
return;
|
|
2899
3274
|
}
|
|
2900
|
-
const normalized =
|
|
3275
|
+
const normalized = resolve10(track.worktreePath);
|
|
2901
3276
|
if (seenPaths.has(normalized)) {
|
|
2902
3277
|
violations.push(violation8("high", "worktree.l2.track-path-collision", `duplicate worktreePath "${track.worktreePath}" across parallel tracks — L2 parallel-writable isolation requires a distinct absolute Worktree path per track (N parallel invokes ≠ isolation)`, "give every parallel track its own git worktree checkout"));
|
|
2903
3278
|
return;
|
|
2904
3279
|
}
|
|
2905
3280
|
seenPaths.add(normalized);
|
|
2906
|
-
if (!
|
|
3281
|
+
if (!existsSync9(track.worktreePath)) {
|
|
2907
3282
|
violations.push(violation8("high", "worktree.l2.track-missing", `track worktree directory "${track.worktreePath}" does not exist`, `create it before dispatch: git worktree add ${track.worktreePath} ${track.workingBranch}`));
|
|
2908
3283
|
return;
|
|
2909
3284
|
}
|
|
@@ -2929,33 +3304,33 @@ function assertBranchAlignment(worktreePath, expectedBranch, opts = {}) {
|
|
|
2929
3304
|
function shellQuote(value) {
|
|
2930
3305
|
return "'" + value.replaceAll("'", `'"'"'`) + "'";
|
|
2931
3306
|
}
|
|
2932
|
-
function
|
|
3307
|
+
function isPlainObject6(value) {
|
|
2933
3308
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2934
3309
|
}
|
|
2935
3310
|
function collectActiveLifecycleBranches(snapshots) {
|
|
2936
3311
|
const owned = new Set;
|
|
2937
3312
|
for (const doc of snapshots) {
|
|
2938
3313
|
const branch = doc.branch;
|
|
2939
|
-
if (
|
|
3314
|
+
if (isPlainObject6(branch) && typeof branch.integration === "string" && branch.integration.trim() !== "") {
|
|
2940
3315
|
owned.add(branch.integration);
|
|
2941
3316
|
}
|
|
2942
3317
|
if (!Array.isArray(doc.plans))
|
|
2943
3318
|
continue;
|
|
2944
3319
|
for (const row of doc.plans) {
|
|
2945
|
-
if (!
|
|
3320
|
+
if (!isPlainObject6(row))
|
|
2946
3321
|
continue;
|
|
2947
3322
|
const lease = row.execution_lease;
|
|
2948
|
-
if (
|
|
3323
|
+
if (isPlainObject6(lease) && typeof lease.working_branch === "string" && lease.working_branch.trim() !== "") {
|
|
2949
3324
|
owned.add(lease.working_branch);
|
|
2950
3325
|
}
|
|
2951
3326
|
const meta = row.metadata;
|
|
2952
|
-
if (
|
|
3327
|
+
if (isPlainObject6(meta) && Array.isArray(meta.track_branches)) {
|
|
2953
3328
|
for (const branch2 of meta.track_branches) {
|
|
2954
3329
|
if (typeof branch2 === "string" && branch2.trim() !== "")
|
|
2955
3330
|
owned.add(branch2);
|
|
2956
3331
|
}
|
|
2957
3332
|
}
|
|
2958
|
-
if (
|
|
3333
|
+
if (isPlainObject6(meta) && typeof meta.working_branch === "string" && meta.working_branch.trim() !== "") {
|
|
2959
3334
|
owned.add(meta.working_branch);
|
|
2960
3335
|
}
|
|
2961
3336
|
}
|
|
@@ -2963,8 +3338,8 @@ function collectActiveLifecycleBranches(snapshots) {
|
|
|
2963
3338
|
return [...owned];
|
|
2964
3339
|
}
|
|
2965
3340
|
function scanActiveLifecycleBranches(harnessDir, governingWorkflowId) {
|
|
2966
|
-
const registerPath =
|
|
2967
|
-
if (!
|
|
3341
|
+
const registerPath = join12(harnessDir, "status.json");
|
|
3342
|
+
if (!existsSync10(registerPath))
|
|
2968
3343
|
return { kind: "ok", branches: [], notes: [] };
|
|
2969
3344
|
let register;
|
|
2970
3345
|
try {
|
|
@@ -2972,7 +3347,7 @@ function scanActiveLifecycleBranches(harnessDir, governingWorkflowId) {
|
|
|
2972
3347
|
} catch (error) {
|
|
2973
3348
|
return { kind: "refusal", code: "worktree.l1.lifecycle-register-unreadable", detail: `${registerPath}: ${error.message}` };
|
|
2974
3349
|
}
|
|
2975
|
-
if (!
|
|
3350
|
+
if (!isPlainObject6(register) || register.version !== 2 || !Array.isArray(register.workflows)) {
|
|
2976
3351
|
return {
|
|
2977
3352
|
kind: "refusal",
|
|
2978
3353
|
code: "worktree.l1.lifecycle-register-unreadable",
|
|
@@ -3003,10 +3378,10 @@ function scanActiveLifecycleBranches(harnessDir, governingWorkflowId) {
|
|
|
3003
3378
|
}
|
|
3004
3379
|
if (id === governingWorkflowId)
|
|
3005
3380
|
continue;
|
|
3006
|
-
const snapshotPath =
|
|
3381
|
+
const snapshotPath = join12(workflowsDir, id, WORKFLOW_SNAPSHOT_FILE);
|
|
3007
3382
|
let snapshot;
|
|
3008
3383
|
try {
|
|
3009
|
-
const read = readWorkflowSnapshot(
|
|
3384
|
+
const read = readWorkflowSnapshot(dirname8(snapshotPath));
|
|
3010
3385
|
snapshot = read.snapshot;
|
|
3011
3386
|
notes.push(...read.diagnostics);
|
|
3012
3387
|
} catch (error) {
|
|
@@ -3059,14 +3434,14 @@ function gitOut(cwd, args) {
|
|
|
3059
3434
|
}
|
|
3060
3435
|
}
|
|
3061
3436
|
function probeHarnessWithStatus(root) {
|
|
3062
|
-
if (isFile2(
|
|
3063
|
-
return
|
|
3064
|
-
if (isFile2(
|
|
3065
|
-
return
|
|
3066
|
-
if (hasWorkflowSnapshot(
|
|
3067
|
-
return
|
|
3068
|
-
if (hasWorkflowSnapshot(
|
|
3069
|
-
return
|
|
3437
|
+
if (isFile2(join13(root, ".mstar", "status.json")))
|
|
3438
|
+
return join13(root, ".mstar");
|
|
3439
|
+
if (isFile2(join13(root, ".agents", "status.json")))
|
|
3440
|
+
return join13(root, ".agents");
|
|
3441
|
+
if (hasWorkflowSnapshot(join13(root, ".mstar")))
|
|
3442
|
+
return join13(root, ".mstar");
|
|
3443
|
+
if (hasWorkflowSnapshot(join13(root, ".agents")))
|
|
3444
|
+
return join13(root, ".agents");
|
|
3070
3445
|
return null;
|
|
3071
3446
|
}
|
|
3072
3447
|
function hasWorkflowSnapshot(harnessDir) {
|
|
@@ -3074,13 +3449,13 @@ function hasWorkflowSnapshot(harnessDir) {
|
|
|
3074
3449
|
try {
|
|
3075
3450
|
workflowsDir = resolveWorkflowDir(harnessDir, { harnessDir });
|
|
3076
3451
|
} catch {
|
|
3077
|
-
workflowsDir =
|
|
3452
|
+
workflowsDir = join13(harnessDir, "workflows");
|
|
3078
3453
|
}
|
|
3079
3454
|
if (!isDirectory2(workflowsDir))
|
|
3080
3455
|
return false;
|
|
3081
3456
|
try {
|
|
3082
|
-
for (const entry of
|
|
3083
|
-
if (entry.isDirectory() && isFile2(
|
|
3457
|
+
for (const entry of readdirSync7(workflowsDir, { withFileTypes: true })) {
|
|
3458
|
+
if (entry.isDirectory() && isFile2(join13(workflowsDir, entry.name, "snapshot.json")))
|
|
3084
3459
|
return true;
|
|
3085
3460
|
}
|
|
3086
3461
|
} catch {
|
|
@@ -3089,16 +3464,16 @@ function hasWorkflowSnapshot(harnessDir) {
|
|
|
3089
3464
|
return false;
|
|
3090
3465
|
}
|
|
3091
3466
|
function checkoutRootNearestExisting(dir, boundary) {
|
|
3092
|
-
const stop =
|
|
3093
|
-
const startInside = isInside(
|
|
3094
|
-
let current =
|
|
3467
|
+
const stop = resolve11(boundary);
|
|
3468
|
+
const startInside = isInside(resolve11(dir), stop);
|
|
3469
|
+
let current = resolve11(dir);
|
|
3095
3470
|
for (;; ) {
|
|
3096
3471
|
const probed = probeCheckoutRoot(current);
|
|
3097
3472
|
if (probed !== null)
|
|
3098
3473
|
return probed;
|
|
3099
3474
|
if (startInside && current === stop)
|
|
3100
3475
|
return null;
|
|
3101
|
-
const parent =
|
|
3476
|
+
const parent = dirname9(current);
|
|
3102
3477
|
if (parent === current)
|
|
3103
3478
|
return null;
|
|
3104
3479
|
if (startInside && !isInside(parent, stop))
|
|
@@ -3118,7 +3493,7 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
3118
3493
|
if (!isDirectory2(controlRoot)) {
|
|
3119
3494
|
throw new SddScriptError(`mstar sdd workspace: CONTROL_ROOT / MSTAR_CONTROL_ROOT is not a directory: ${controlRoot}`, 1);
|
|
3120
3495
|
}
|
|
3121
|
-
const supplied =
|
|
3496
|
+
const supplied = realpathSync5(controlRoot);
|
|
3122
3497
|
const main = readMainWorktree(supplied);
|
|
3123
3498
|
if (main !== null) {
|
|
3124
3499
|
if (main.root !== supplied) {
|
|
@@ -3127,7 +3502,7 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
3127
3502
|
` + ` Re-run with MSTAR_CONTROL_ROOT=${main.root}`, 1);
|
|
3128
3503
|
}
|
|
3129
3504
|
root = main.root;
|
|
3130
|
-
} else if (isFile2(
|
|
3505
|
+
} else if (isFile2(join13(supplied, ".git")) || gitOut(supplied, ["rev-parse", "--is-inside-work-tree"]) === "true") {
|
|
3131
3506
|
throw new SddScriptError(`mstar sdd workspace: cannot verify the main worktree for CONTROL_ROOT "${supplied}" (git worktree discovery failed).
|
|
3132
3507
|
Refusing to resolve or create any SDD tree without a verified main worktree.`, 1);
|
|
3133
3508
|
} else {
|
|
@@ -3145,22 +3520,22 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
3145
3520
|
const harnessOverride = opts.harnessDir ?? (process.env.MSTAR_HARNESS_DIR || undefined);
|
|
3146
3521
|
let harnessDir;
|
|
3147
3522
|
if (harnessOverride) {
|
|
3148
|
-
harnessDir =
|
|
3523
|
+
harnessDir = resolve11(root, harnessOverride);
|
|
3149
3524
|
} else {
|
|
3150
3525
|
const rc = findMstarc(root, root);
|
|
3151
|
-
const rcHarnessDir = rc !== null ? parseMstarc(
|
|
3526
|
+
const rcHarnessDir = rc !== null ? parseMstarc(readFileSync9(rc, "utf8")).harnessDir : undefined;
|
|
3152
3527
|
if (rcHarnessDir) {
|
|
3153
|
-
harnessDir =
|
|
3528
|
+
harnessDir = resolve11(rc !== null ? dirname9(rc) : root, rcHarnessDir);
|
|
3154
3529
|
} else {
|
|
3155
3530
|
const probed = probeHarnessWithStatus(root);
|
|
3156
3531
|
if (probed) {
|
|
3157
3532
|
harnessDir = probed;
|
|
3158
|
-
} else if (isDirectory2(
|
|
3159
|
-
harnessDir =
|
|
3160
|
-
} else if (isDirectory2(
|
|
3161
|
-
harnessDir =
|
|
3533
|
+
} else if (isDirectory2(join13(root, ".mstar"))) {
|
|
3534
|
+
harnessDir = join13(root, ".mstar");
|
|
3535
|
+
} else if (isDirectory2(join13(root, ".agents"))) {
|
|
3536
|
+
harnessDir = join13(root, ".agents");
|
|
3162
3537
|
} else {
|
|
3163
|
-
harnessDir =
|
|
3538
|
+
harnessDir = join13(root, ".mstar");
|
|
3164
3539
|
}
|
|
3165
3540
|
}
|
|
3166
3541
|
}
|
|
@@ -3171,9 +3546,9 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
3171
3546
|
}
|
|
3172
3547
|
const sddDir = resolveSddDir(harnessDir, planId);
|
|
3173
3548
|
mkdirSync6(sddDir, { recursive: true });
|
|
3174
|
-
writeFileSync4(
|
|
3549
|
+
writeFileSync4(join13(sddDir, ".gitignore"), `*
|
|
3175
3550
|
`);
|
|
3176
|
-
return
|
|
3551
|
+
return realpathSync5(sddDir);
|
|
3177
3552
|
}
|
|
3178
3553
|
function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
3179
3554
|
if (!planFile || !Number.isInteger(taskN) || taskN < 1) {
|
|
@@ -3182,7 +3557,7 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
|
3182
3557
|
const bound = opts.context;
|
|
3183
3558
|
const observedCwd = opts.cwd ?? process.cwd();
|
|
3184
3559
|
if (bound) {
|
|
3185
|
-
const inputPlan = canonicalizeNearestExisting(
|
|
3560
|
+
const inputPlan = canonicalizeNearestExisting(resolve11(observedCwd, planFile));
|
|
3186
3561
|
if (inputPlan !== canonicalizeNearestExisting(bound.planFile)) {
|
|
3187
3562
|
throwGateFail([
|
|
3188
3563
|
contextViolation("high", "sdd.context.plan-file-mismatch", `plan file "${planFile}" does not match the bound context plan file "${bound.planFile}" — ` + "bound mode extracts only the resolved context's plan; refused before any read or write")
|
|
@@ -3191,16 +3566,16 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
|
3191
3566
|
}
|
|
3192
3567
|
let content;
|
|
3193
3568
|
try {
|
|
3194
|
-
content =
|
|
3569
|
+
content = readFileSync9(planFile, "utf8");
|
|
3195
3570
|
} catch {
|
|
3196
3571
|
throw new SddScriptError(`no such plan file: ${planFile}`, 2);
|
|
3197
3572
|
}
|
|
3198
3573
|
let out;
|
|
3199
3574
|
let mkdirAfterGate = null;
|
|
3200
3575
|
if (outFile) {
|
|
3201
|
-
out = bound ?
|
|
3576
|
+
out = bound ? resolve11(observedCwd, outFile) : outFile;
|
|
3202
3577
|
} else if (bound) {
|
|
3203
|
-
out =
|
|
3578
|
+
out = join13(bound.sddDir, `task-${taskN}-brief.md`);
|
|
3204
3579
|
mkdirAfterGate = bound.sddDir;
|
|
3205
3580
|
} else {
|
|
3206
3581
|
const sddDir = opts.sddDir ?? process.env.SDD_DIR;
|
|
@@ -3208,7 +3583,7 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
|
3208
3583
|
throw new SddScriptError("mstar sdd task-brief: set SDD_DIR or pass OUTFILE (run mstar sdd workspace PLAN_ID first)", 2);
|
|
3209
3584
|
}
|
|
3210
3585
|
mkdirSync6(sddDir, { recursive: true });
|
|
3211
|
-
out =
|
|
3586
|
+
out = join13(sddDir, `task-${taskN}-brief.md`);
|
|
3212
3587
|
}
|
|
3213
3588
|
if (bound) {
|
|
3214
3589
|
const gate2 = checkSddAction(bound, { kind: "artifact", cwd: observedCwd, target: out });
|
|
@@ -3241,15 +3616,15 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
|
3241
3616
|
if (printed.length === 0) {
|
|
3242
3617
|
throw new SddScriptError(`task ${taskN} not found in ${planFile} (no heading matching Task ${taskN})`, 3);
|
|
3243
3618
|
}
|
|
3244
|
-
return bound ?
|
|
3619
|
+
return bound ? resolve11(observedCwd, out) : out;
|
|
3245
3620
|
}
|
|
3246
3621
|
function isInside(child, ancestor) {
|
|
3247
3622
|
const rel = relative3(ancestor, child);
|
|
3248
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
3623
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute8(rel);
|
|
3249
3624
|
}
|
|
3250
3625
|
function canonicalDir(path) {
|
|
3251
3626
|
try {
|
|
3252
|
-
return statSync4(path).isDirectory() ?
|
|
3627
|
+
return statSync4(path).isDirectory() ? realpathSync5(path) : null;
|
|
3253
3628
|
} catch {
|
|
3254
3629
|
return null;
|
|
3255
3630
|
}
|
|
@@ -3279,11 +3654,11 @@ function checkSddAction(context, action) {
|
|
|
3279
3654
|
const featureReal = canonicalDir(context.featureCwd);
|
|
3280
3655
|
const canonicalSddDir = canonicalizeNearestExisting(context.sddDir);
|
|
3281
3656
|
const canonicalPlanFile = canonicalizeNearestExisting(context.planFile);
|
|
3282
|
-
const cwdResolved =
|
|
3657
|
+
const cwdResolved = resolve11(action.cwd);
|
|
3283
3658
|
const checkTarget = (baseDir, target, kind) => {
|
|
3284
3659
|
if (featureReal === null)
|
|
3285
3660
|
return;
|
|
3286
|
-
const targetAbs =
|
|
3661
|
+
const targetAbs = resolve11(baseDir, target);
|
|
3287
3662
|
const canonical = canonicalizeNearestExisting(targetAbs);
|
|
3288
3663
|
if (!isInside(canonical, featureReal)) {
|
|
3289
3664
|
const declaredPrefix = targetAbs === featureReal || targetAbs.startsWith(`${featureReal}/`);
|
|
@@ -3311,10 +3686,10 @@ function checkSddAction(context, action) {
|
|
|
3311
3686
|
add("sdd.context.target-missing", "artifact checks require the destination target");
|
|
3312
3687
|
return { ok: false, violations };
|
|
3313
3688
|
}
|
|
3314
|
-
const targetAbs =
|
|
3689
|
+
const targetAbs = resolve11(cwdResolved, action.target);
|
|
3315
3690
|
const canonical = canonicalizeNearestExisting(targetAbs);
|
|
3316
3691
|
if (!isInside(canonical, canonicalSddDir) && canonical !== canonicalPlanFile) {
|
|
3317
|
-
const rawSddDir =
|
|
3692
|
+
const rawSddDir = resolve11(context.sddDir);
|
|
3318
3693
|
const declaredPrefix = targetAbs === rawSddDir || targetAbs.startsWith(`${rawSddDir}/`) || targetAbs === canonicalSddDir || targetAbs.startsWith(`${canonicalSddDir}/`) || targetAbs === canonicalPlanFile;
|
|
3319
3694
|
if (declaredPrefix) {
|
|
3320
3695
|
add("sdd.context.artifact-symlink-escape", `artifact target "${targetAbs}" canonicalizes to "${canonical}", outside the plan's control sddDir — symlink escape refused before write`);
|
|
@@ -4047,7 +4422,7 @@ function referenceExists(repoRoot, docText) {
|
|
|
4047
4422
|
if (ref === "" || seen.has(ref))
|
|
4048
4423
|
continue;
|
|
4049
4424
|
seen.add(ref);
|
|
4050
|
-
if (SCHEME_RE.test(ref) || ref.startsWith("{") || ref.startsWith("#") || ref.includes("*") || ref.includes("?") || ref.startsWith("~") ||
|
|
4425
|
+
if (SCHEME_RE.test(ref) || ref.startsWith("{") || ref.startsWith("#") || ref.includes("*") || ref.includes("?") || ref.startsWith("~") || isAbsolute10(ref)) {
|
|
4051
4426
|
continue;
|
|
4052
4427
|
}
|
|
4053
4428
|
if (ref.includes("/") || REF_EXT_RE.test(ref)) {
|
|
@@ -4066,7 +4441,7 @@ function referenceExists(repoRoot, docText) {
|
|
|
4066
4441
|
const dir = stack.pop();
|
|
4067
4442
|
let entries;
|
|
4068
4443
|
try {
|
|
4069
|
-
entries =
|
|
4444
|
+
entries = readdirSync10(dir, { withFileTypes: true });
|
|
4070
4445
|
} catch {
|
|
4071
4446
|
continue;
|
|
4072
4447
|
}
|
|
@@ -4075,7 +4450,7 @@ function referenceExists(repoRoot, docText) {
|
|
|
4075
4450
|
break;
|
|
4076
4451
|
if (entry.isDirectory()) {
|
|
4077
4452
|
if (!WALK_SKIP_DIRS.has(entry.name))
|
|
4078
|
-
stack.push(
|
|
4453
|
+
stack.push(join17(dir, entry.name));
|
|
4079
4454
|
} else if (!entry.isSymbolicLink()) {
|
|
4080
4455
|
const base = entry.name.replace(/\.(?:ts|tsx|js|jsx|mjs|cjs)$/, "");
|
|
4081
4456
|
if (moduleNames.has(base))
|
|
@@ -4087,7 +4462,7 @@ function referenceExists(repoRoot, docText) {
|
|
|
4087
4462
|
for (const { ref, isSymbol, module } of refs) {
|
|
4088
4463
|
if (!isSymbol || module === undefined) {
|
|
4089
4464
|
const candidate = ref.replace(LINE_SUFFIX_RE, "").replace(ANCHOR_RE, "");
|
|
4090
|
-
if (
|
|
4465
|
+
if (existsSync13(resolve15(repoRoot, candidate))) {
|
|
4091
4466
|
checked++;
|
|
4092
4467
|
} else {
|
|
4093
4468
|
violations.push(violation11("medium", "compound.reference.missing-file", `referenced path \`${ref}\` does not exist under ${repoRoot} (compound-refresh Phase 2: referenced code still exists?)`, "update the doc to reference an existing path, or delete the stale reference"));
|
|
@@ -4107,18 +4482,18 @@ function collectKnowledgeDocs(dir) {
|
|
|
4107
4482
|
const current = stack.pop();
|
|
4108
4483
|
let entries;
|
|
4109
4484
|
try {
|
|
4110
|
-
entries =
|
|
4485
|
+
entries = readdirSync10(current, { withFileTypes: true });
|
|
4111
4486
|
} catch {
|
|
4112
4487
|
continue;
|
|
4113
4488
|
}
|
|
4114
4489
|
for (const entry of entries) {
|
|
4115
4490
|
if (entry.isSymbolicLink())
|
|
4116
4491
|
continue;
|
|
4117
|
-
const full =
|
|
4492
|
+
const full = join17(current, entry.name);
|
|
4118
4493
|
if (entry.isDirectory()) {
|
|
4119
4494
|
stack.push(full);
|
|
4120
4495
|
} else if (entry.name.endsWith(".md") && entry.name !== "README.md" && entry.name !== "index.md") {
|
|
4121
|
-
docs.push(relative6(dir, full).split(
|
|
4496
|
+
docs.push(relative6(dir, full).split(sep5).join("/"));
|
|
4122
4497
|
}
|
|
4123
4498
|
}
|
|
4124
4499
|
}
|
|
@@ -4134,14 +4509,14 @@ function normalizeIndexRef(cell) {
|
|
|
4134
4509
|
}
|
|
4135
4510
|
function assertIndexRows(knowledgeDir) {
|
|
4136
4511
|
const violations = [];
|
|
4137
|
-
const readmePath =
|
|
4138
|
-
if (!
|
|
4512
|
+
const readmePath = join17(knowledgeDir, "README.md");
|
|
4513
|
+
if (!existsSync13(readmePath)) {
|
|
4139
4514
|
violations.push(violation11("medium", "compound.index.missing-readme", `missing ${readmePath} — the knowledge index is required (mstar-compound Phase 6: every doc gets a README.md row)`, "create knowledge/README.md with a Document / Source Plan / Description / Status table"));
|
|
4140
4515
|
return { ok: false, violations };
|
|
4141
4516
|
}
|
|
4142
4517
|
const docs = collectKnowledgeDocs(knowledgeDir);
|
|
4143
4518
|
const rows = new Set;
|
|
4144
|
-
for (const line of
|
|
4519
|
+
for (const line of readFileSync12(readmePath, "utf8").split(/\r?\n/)) {
|
|
4145
4520
|
if (!line.trim().startsWith("|"))
|
|
4146
4521
|
continue;
|
|
4147
4522
|
const cells = line.split("|").map((c) => c.trim());
|
|
@@ -4159,16 +4534,16 @@ function assertIndexRows(knowledgeDir) {
|
|
|
4159
4534
|
return { ok: violations.length === 0, violations };
|
|
4160
4535
|
}
|
|
4161
4536
|
function isFileLikeRoot(root) {
|
|
4162
|
-
return /^[^.]*\.[A-Za-z0-9]{1,10}$/.test(
|
|
4537
|
+
return /^[^.]*\.[A-Za-z0-9]{1,10}$/.test(basename11(root));
|
|
4163
4538
|
}
|
|
4164
4539
|
function scopeGuard(path, allowedRoots) {
|
|
4165
|
-
const resolved =
|
|
4540
|
+
const resolved = resolve15(path);
|
|
4166
4541
|
for (const root of allowedRoots) {
|
|
4167
|
-
const r =
|
|
4542
|
+
const r = resolve15(root);
|
|
4168
4543
|
if (isFileLikeRoot(r)) {
|
|
4169
4544
|
if (resolved === r)
|
|
4170
4545
|
return { ok: true, violations: [] };
|
|
4171
|
-
} else if (resolved === r || resolved.startsWith(r +
|
|
4546
|
+
} else if (resolved === r || resolved.startsWith(r + sep5)) {
|
|
4172
4547
|
return { ok: true, violations: [] };
|
|
4173
4548
|
}
|
|
4174
4549
|
}
|
|
@@ -4324,8 +4699,8 @@ function validateRoleMapping(rolesDir, options = {}) {
|
|
|
4324
4699
|
const violations = [];
|
|
4325
4700
|
const referenceById = new Map(mapping.map((m) => [m.agentId, m.reference]));
|
|
4326
4701
|
for (const { agentId, reference } of mapping) {
|
|
4327
|
-
if (!
|
|
4328
|
-
violations.push(violation13("medium", "roles.mapping.reference.missing", `role "${agentId}" maps to ${reference} which does not exist under ${rolesDir} (mstar-roles § Role Reference Mapping)`, `create ${
|
|
4702
|
+
if (!existsSync14(join18(rolesDir, reference))) {
|
|
4703
|
+
violations.push(violation13("medium", "roles.mapping.reference.missing", `role "${agentId}" maps to ${reference} which does not exist under ${rolesDir} (mstar-roles § Role Reference Mapping)`, `create ${join18(rolesDir, reference)} or fix the mapping row`));
|
|
4329
4704
|
}
|
|
4330
4705
|
}
|
|
4331
4706
|
for (const { family, memberIds } of families) {
|
|
@@ -4584,7 +4959,7 @@ class DshMstar extends Service {
|
|
|
4584
4959
|
// src/gates/_shared.ts
|
|
4585
4960
|
var import_schemastery = __toESM(require_lib2(), 1);
|
|
4586
4961
|
import { existsSync as existsSync2 } from "node:fs";
|
|
4587
|
-
import { resolve as
|
|
4962
|
+
import { resolve as resolve5 } from "node:path";
|
|
4588
4963
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4589
4964
|
var STATUS_FILE = "status.json";
|
|
4590
4965
|
var PERSONA_INTERPOLATION_HAZARD = /\{\{[\s\S]*\}\}/;
|
|
@@ -4683,14 +5058,14 @@ class HarnessResolver {
|
|
|
4683
5058
|
explicit;
|
|
4684
5059
|
cache = new Map;
|
|
4685
5060
|
constructor(explicit) {
|
|
4686
|
-
this.explicit = explicit !== undefined && explicit.trim() !== "" ?
|
|
5061
|
+
this.explicit = explicit !== undefined && explicit.trim() !== "" ? resolve5(process.cwd(), explicit) : null;
|
|
4687
5062
|
}
|
|
4688
5063
|
forWorkspace(cwd) {
|
|
4689
5064
|
if (this.explicit !== null)
|
|
4690
5065
|
return this.explicit;
|
|
4691
5066
|
if (cwd === undefined || cwd.trim() === "")
|
|
4692
5067
|
return null;
|
|
4693
|
-
const abs =
|
|
5068
|
+
const abs = resolve5(cwd);
|
|
4694
5069
|
const hit = this.cache.get(abs);
|
|
4695
5070
|
if (hit !== undefined)
|
|
4696
5071
|
return hit;
|
|
@@ -4761,21 +5136,21 @@ function skillLocalConfig(config) {
|
|
|
4761
5136
|
}
|
|
4762
5137
|
|
|
4763
5138
|
// src/gates/catalog.ts
|
|
4764
|
-
import { existsSync as
|
|
4765
|
-
import { basename as
|
|
5139
|
+
import { existsSync as existsSync15, readFileSync as readFileSync10, readdirSync as readdirSync5, realpathSync as realpathSync6 } from "node:fs";
|
|
5140
|
+
import { basename as basename6, isAbsolute as isAbsolute9, join as join19, relative as relative7, sep as sep3 } from "node:path";
|
|
4766
5141
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
4767
5142
|
|
|
4768
5143
|
// src/gates/agent-flow.ts
|
|
4769
|
-
import { appendFileSync, closeSync, existsSync as
|
|
4770
|
-
import { join as
|
|
5144
|
+
import { appendFileSync, closeSync, existsSync as existsSync11, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync3, readSync, renameSync as renameSync2, rmdirSync, statSync as statSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5145
|
+
import { join as join14 } from "node:path";
|
|
4771
5146
|
|
|
4772
5147
|
// src/gates/dispatch.ts
|
|
4773
|
-
import { existsSync as
|
|
4774
|
-
import { basename as
|
|
5148
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
5149
|
+
import { basename as basename3, join as join8, resolve as resolve8 } from "node:path";
|
|
4775
5150
|
|
|
4776
5151
|
// src/gates/workflow-selection.ts
|
|
4777
|
-
import { existsSync as
|
|
4778
|
-
import { dirname as
|
|
5152
|
+
import { existsSync as existsSync3, readdirSync, realpathSync, statSync as statSync2 } from "node:fs";
|
|
5153
|
+
import { dirname as dirname7, isAbsolute as isAbsolute4, join as join5, normalize, relative as relative4, sep } from "node:path";
|
|
4779
5154
|
var TERMINAL_STATUS_CACHE_MAX = 64;
|
|
4780
5155
|
var terminalStatusCache = new Map;
|
|
4781
5156
|
function terminalStatusOf(snapshotPath) {
|
|
@@ -4811,7 +5186,7 @@ function terminalStatusOf(snapshotPath) {
|
|
|
4811
5186
|
}
|
|
4812
5187
|
function stripTrailingSeparators(path) {
|
|
4813
5188
|
let out = path;
|
|
4814
|
-
while (out.length > 1 && out.endsWith(
|
|
5189
|
+
while (out.length > 1 && out.endsWith(sep))
|
|
4815
5190
|
out = out.slice(0, -1);
|
|
4816
5191
|
return out;
|
|
4817
5192
|
}
|
|
@@ -4821,13 +5196,13 @@ function lexical(path) {
|
|
|
4821
5196
|
function canonicalPath(path) {
|
|
4822
5197
|
const fallback = lexical(path);
|
|
4823
5198
|
try {
|
|
4824
|
-
return { kind: "ok", path: lexical(
|
|
5199
|
+
return { kind: "ok", path: lexical(realpathSync(path)) };
|
|
4825
5200
|
} catch (error) {
|
|
4826
5201
|
return error.code === "ENOENT" ? { kind: "missing", path: fallback } : { kind: "error" };
|
|
4827
5202
|
}
|
|
4828
5203
|
}
|
|
4829
5204
|
function within(root, cwd) {
|
|
4830
|
-
if (root === "" || cwd === "" || !
|
|
5205
|
+
if (root === "" || cwd === "" || !isAbsolute4(root) || !isAbsolute4(cwd))
|
|
4831
5206
|
return false;
|
|
4832
5207
|
const rootCanonical = canonicalPath(root);
|
|
4833
5208
|
if (rootCanonical.kind === "error")
|
|
@@ -4841,7 +5216,7 @@ function within(root, cwd) {
|
|
|
4841
5216
|
if (rootPath === cwdPath)
|
|
4842
5217
|
return true;
|
|
4843
5218
|
const rel = relative4(rootPath, cwdPath);
|
|
4844
|
-
return rel !== "" && !
|
|
5219
|
+
return rel !== "" && !isAbsolute4(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
4845
5220
|
}
|
|
4846
5221
|
function validLeases(snapshot) {
|
|
4847
5222
|
if (!Array.isArray(snapshot.plans))
|
|
@@ -4856,8 +5231,8 @@ function validLeases(snapshot) {
|
|
|
4856
5231
|
return leases;
|
|
4857
5232
|
}
|
|
4858
5233
|
function readSnapshot(harnessDir, entry) {
|
|
4859
|
-
const snapshotPath =
|
|
4860
|
-
if (!
|
|
5234
|
+
const snapshotPath = join5(harnessDir, entry.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
5235
|
+
if (!existsSync3(snapshotPath))
|
|
4861
5236
|
return;
|
|
4862
5237
|
try {
|
|
4863
5238
|
const snapshot = asRecord(readJson(snapshotPath));
|
|
@@ -4913,8 +5288,8 @@ function automaticBinding(harnessDir, entries, hint) {
|
|
|
4913
5288
|
return byCwd === undefined ? undefined : { kind: "active", workflowId: byCwd.id, dir: byCwd.dir };
|
|
4914
5289
|
}
|
|
4915
5290
|
function resolveActiveWorkflow(harnessDir, hint) {
|
|
4916
|
-
const statusPath =
|
|
4917
|
-
if (!
|
|
5291
|
+
const statusPath = join5(harnessDir, STATUS_FILE);
|
|
5292
|
+
if (!existsSync3(statusPath)) {
|
|
4918
5293
|
return {
|
|
4919
5294
|
kind: "error",
|
|
4920
5295
|
code: "status.missing",
|
|
@@ -5004,7 +5379,7 @@ function resolveReadWorkflow(harnessDir, hint) {
|
|
|
5004
5379
|
if (active.kind === "error" && active.code !== "workflow.selection.no-active")
|
|
5005
5380
|
return active;
|
|
5006
5381
|
const workflowsDir = resolveWorkflowDir(harnessDir, { harnessDir });
|
|
5007
|
-
if (!
|
|
5382
|
+
if (!existsSync3(workflowsDir)) {
|
|
5008
5383
|
return {
|
|
5009
5384
|
kind: "error",
|
|
5010
5385
|
code: "workflow.selection.no-snapshot",
|
|
@@ -5025,8 +5400,8 @@ function resolveReadWorkflow(harnessDir, hint) {
|
|
|
5025
5400
|
for (const entry of entries) {
|
|
5026
5401
|
if (!entry.isDirectory())
|
|
5027
5402
|
continue;
|
|
5028
|
-
const snapshotPath =
|
|
5029
|
-
if (!
|
|
5403
|
+
const snapshotPath = join5(workflowsDir, entry.name, WORKFLOW_SNAPSHOT_FILE);
|
|
5404
|
+
if (!existsSync3(snapshotPath)) {
|
|
5030
5405
|
terminalStatusCache.delete(snapshotPath);
|
|
5031
5406
|
continue;
|
|
5032
5407
|
}
|
|
@@ -5034,11 +5409,11 @@ function resolveReadWorkflow(harnessDir, hint) {
|
|
|
5034
5409
|
if (verdict === undefined || !verdict.terminal)
|
|
5035
5410
|
continue;
|
|
5036
5411
|
if (best === undefined || verdict.mtimeMs > best.mtimeMs) {
|
|
5037
|
-
best = { workflowId: entry.name, dir:
|
|
5412
|
+
best = { workflowId: entry.name, dir: join5(relative4(harnessDir, workflowsDir), entry.name), mtimeMs: verdict.mtimeMs };
|
|
5038
5413
|
}
|
|
5039
5414
|
}
|
|
5040
5415
|
for (const cachedPath of terminalStatusCache.keys()) {
|
|
5041
|
-
if (!
|
|
5416
|
+
if (!existsSync3(join5(dirname7(cachedPath), WORKFLOW_SNAPSHOT_FILE))) {
|
|
5042
5417
|
terminalStatusCache.delete(cachedPath);
|
|
5043
5418
|
}
|
|
5044
5419
|
}
|
|
@@ -5207,12 +5582,12 @@ function isNaValue(value) {
|
|
|
5207
5582
|
function planIdOf(headerRegion) {
|
|
5208
5583
|
const planPath = assignmentHeaderValue(headerRegion, "Plan Path");
|
|
5209
5584
|
if (!isNaValue(planPath)) {
|
|
5210
|
-
const id =
|
|
5585
|
+
const id = basename3(firstToken(planPath) ?? "");
|
|
5211
5586
|
return id.endsWith(".md") ? id.slice(0, -3) : id;
|
|
5212
5587
|
}
|
|
5213
5588
|
const sddDir = assignmentHeaderValue(headerRegion, "SDD dir");
|
|
5214
5589
|
if (!isNaValue(sddDir)) {
|
|
5215
|
-
const id =
|
|
5590
|
+
const id = basename3(firstToken(sddDir) ?? "");
|
|
5216
5591
|
return id === "" ? undefined : id;
|
|
5217
5592
|
}
|
|
5218
5593
|
const planId = assignmentHeaderValue(headerRegion, "plan_id");
|
|
@@ -5237,12 +5612,12 @@ function leaseGateViolations(harnessDir, exec, writable, prompt, hint) {
|
|
|
5237
5612
|
if (!sdd)
|
|
5238
5613
|
return [];
|
|
5239
5614
|
if (selection.kind === "error" && selection.code === "status.unreadable") {
|
|
5240
|
-
return [leaseViolation("lease.dispatch.unreadable", `cannot read ${
|
|
5615
|
+
return [leaseViolation("lease.dispatch.unreadable", `cannot read ${join8(harnessDir, STATUS_FILE)}: the plan's execution_lease state is unverifiable; STOP before writable dispatch`, "restore a valid status.json (the status gate refuses invalid writes)")];
|
|
5241
5616
|
}
|
|
5242
|
-
return [leaseViolation("lease.dispatch.unverifiable", `${
|
|
5617
|
+
return [leaseViolation("lease.dispatch.unverifiable", `${join8(harnessDir, STATUS_FILE)}: ${selection.kind === "error" ? selection.message : "no active workflow"} — the plan's execution_lease state is unverifiable; STOP before writable dispatch`, "create a valid v2 status.json registering an active workflow (first implement dispatch requires a plan row + lease)")];
|
|
5243
5618
|
}
|
|
5244
|
-
const snapshotPath =
|
|
5245
|
-
if (!
|
|
5619
|
+
const snapshotPath = join8(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
5620
|
+
if (!existsSync7(snapshotPath)) {
|
|
5246
5621
|
if (!sdd)
|
|
5247
5622
|
return [];
|
|
5248
5623
|
return [leaseViolation("lease.dispatch.unverifiable", `${snapshotPath} is missing — the plan's execution_lease state is unverifiable; STOP before writable dispatch`, "create a valid workflow snapshot registering the plan row (first implement dispatch requires a plan row)")];
|
|
@@ -5275,7 +5650,7 @@ function leaseGateViolations(harnessDir, exec, writable, prompt, hint) {
|
|
|
5275
5650
|
const wt = worktree === undefined ? undefined : firstToken(worktree);
|
|
5276
5651
|
if (isNaValue(wt)) {
|
|
5277
5652
|
violations.push(leaseViolation("lease.dispatch.worktree-mismatch", "Assignment declares no Worktree path — cannot confirm this dispatch matches execution_lease.worktree_path", "add the absolute Worktree path to the Assignment (must equal the lease worktree_path)"));
|
|
5278
|
-
} else if (
|
|
5653
|
+
} else if (resolve8(wt) !== resolve8(String(lease.worktree_path ?? ""))) {
|
|
5279
5654
|
violations.push(leaseViolation("lease.dispatch.worktree-mismatch", `Assignment Worktree path "${wt}" differs from execution_lease.worktree_path "${String(lease.worktree_path)}"`, "align the Assignment with the lease worktree path (or update the lease)"));
|
|
5280
5655
|
}
|
|
5281
5656
|
const forms = parseAssignmentBranchForms(header);
|
|
@@ -5316,7 +5691,7 @@ function worktreeL2Violations(header) {
|
|
|
5316
5691
|
return violations;
|
|
5317
5692
|
}
|
|
5318
5693
|
function activeSnapshotRows(harnessDir, hint) {
|
|
5319
|
-
if (!
|
|
5694
|
+
if (!existsSync7(join8(harnessDir, STATUS_FILE)))
|
|
5320
5695
|
return { rows: null, snapshot: null, workflowId: null, unreadable: false };
|
|
5321
5696
|
const selection = resolveActiveWorkflow(harnessDir, hint);
|
|
5322
5697
|
if (selection.kind !== "active") {
|
|
@@ -5327,8 +5702,8 @@ function activeSnapshotRows(harnessDir, hint) {
|
|
|
5327
5702
|
unreadable: selection.kind === "error" && selection.code !== "workflow.selection.no-active"
|
|
5328
5703
|
};
|
|
5329
5704
|
}
|
|
5330
|
-
const snapshotDir =
|
|
5331
|
-
if (!
|
|
5705
|
+
const snapshotDir = join8(harnessDir, selection.dir);
|
|
5706
|
+
if (!existsSync7(join8(snapshotDir, WORKFLOW_SNAPSHOT_FILE))) {
|
|
5332
5707
|
return { rows: null, snapshot: null, workflowId: selection.workflowId, unreadable: true };
|
|
5333
5708
|
}
|
|
5334
5709
|
let snapshot;
|
|
@@ -5432,7 +5807,7 @@ function gateWorkflow(ctx, harnessDir, config, adapter, exec, hintReadOf) {
|
|
|
5432
5807
|
}
|
|
5433
5808
|
const pb = writableFanOutUncovered(harnessDir, hintReadOf().hint);
|
|
5434
5809
|
if (pb.unreadable) {
|
|
5435
|
-
const statusLabel = harnessDir === null ? STATUS_FILE :
|
|
5810
|
+
const statusLabel = harnessDir === null ? STATUS_FILE : join8(harnessDir, STATUS_FILE);
|
|
5436
5811
|
ctx.logger(DISPATCH_LOGGER).warn(`workflow gate (${toolName}) (fail-open): cannot read ${statusLabel} — P-b lease attribution degraded; call allowed`);
|
|
5437
5812
|
}
|
|
5438
5813
|
const verdict = workflowPolicy(config, adapter.workflowAskCache, {
|
|
@@ -5544,10 +5919,16 @@ function assignmentTextFromFields(fields) {
|
|
|
5544
5919
|
lines.push(`**Delegation**: ${fields.delegation}`);
|
|
5545
5920
|
if (fields.taskCategory !== undefined)
|
|
5546
5921
|
lines.push(`**Task category**: ${fields.taskCategory}`);
|
|
5922
|
+
if (fields.taskBudget !== undefined)
|
|
5923
|
+
lines.push(`**Task budget (implement / ops rounds)**: ${fields.taskBudget}`);
|
|
5547
5924
|
if (fields.workingBranch !== undefined)
|
|
5548
5925
|
lines.push(`**Working branch**: ${fields.workingBranch}`);
|
|
5549
5926
|
if (fields.branchPolicy !== undefined)
|
|
5550
5927
|
lines.push(`**Branch policy**: ${fields.branchPolicy}`);
|
|
5928
|
+
if (fields.budget !== undefined)
|
|
5929
|
+
lines.push(`**Budget (review / QC seats)**: ${fields.budget}`);
|
|
5930
|
+
if (fields.returnShape !== undefined)
|
|
5931
|
+
lines.push(`**Return shape (review / QC seats)**: ${fields.returnShape}`);
|
|
5551
5932
|
return lines.join(`
|
|
5552
5933
|
`);
|
|
5553
5934
|
}
|
|
@@ -5637,7 +6018,7 @@ function callPairingKey(exec) {
|
|
|
5637
6018
|
function resolveAgentFlowWriteTarget(harnessDir, hint) {
|
|
5638
6019
|
const selection = resolveActiveWorkflow(harnessDir, hint);
|
|
5639
6020
|
if (selection.kind === "active")
|
|
5640
|
-
return { dir:
|
|
6021
|
+
return { dir: join14(harnessDir, selection.dir), selection };
|
|
5641
6022
|
const message = selection.kind === "error" ? selection.message : "no active lifecycle (unexpected terminal selection — the active-set resolver has no history view)";
|
|
5642
6023
|
if (!noActiveWarned) {
|
|
5643
6024
|
noActiveWarned = true;
|
|
@@ -5657,7 +6038,7 @@ function sleepSync(ms) {
|
|
|
5657
6038
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
5658
6039
|
}
|
|
5659
6040
|
function withWorkflowDirLock(workflowDir, fn, opts = {}) {
|
|
5660
|
-
const lockDir =
|
|
6041
|
+
const lockDir = join14(workflowDir, WORKFLOW_LEDGER_LOCKDIR);
|
|
5661
6042
|
if (heldWorkflowLocks.has(lockDir)) {
|
|
5662
6043
|
throw new Error(`${lockDir} is already held by this process — withWorkflowDirLock is not reentrant; a nested acquisition on the same workflow dir is a bug`);
|
|
5663
6044
|
}
|
|
@@ -5683,7 +6064,7 @@ function withWorkflowDirLock(workflowDir, fn, opts = {}) {
|
|
|
5683
6064
|
heldWorkflowLocks.add(lockDir);
|
|
5684
6065
|
try {
|
|
5685
6066
|
try {
|
|
5686
|
-
writeFileSync2(
|
|
6067
|
+
writeFileSync2(join14(lockDir, WORKFLOW_LOCKER_HOLDER_PID), String(process.pid), "utf8");
|
|
5687
6068
|
} catch {}
|
|
5688
6069
|
return fn();
|
|
5689
6070
|
} finally {
|
|
@@ -5692,7 +6073,7 @@ function withWorkflowDirLock(workflowDir, fn, opts = {}) {
|
|
|
5692
6073
|
const current = statSync5(lockDir);
|
|
5693
6074
|
if (acquired !== null && current.dev === acquired.dev && current.ino === acquired.ino) {
|
|
5694
6075
|
try {
|
|
5695
|
-
unlinkSync2(
|
|
6076
|
+
unlinkSync2(join14(lockDir, WORKFLOW_LOCKER_HOLDER_PID));
|
|
5696
6077
|
} catch {}
|
|
5697
6078
|
rmdirSync(lockDir);
|
|
5698
6079
|
}
|
|
@@ -5700,13 +6081,13 @@ function withWorkflowDirLock(workflowDir, fn, opts = {}) {
|
|
|
5700
6081
|
}
|
|
5701
6082
|
}
|
|
5702
6083
|
function appendEvent(workflowDir, event) {
|
|
5703
|
-
const file =
|
|
6084
|
+
const file = join14(workflowDir, AGENT_FLOW_FILE);
|
|
5704
6085
|
withWorkflowDirLock(workflowDir, () => {
|
|
5705
6086
|
appendFileSync(file, `${JSON.stringify(event)}
|
|
5706
6087
|
`);
|
|
5707
6088
|
if (statSync5(file).size <= AGENT_FLOW_SIZE_GATE_BYTES)
|
|
5708
6089
|
return;
|
|
5709
|
-
const content =
|
|
6090
|
+
const content = readFileSync3(file, "utf8");
|
|
5710
6091
|
const lines = content.replace(/\n$/, "").split(`
|
|
5711
6092
|
`);
|
|
5712
6093
|
if (lines.length > AGENT_FLOW_MAX_EVENTS) {
|
|
@@ -6110,7 +6491,7 @@ function summaryOf(events) {
|
|
|
6110
6491
|
function ledgerContent(file, n) {
|
|
6111
6492
|
const size = statSync5(file).size;
|
|
6112
6493
|
if (size <= AGENT_FLOW_TAIL_READ_THRESHOLD_BYTES) {
|
|
6113
|
-
return
|
|
6494
|
+
return readFileSync3(file, "utf8");
|
|
6114
6495
|
}
|
|
6115
6496
|
let windowBytes = AGENT_FLOW_TAIL_READ_THRESHOLD_BYTES;
|
|
6116
6497
|
const fd = openSync(file, "r");
|
|
@@ -6142,8 +6523,8 @@ function ledgerContent(file, n) {
|
|
|
6142
6523
|
}
|
|
6143
6524
|
}
|
|
6144
6525
|
function readAgentFlow(workflowDir, limit) {
|
|
6145
|
-
const file =
|
|
6146
|
-
if (!
|
|
6526
|
+
const file = join14(workflowDir, AGENT_FLOW_FILE);
|
|
6527
|
+
if (!existsSync11(file)) {
|
|
6147
6528
|
return { events: [], summary: [] };
|
|
6148
6529
|
}
|
|
6149
6530
|
const n = limit === undefined ? AGENT_FLOW_DEFAULT_LIMIT : Math.max(0, Math.floor(limit));
|
|
@@ -6308,8 +6689,8 @@ function reserveCatalogCandidate(pairing, ref, exec) {
|
|
|
6308
6689
|
map.set(label, { ref, fromSeq: seq });
|
|
6309
6690
|
ref.catalog = { session, label };
|
|
6310
6691
|
}
|
|
6311
|
-
function matchCatalogEvent(session, label,
|
|
6312
|
-
const result =
|
|
6692
|
+
function matchCatalogEvent(session, label, join9, envelope) {
|
|
6693
|
+
const result = join9.result;
|
|
6313
6694
|
if (result === undefined)
|
|
6314
6695
|
return;
|
|
6315
6696
|
const rec = asRecord(envelope);
|
|
@@ -6323,7 +6704,7 @@ function matchCatalogEvent(session, label, join8, envelope) {
|
|
|
6323
6704
|
const seq = rec.seq;
|
|
6324
6705
|
if (typeof seq !== "number" || !Number.isInteger(seq))
|
|
6325
6706
|
return;
|
|
6326
|
-
if (seq <
|
|
6707
|
+
if (seq < join9.fromSeq || seq >= session.seq || seq >= WORKFLOW_LEDGER_MAX_SEQ)
|
|
6327
6708
|
return;
|
|
6328
6709
|
const childCreatedAt = data.childCreatedAt;
|
|
6329
6710
|
if (typeof childCreatedAt !== "number" || !Number.isInteger(childCreatedAt) || childCreatedAt < 0)
|
|
@@ -6339,9 +6720,9 @@ function matchCatalogEvent(session, label, join8, envelope) {
|
|
|
6339
6720
|
return;
|
|
6340
6721
|
return childId;
|
|
6341
6722
|
}
|
|
6342
|
-
function consumeCatalogCandidate(map, label,
|
|
6723
|
+
function consumeCatalogCandidate(map, label, join9, childId) {
|
|
6343
6724
|
map.set(label, null);
|
|
6344
|
-
const ref =
|
|
6725
|
+
const ref = join9.ref;
|
|
6345
6726
|
ref.childId = childId;
|
|
6346
6727
|
delete ref.catalog;
|
|
6347
6728
|
recordSubagentLink({ ref, childId, label });
|
|
@@ -6352,16 +6733,16 @@ function advanceCatalogCandidate(pairing, ref, result) {
|
|
|
6352
6733
|
if (catalog === undefined)
|
|
6353
6734
|
return;
|
|
6354
6735
|
const map = pairing.catalogBySession.get(catalog.session);
|
|
6355
|
-
const
|
|
6356
|
-
if (map === undefined ||
|
|
6736
|
+
const join9 = map?.get(catalog.label);
|
|
6737
|
+
if (map === undefined || join9 === undefined || join9 === null || join9.ref !== ref)
|
|
6357
6738
|
return;
|
|
6358
|
-
|
|
6739
|
+
join9.result = result;
|
|
6359
6740
|
const end = catalog.session.seq;
|
|
6360
6741
|
let skippedReads = 0;
|
|
6361
6742
|
let firstSkippedSeq;
|
|
6362
6743
|
let firstSkippedError;
|
|
6363
6744
|
try {
|
|
6364
|
-
for (let seq =
|
|
6745
|
+
for (let seq = join9.fromSeq;seq < end; seq += 1) {
|
|
6365
6746
|
let envelope;
|
|
6366
6747
|
try {
|
|
6367
6748
|
envelope = catalog.session.eventAt(seq);
|
|
@@ -6373,10 +6754,10 @@ function advanceCatalogCandidate(pairing, ref, result) {
|
|
|
6373
6754
|
}
|
|
6374
6755
|
continue;
|
|
6375
6756
|
}
|
|
6376
|
-
const childId = matchCatalogEvent(catalog.session, catalog.label,
|
|
6757
|
+
const childId = matchCatalogEvent(catalog.session, catalog.label, join9, envelope);
|
|
6377
6758
|
if (childId === undefined)
|
|
6378
6759
|
continue;
|
|
6379
|
-
consumeCatalogCandidate(map, catalog.label,
|
|
6760
|
+
consumeCatalogCandidate(map, catalog.label, join9, childId);
|
|
6380
6761
|
return;
|
|
6381
6762
|
}
|
|
6382
6763
|
} finally {
|
|
@@ -6451,13 +6832,13 @@ function registerSubagentCatalogListener(ctx, pairing) {
|
|
|
6451
6832
|
const map = pairing.catalogBySession.get(session);
|
|
6452
6833
|
if (map === undefined)
|
|
6453
6834
|
return;
|
|
6454
|
-
for (const [label,
|
|
6455
|
-
if (
|
|
6835
|
+
for (const [label, join9] of map) {
|
|
6836
|
+
if (join9 === null || join9.result === undefined)
|
|
6456
6837
|
continue;
|
|
6457
|
-
const childId = matchCatalogEvent(session, label,
|
|
6838
|
+
const childId = matchCatalogEvent(session, label, join9, envelope);
|
|
6458
6839
|
if (childId === undefined)
|
|
6459
6840
|
continue;
|
|
6460
|
-
consumeCatalogCandidate(map, label,
|
|
6841
|
+
consumeCatalogCandidate(map, label, join9, childId);
|
|
6461
6842
|
}
|
|
6462
6843
|
} catch (error) {
|
|
6463
6844
|
log("warn", `subagent catalog observation failed (contained — the session append proceeds): ${errorMessage(error)}`);
|
|
@@ -6466,8 +6847,8 @@ function registerSubagentCatalogListener(ctx, pairing) {
|
|
|
6466
6847
|
}
|
|
6467
6848
|
|
|
6468
6849
|
// src/engine-status-store.ts
|
|
6469
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
6470
|
-
import { dirname as
|
|
6850
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync3, rmSync, statSync as statSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6851
|
+
import { dirname as dirname10, join as join16 } from "node:path";
|
|
6471
6852
|
var ENGINE_STATUS_SNAPSHOT_VERSION = 1;
|
|
6472
6853
|
var ENGINE_STATUS_SNAPSHOT_ENTRY_VERSION = 1;
|
|
6473
6854
|
var ENGINE_STATUS_SNAPSHOT_MAX_PER_SESSION = 50;
|
|
@@ -6476,19 +6857,19 @@ var ENGINE_STATUS_SNAPSHOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
|
6476
6857
|
var ENGINE_STATUS_SNAPSHOT_LOCK_TIMEOUT_MS = 250;
|
|
6477
6858
|
var ENGINE_STATUS_SNAPSHOT_RELATIVE_PATH = "snapshots/engine-status.json";
|
|
6478
6859
|
function engineStatusSnapshotPath(harnessDir) {
|
|
6479
|
-
return
|
|
6860
|
+
return join16(harnessDir, ENGINE_STATUS_SNAPSHOT_RELATIVE_PATH);
|
|
6480
6861
|
}
|
|
6481
6862
|
function engineStatusUnavailable(reason) {
|
|
6482
6863
|
return { kind: "unavailable", reason };
|
|
6483
6864
|
}
|
|
6484
|
-
function
|
|
6865
|
+
function isPlainObject4(value) {
|
|
6485
6866
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6486
6867
|
}
|
|
6487
6868
|
function nonEmptyString(value) {
|
|
6488
6869
|
return typeof value === "string" && value !== "" ? value : undefined;
|
|
6489
6870
|
}
|
|
6490
6871
|
function asEntry(value) {
|
|
6491
|
-
if (!
|
|
6872
|
+
if (!isPlainObject4(value))
|
|
6492
6873
|
return;
|
|
6493
6874
|
if (value.rv !== ENGINE_STATUS_SNAPSHOT_ENTRY_VERSION)
|
|
6494
6875
|
return;
|
|
@@ -6498,12 +6879,12 @@ function asEntry(value) {
|
|
|
6498
6879
|
return;
|
|
6499
6880
|
if (typeof value.turn !== "number" || !Number.isFinite(value.turn))
|
|
6500
6881
|
return;
|
|
6501
|
-
if (!
|
|
6882
|
+
if (!isPlainObject4(value.payload))
|
|
6502
6883
|
return;
|
|
6503
6884
|
return { rv: value.rv, cwd, at, turn: value.turn, payload: value.payload };
|
|
6504
6885
|
}
|
|
6505
6886
|
function asBinding(value) {
|
|
6506
|
-
if (!
|
|
6887
|
+
if (!isPlainObject4(value))
|
|
6507
6888
|
return;
|
|
6508
6889
|
const cwd = nonEmptyString(value.cwd);
|
|
6509
6890
|
if (cwd === undefined)
|
|
@@ -6534,7 +6915,7 @@ function readEngineStatusSnapshot(harnessDir, sessionId) {
|
|
|
6534
6915
|
return engineStatusUnavailable("no-session-entry");
|
|
6535
6916
|
let raw;
|
|
6536
6917
|
try {
|
|
6537
|
-
raw =
|
|
6918
|
+
raw = readFileSync7(engineStatusSnapshotPath(harnessDir), "utf8");
|
|
6538
6919
|
} catch (error) {
|
|
6539
6920
|
const code = error.code;
|
|
6540
6921
|
return engineStatusUnavailable(code === "ENOENT" ? "absent" : "unreadable");
|
|
@@ -6545,11 +6926,11 @@ function readEngineStatusSnapshot(harnessDir, sessionId) {
|
|
|
6545
6926
|
} catch {
|
|
6546
6927
|
return engineStatusUnavailable("invalid-json");
|
|
6547
6928
|
}
|
|
6548
|
-
if (!
|
|
6929
|
+
if (!isPlainObject4(doc) || doc.sv !== ENGINE_STATUS_SNAPSHOT_VERSION) {
|
|
6549
6930
|
return engineStatusUnavailable("envelope-schema");
|
|
6550
6931
|
}
|
|
6551
6932
|
const entries = doc.entries;
|
|
6552
|
-
if (!
|
|
6933
|
+
if (!isPlainObject4(entries))
|
|
6553
6934
|
return engineStatusUnavailable("envelope-schema");
|
|
6554
6935
|
if (!Object.hasOwn(entries, sessionId))
|
|
6555
6936
|
return engineStatusUnavailable("no-session-entry");
|
|
@@ -6574,7 +6955,7 @@ function serializeEnvelope(entries, bindings) {
|
|
|
6574
6955
|
function loadForWrite(harnessDir) {
|
|
6575
6956
|
let raw;
|
|
6576
6957
|
try {
|
|
6577
|
-
raw =
|
|
6958
|
+
raw = readFileSync7(engineStatusSnapshotPath(harnessDir), "utf8");
|
|
6578
6959
|
} catch (error) {
|
|
6579
6960
|
if (error.code === "ENOENT") {
|
|
6580
6961
|
return {
|
|
@@ -6590,7 +6971,7 @@ function loadForWrite(harnessDir) {
|
|
|
6590
6971
|
} catch {
|
|
6591
6972
|
return { kind: "refused", reason: "store-invalid-json" };
|
|
6592
6973
|
}
|
|
6593
|
-
if (!
|
|
6974
|
+
if (!isPlainObject4(parsed) || parsed.sv !== ENGINE_STATUS_SNAPSHOT_VERSION || !isPlainObject4(parsed.entries)) {
|
|
6594
6975
|
return { kind: "refused", reason: "store-envelope-schema" };
|
|
6595
6976
|
}
|
|
6596
6977
|
const entries = sessionMap();
|
|
@@ -6609,7 +6990,7 @@ function loadForWrite(harnessDir) {
|
|
|
6609
6990
|
}
|
|
6610
6991
|
const bindings = bindingMap();
|
|
6611
6992
|
if (parsed.bindings !== undefined) {
|
|
6612
|
-
if (!
|
|
6993
|
+
if (!isPlainObject4(parsed.bindings))
|
|
6613
6994
|
return { kind: "refused", reason: "store-bindings-schema" };
|
|
6614
6995
|
for (const [key, value] of Object.entries(parsed.bindings)) {
|
|
6615
6996
|
const binding = asBinding(value);
|
|
@@ -6664,12 +7045,12 @@ function writeEngineStatusSnapshot(harnessDir, input) {
|
|
|
6664
7045
|
return { kind: "degraded", reason: "no-session-cwd" };
|
|
6665
7046
|
if (!Number.isFinite(input.turn))
|
|
6666
7047
|
return { kind: "degraded", reason: "invalid-turn" };
|
|
6667
|
-
if (!
|
|
7048
|
+
if (!isPlainObject4(input.payload))
|
|
6668
7049
|
return { kind: "degraded", reason: "payload-not-object" };
|
|
6669
7050
|
const at = (input.now ?? new Date).toISOString();
|
|
6670
7051
|
if (!Number.isFinite(Date.parse(at)))
|
|
6671
7052
|
return { kind: "degraded", reason: "invalid-timestamp" };
|
|
6672
|
-
const dir =
|
|
7053
|
+
const dir = dirname10(engineStatusSnapshotPath(harnessDir));
|
|
6673
7054
|
const file = engineStatusSnapshotPath(harnessDir);
|
|
6674
7055
|
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
|
|
6675
7056
|
try {
|
|
@@ -6732,7 +7113,7 @@ var bindingStoreMemos = new Map;
|
|
|
6732
7113
|
function parseBindingStore(file) {
|
|
6733
7114
|
let raw;
|
|
6734
7115
|
try {
|
|
6735
|
-
raw =
|
|
7116
|
+
raw = readFileSync7(file, "utf8");
|
|
6736
7117
|
} catch {
|
|
6737
7118
|
return { kind: "unavailable", reason: "store-unreadable" };
|
|
6738
7119
|
}
|
|
@@ -6742,13 +7123,13 @@ function parseBindingStore(file) {
|
|
|
6742
7123
|
} catch {
|
|
6743
7124
|
return { kind: "unavailable", reason: "store-invalid-json" };
|
|
6744
7125
|
}
|
|
6745
|
-
if (!
|
|
7126
|
+
if (!isPlainObject4(parsed) || parsed.sv !== ENGINE_STATUS_SNAPSHOT_VERSION || !isPlainObject4(parsed.entries)) {
|
|
6746
7127
|
return { kind: "unavailable", reason: "store-envelope-schema" };
|
|
6747
7128
|
}
|
|
6748
7129
|
const bindings = parsed.bindings;
|
|
6749
7130
|
if (bindings === undefined)
|
|
6750
7131
|
return { kind: "ok", bindings: undefined };
|
|
6751
|
-
if (!
|
|
7132
|
+
if (!isPlainObject4(bindings))
|
|
6752
7133
|
return { kind: "unavailable", reason: "store-bindings-schema" };
|
|
6753
7134
|
return { kind: "ok", bindings };
|
|
6754
7135
|
}
|
|
@@ -6806,7 +7187,7 @@ function updateWorkflowSessionBinding(harnessDir, sessionId, cwd, update) {
|
|
|
6806
7187
|
return { kind: "degraded", reason: "invalid-selected-workflow-id" };
|
|
6807
7188
|
}
|
|
6808
7189
|
const file = engineStatusSnapshotPath(harnessDir);
|
|
6809
|
-
const dir =
|
|
7190
|
+
const dir = dirname10(file);
|
|
6810
7191
|
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
|
|
6811
7192
|
try {
|
|
6812
7193
|
mkdirSync4(dir, { recursive: true });
|
|
@@ -6858,7 +7239,7 @@ function residualSeverityIndex(severity) {
|
|
|
6858
7239
|
function pluginVersion() {
|
|
6859
7240
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
6860
7241
|
try {
|
|
6861
|
-
const pkg = JSON.parse(
|
|
7242
|
+
const pkg = JSON.parse(readFileSync10(new URL(rel, import.meta.url), "utf8"));
|
|
6862
7243
|
if (typeof pkg.version === "string" && pkg.version !== "")
|
|
6863
7244
|
return pkg.version;
|
|
6864
7245
|
} catch {}
|
|
@@ -7024,8 +7405,8 @@ function hhmm(ts) {
|
|
|
7024
7405
|
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
7025
7406
|
}
|
|
7026
7407
|
function harnessStateSource(harnessDir, selection) {
|
|
7027
|
-
const statusPath =
|
|
7028
|
-
if (!
|
|
7408
|
+
const statusPath = join19(harnessDir, STATUS_FILE);
|
|
7409
|
+
if (!existsSync15(statusPath))
|
|
7029
7410
|
return null;
|
|
7030
7411
|
try {
|
|
7031
7412
|
const rollup = residualRollup(harnessDir);
|
|
@@ -7034,9 +7415,9 @@ function harnessStateSource(harnessDir, selection) {
|
|
|
7034
7415
|
if (selection.kind === "error") {
|
|
7035
7416
|
return selectionErrorState(selection, rollup, harnessDir);
|
|
7036
7417
|
}
|
|
7037
|
-
const snapshotPath =
|
|
7418
|
+
const snapshotPath = join19(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
7038
7419
|
let snapshot;
|
|
7039
|
-
if (!
|
|
7420
|
+
if (!existsSync15(snapshotPath)) {
|
|
7040
7421
|
return selectionErrorState({
|
|
7041
7422
|
kind: "error",
|
|
7042
7423
|
code: "workflow.selection.snapshot-unreadable",
|
|
@@ -7044,7 +7425,7 @@ function harnessStateSource(harnessDir, selection) {
|
|
|
7044
7425
|
}, rollup, harnessDir);
|
|
7045
7426
|
}
|
|
7046
7427
|
try {
|
|
7047
|
-
snapshot = readWorkflowSnapshot(
|
|
7428
|
+
snapshot = readWorkflowSnapshot(join19(harnessDir, selection.dir)).snapshot;
|
|
7048
7429
|
} catch {
|
|
7049
7430
|
return selectionErrorState({
|
|
7050
7431
|
kind: "error",
|
|
@@ -7100,7 +7481,7 @@ function harnessStateSource(harnessDir, selection) {
|
|
|
7100
7481
|
leases,
|
|
7101
7482
|
knowledge: knowledgeDigest(harnessDir),
|
|
7102
7483
|
direction: compass !== undefined ? compassDirection(compass.compassPath) : null,
|
|
7103
|
-
agentFlow: readAgentFlow(
|
|
7484
|
+
agentFlow: readAgentFlow(join19(harnessDir, selection.dir), 50)
|
|
7104
7485
|
};
|
|
7105
7486
|
} catch {
|
|
7106
7487
|
return null;
|
|
@@ -7130,21 +7511,21 @@ function selectionErrorState(selection, rollup, harnessDir) {
|
|
|
7130
7511
|
function projectRollupSource(harnessDir, residuals) {
|
|
7131
7512
|
const milestones = [];
|
|
7132
7513
|
const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
7133
|
-
if (
|
|
7514
|
+
if (existsSync15(projectsDir)) {
|
|
7134
7515
|
let entries;
|
|
7135
7516
|
try {
|
|
7136
|
-
entries =
|
|
7517
|
+
entries = readdirSync5(projectsDir, { withFileTypes: true });
|
|
7137
7518
|
} catch {
|
|
7138
7519
|
entries = [];
|
|
7139
7520
|
}
|
|
7140
7521
|
for (const entry of entries) {
|
|
7141
7522
|
if (!entry.isDirectory())
|
|
7142
7523
|
continue;
|
|
7143
|
-
const roadmapPath =
|
|
7144
|
-
if (!
|
|
7524
|
+
const roadmapPath = join19(projectsDir, entry.name, PROJECT_ROADMAP_FILE);
|
|
7525
|
+
if (!existsSync15(roadmapPath))
|
|
7145
7526
|
continue;
|
|
7146
7527
|
try {
|
|
7147
|
-
const doc = parseCompassFrontmatterText(
|
|
7528
|
+
const doc = parseCompassFrontmatterText(readFileSync10(roadmapPath, "utf8"), roadmapPath);
|
|
7148
7529
|
if (Array.isArray(doc.milestones)) {
|
|
7149
7530
|
for (const milestone of doc.milestones) {
|
|
7150
7531
|
if (typeof milestone === "string" && milestone.trim() !== "")
|
|
@@ -7206,11 +7587,11 @@ function residualRollup(harnessDir) {
|
|
|
7206
7587
|
}
|
|
7207
7588
|
function projectRegisters(harnessDir) {
|
|
7208
7589
|
const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
7209
|
-
if (!
|
|
7590
|
+
if (!existsSync15(projectsDir))
|
|
7210
7591
|
return [];
|
|
7211
7592
|
let entries;
|
|
7212
7593
|
try {
|
|
7213
|
-
entries =
|
|
7594
|
+
entries = readdirSync5(projectsDir, { withFileTypes: true });
|
|
7214
7595
|
} catch {
|
|
7215
7596
|
return [];
|
|
7216
7597
|
}
|
|
@@ -7218,8 +7599,8 @@ function projectRegisters(harnessDir) {
|
|
|
7218
7599
|
for (const entry of entries) {
|
|
7219
7600
|
if (!entry.isDirectory())
|
|
7220
7601
|
continue;
|
|
7221
|
-
const registerPath =
|
|
7222
|
-
if (!
|
|
7602
|
+
const registerPath = join19(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
7603
|
+
if (!existsSync15(registerPath))
|
|
7223
7604
|
continue;
|
|
7224
7605
|
try {
|
|
7225
7606
|
registers.push({ projectId: entry.name, doc: readJson(registerPath) });
|
|
@@ -7230,13 +7611,13 @@ function projectRegisters(harnessDir) {
|
|
|
7230
7611
|
return registers;
|
|
7231
7612
|
}
|
|
7232
7613
|
function knowledgeDigest(harnessDir) {
|
|
7233
|
-
const indexPath =
|
|
7234
|
-
if (!
|
|
7614
|
+
const indexPath = join19(harnessDir, "knowledge", "README.md");
|
|
7615
|
+
if (!existsSync15(indexPath))
|
|
7235
7616
|
return null;
|
|
7236
7617
|
try {
|
|
7237
7618
|
const categories = new Set;
|
|
7238
7619
|
let docCount = 0;
|
|
7239
|
-
for (const line of
|
|
7620
|
+
for (const line of readFileSync10(indexPath, "utf8").split(/\r?\n/)) {
|
|
7240
7621
|
const row = line.trim().match(/^\|(.+)\|$/);
|
|
7241
7622
|
if (row === null)
|
|
7242
7623
|
continue;
|
|
@@ -7259,7 +7640,7 @@ function knowledgeDigest(harnessDir) {
|
|
|
7259
7640
|
}
|
|
7260
7641
|
function compassDirection(compassPath) {
|
|
7261
7642
|
try {
|
|
7262
|
-
const content =
|
|
7643
|
+
const content = readFileSync10(compassPath, "utf8");
|
|
7263
7644
|
const section = content.match(/^## Direction lock[^\n]*\n+([\s\S]*?)(?=\n## |$)/m);
|
|
7264
7645
|
if (section === null)
|
|
7265
7646
|
return null;
|
|
@@ -7276,20 +7657,20 @@ function compassDirection(compassPath) {
|
|
|
7276
7657
|
}
|
|
7277
7658
|
function selectedCompass(harnessDir, workflowId, snapshot) {
|
|
7278
7659
|
const ref = snapshot.compass_ref;
|
|
7279
|
-
if (typeof ref !== "string" || ref.trim() === "" ||
|
|
7660
|
+
if (typeof ref !== "string" || ref.trim() === "" || isAbsolute9(ref))
|
|
7280
7661
|
return;
|
|
7281
|
-
const compassPath =
|
|
7662
|
+
const compassPath = join19(harnessDir, ref);
|
|
7282
7663
|
if (!pathInside(harnessDir, compassPath))
|
|
7283
7664
|
return;
|
|
7284
|
-
if (
|
|
7665
|
+
if (basename6(compassPath) !== "delivery-compass.md")
|
|
7285
7666
|
return;
|
|
7286
|
-
if (!
|
|
7667
|
+
if (!existsSync15(compassPath))
|
|
7287
7668
|
return;
|
|
7288
7669
|
let resolvedHarness;
|
|
7289
7670
|
let resolvedCompass;
|
|
7290
7671
|
try {
|
|
7291
|
-
resolvedHarness =
|
|
7292
|
-
resolvedCompass =
|
|
7672
|
+
resolvedHarness = realpathSync6(harnessDir);
|
|
7673
|
+
resolvedCompass = realpathSync6(compassPath);
|
|
7293
7674
|
} catch {
|
|
7294
7675
|
return;
|
|
7295
7676
|
}
|
|
@@ -7309,15 +7690,15 @@ function selectedCompass(harnessDir, workflowId, snapshot) {
|
|
|
7309
7690
|
}
|
|
7310
7691
|
function pathInside(root, candidate) {
|
|
7311
7692
|
const rel = relative7(root, candidate);
|
|
7312
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep3}`) && !
|
|
7693
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute9(rel);
|
|
7313
7694
|
}
|
|
7314
7695
|
function iterationGateSource(harnessDir, selection) {
|
|
7315
7696
|
if (harnessDir === null || selection === undefined || selection.kind === "error")
|
|
7316
7697
|
return;
|
|
7317
|
-
const statusPath =
|
|
7318
|
-
if (!
|
|
7698
|
+
const statusPath = join19(harnessDir, STATUS_FILE);
|
|
7699
|
+
if (!existsSync15(statusPath))
|
|
7319
7700
|
return;
|
|
7320
|
-
const snapshotPath =
|
|
7701
|
+
const snapshotPath = join19(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
7321
7702
|
try {
|
|
7322
7703
|
const snapshotDoc = readJson(snapshotPath);
|
|
7323
7704
|
const compass = selectedCompass(harnessDir, selection.workflowId, snapshotDoc);
|
|
@@ -7407,7 +7788,7 @@ function persistEngineStatusSnapshot(ctx, harnessDir, cwd, turn, catalogPayload,
|
|
|
7407
7788
|
}
|
|
7408
7789
|
|
|
7409
7790
|
// src/engine-status-endpoint.ts
|
|
7410
|
-
import { isAbsolute as
|
|
7791
|
+
import { isAbsolute as isAbsolute11 } from "node:path";
|
|
7411
7792
|
|
|
7412
7793
|
// ../../node_modules/.bun/@deepseek-ai+dsh-typert-protocol@0.1.5-rc.2+1e4e7adc57ac8890/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
|
|
7413
7794
|
import { Service as Service2 } from "@deepseek-ai/cordis";
|
|
@@ -7463,7 +7844,7 @@ function liveSeqOf(value) {
|
|
|
7463
7844
|
function isRefusedPath(cwd) {
|
|
7464
7845
|
if (cwd.includes("\x00"))
|
|
7465
7846
|
return true;
|
|
7466
|
-
if (!
|
|
7847
|
+
if (!isAbsolute11(cwd))
|
|
7467
7848
|
return true;
|
|
7468
7849
|
return cwd.split(/[\\/]+/).includes("..");
|
|
7469
7850
|
}
|
|
@@ -7699,12 +8080,12 @@ function installEngineStatusEndpoint(ctx, options) {
|
|
|
7699
8080
|
}
|
|
7700
8081
|
|
|
7701
8082
|
// src/gates/status.ts
|
|
7702
|
-
import { existsSync as existsSync17, readdirSync as
|
|
7703
|
-
import { basename as
|
|
8083
|
+
import { existsSync as existsSync17, readdirSync as readdirSync8 } from "node:fs";
|
|
8084
|
+
import { basename as basename8, join as join21, relative as relative9, resolve as resolve12 } from "node:path";
|
|
7704
8085
|
var LOGGER_NAME = "mstar/status-gate";
|
|
7705
8086
|
function harnessDocKindOfTarget(harnessDir, targetPath) {
|
|
7706
|
-
const resolved =
|
|
7707
|
-
const name =
|
|
8087
|
+
const resolved = resolve12(targetPath);
|
|
8088
|
+
const name = basename8(resolved);
|
|
7708
8089
|
if (name !== STATUS_FILE && name !== WORKFLOW_SNAPSHOT_FILE && name !== PROJECT_REGISTER_FILE)
|
|
7709
8090
|
return null;
|
|
7710
8091
|
const rel = relative9(harnessDir, resolved);
|
|
@@ -7716,8 +8097,8 @@ function harnessDocKindOfTarget(harnessDir, targetPath) {
|
|
|
7716
8097
|
workflowDir = resolveWorkflowDir(harnessDir, { harnessDir });
|
|
7717
8098
|
projectDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
7718
8099
|
} catch {
|
|
7719
|
-
workflowDir =
|
|
7720
|
-
projectDir =
|
|
8100
|
+
workflowDir = join21(harnessDir, "workflows");
|
|
8101
|
+
projectDir = join21(harnessDir, "projects");
|
|
7721
8102
|
}
|
|
7722
8103
|
if (name === WORKFLOW_SNAPSHOT_FILE && /^[^/]+\/snapshot\.json$/.test(relative9(workflowDir, resolved)))
|
|
7723
8104
|
return "snapshot";
|
|
@@ -7773,7 +8154,7 @@ function projectRegisterDocs(harnessDir) {
|
|
|
7773
8154
|
const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
7774
8155
|
let entries;
|
|
7775
8156
|
try {
|
|
7776
|
-
entries =
|
|
8157
|
+
entries = readdirSync8(projectsDir, { withFileTypes: true });
|
|
7777
8158
|
} catch {
|
|
7778
8159
|
return [];
|
|
7779
8160
|
}
|
|
@@ -7781,7 +8162,7 @@ function projectRegisterDocs(harnessDir) {
|
|
|
7781
8162
|
for (const entry of entries) {
|
|
7782
8163
|
if (!entry.isDirectory())
|
|
7783
8164
|
continue;
|
|
7784
|
-
const registerPath =
|
|
8165
|
+
const registerPath = join21(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
7785
8166
|
if (!existsSync17(registerPath))
|
|
7786
8167
|
continue;
|
|
7787
8168
|
try {
|
|
@@ -7855,8 +8236,8 @@ async function editIntentListener(ctx, resolver, config, adapter, target, actor,
|
|
|
7855
8236
|
}
|
|
7856
8237
|
|
|
7857
8238
|
// src/gates/skill-lint.ts
|
|
7858
|
-
import { existsSync as existsSync18, readFileSync as
|
|
7859
|
-
import { basename as
|
|
8239
|
+
import { existsSync as existsSync18, readFileSync as readFileSync13 } from "node:fs";
|
|
8240
|
+
import { basename as basename9, dirname as dirname12, resolve as resolve14, sep as sep6 } from "node:path";
|
|
7860
8241
|
var SKILL_LINT_LOGGER = "mstar/skill-lint";
|
|
7861
8242
|
|
|
7862
8243
|
class SkillLintVetoError extends Error {
|
|
@@ -7897,7 +8278,7 @@ function lintSkillDoc(doc, options = {}) {
|
|
|
7897
8278
|
return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
|
|
7898
8279
|
}
|
|
7899
8280
|
function lintSkillWrite(doc, options) {
|
|
7900
|
-
const skillId =
|
|
8281
|
+
const skillId = basename9(dirname12(resolve14(options.target)));
|
|
7901
8282
|
const result = lintSkillDoc(doc, { skillId });
|
|
7902
8283
|
if (options.hard && !result.ok) {
|
|
7903
8284
|
throw new SkillLintVetoError(options.target, result.violations);
|
|
@@ -7910,13 +8291,13 @@ function skillRootsOf(config) {
|
|
|
7910
8291
|
return roots.map((root) => root.trim()).filter((root) => root !== "");
|
|
7911
8292
|
}
|
|
7912
8293
|
function isSkillTarget(roots, target) {
|
|
7913
|
-
if (
|
|
8294
|
+
if (basename9(target.displayPath) !== "SKILL.md")
|
|
7914
8295
|
return false;
|
|
7915
|
-
const resolvedPath =
|
|
7916
|
-
return roots.some((root) => resolvedPath.startsWith(
|
|
8296
|
+
const resolvedPath = resolve14(target.displayPath);
|
|
8297
|
+
return roots.some((root) => resolvedPath.startsWith(resolve14(root) + sep6));
|
|
7917
8298
|
}
|
|
7918
8299
|
function skillNameOf(target) {
|
|
7919
|
-
return
|
|
8300
|
+
return basename9(dirname12(target.displayPath));
|
|
7920
8301
|
}
|
|
7921
8302
|
function skillCanonicalForm(target) {
|
|
7922
8303
|
return resolveSkillRoot("dsh", { skill: skillNameOf(target), rel: "SKILL.md" });
|
|
@@ -7928,12 +8309,12 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
|
|
|
7928
8309
|
return;
|
|
7929
8310
|
if (!isSkillTarget(roots, target))
|
|
7930
8311
|
return;
|
|
7931
|
-
const skillPath =
|
|
8312
|
+
const skillPath = resolve14(target.displayPath);
|
|
7932
8313
|
if (!existsSync18(skillPath))
|
|
7933
8314
|
return;
|
|
7934
8315
|
let doc;
|
|
7935
8316
|
try {
|
|
7936
|
-
doc =
|
|
8317
|
+
doc = readFileSync13(skillPath, "utf8");
|
|
7937
8318
|
} catch (error) {
|
|
7938
8319
|
ctx.logger(SKILL_LINT_LOGGER).error(`skill lint degraded to allow (cannot read ${skillPath}): ${error.message}`);
|
|
7939
8320
|
ctx.emit("mstar/skill-lint", {
|
|
@@ -7946,7 +8327,7 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
|
|
|
7946
8327
|
});
|
|
7947
8328
|
return;
|
|
7948
8329
|
}
|
|
7949
|
-
const result = lintSkillDoc(doc, { skillId:
|
|
8330
|
+
const result = lintSkillDoc(doc, { skillId: basename9(dirname12(skillPath)) });
|
|
7950
8331
|
if (result.ok)
|
|
7951
8332
|
return;
|
|
7952
8333
|
const hard = resolveSeamHard(harnessDir, config);
|
|
@@ -7980,15 +8361,17 @@ async function skillWriteIntentListener(ctx, resolver, config, target, actor, ne
|
|
|
7980
8361
|
}
|
|
7981
8362
|
|
|
7982
8363
|
// src/gates/seams.ts
|
|
7983
|
-
import { existsSync as existsSync19, readFileSync as readFileSync15, readdirSync as
|
|
7984
|
-
import { basename as
|
|
8364
|
+
import { existsSync as existsSync19, readFileSync as readFileSync15, readdirSync as readdirSync11 } from "node:fs";
|
|
8365
|
+
import { basename as basename10, dirname as dirname13, join as join22, resolve as resolve16, sep as sep8 } from "node:path";
|
|
7985
8366
|
|
|
7986
8367
|
// ../engine/dist/audit.js
|
|
7987
|
-
import { AsyncLocalStorage as
|
|
8368
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
|
|
8369
|
+
import { AsyncLocalStorage as AsyncLocalStorage22 } from "node:async_hooks";
|
|
7988
8370
|
var DATE_PART2 = String.raw`\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])`;
|
|
7989
8371
|
var RFC3339_Z_RE2 = new RegExp(String.raw`^${DATE_PART2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$`);
|
|
7990
8372
|
var DATE_ONLY_RE2 = new RegExp(String.raw`^${DATE_PART2}$`);
|
|
7991
|
-
var heldLockDirs2 = new
|
|
8373
|
+
var heldLockDirs2 = new AsyncLocalStorage3;
|
|
8374
|
+
var writeAuthorizations2 = new AsyncLocalStorage22;
|
|
7992
8375
|
var MSTARC_HARNESS_DIR_KEY2 = "harness_dir";
|
|
7993
8376
|
var MSTARC_PLAN_DIR_KEY2 = "plan_dir";
|
|
7994
8377
|
var MSTARC_SDD_DIR_KEY2 = "sdd_dir";
|
|
@@ -8180,7 +8563,7 @@ var SEAM_LOGGERS = {
|
|
|
8180
8563
|
roles: "mstar/roles-lint"
|
|
8181
8564
|
};
|
|
8182
8565
|
function isIndexFile(path) {
|
|
8183
|
-
const base =
|
|
8566
|
+
const base = basename10(path);
|
|
8184
8567
|
return base === "README.md" || base === "index.md";
|
|
8185
8568
|
}
|
|
8186
8569
|
function isMarkdownDoc(path) {
|
|
@@ -8189,7 +8572,7 @@ function isMarkdownDoc(path) {
|
|
|
8189
8572
|
function isAuditPlanTarget(path) {
|
|
8190
8573
|
if (!isMarkdownDoc(path))
|
|
8191
8574
|
return false;
|
|
8192
|
-
const segments =
|
|
8575
|
+
const segments = resolve16(path).split(sep8);
|
|
8193
8576
|
for (let i = 0;i < segments.length; i++) {
|
|
8194
8577
|
if (segments[i] !== "plans")
|
|
8195
8578
|
continue;
|
|
@@ -8200,7 +8583,7 @@ function isAuditPlanTarget(path) {
|
|
|
8200
8583
|
return false;
|
|
8201
8584
|
}
|
|
8202
8585
|
function isRolesTarget(path) {
|
|
8203
|
-
const segments =
|
|
8586
|
+
const segments = resolve16(path).split(sep8);
|
|
8204
8587
|
const idx = segments.indexOf("mstar-roles");
|
|
8205
8588
|
if (idx === -1)
|
|
8206
8589
|
return false;
|
|
@@ -8210,17 +8593,17 @@ function isRolesTarget(path) {
|
|
|
8210
8593
|
return rest[0] === "SKILL.md" || rest[0] === "references" && rest[rest.length - 1].endsWith(".md");
|
|
8211
8594
|
}
|
|
8212
8595
|
function rolesDirOf(path) {
|
|
8213
|
-
const segments =
|
|
8596
|
+
const segments = resolve16(path).split(sep8);
|
|
8214
8597
|
const idx = segments.indexOf("mstar-roles");
|
|
8215
8598
|
if (idx === -1)
|
|
8216
|
-
return
|
|
8217
|
-
return idx === 0 ?
|
|
8599
|
+
return dirname13(path);
|
|
8600
|
+
return idx === 0 ? sep8 : join22(sep8, ...segments.slice(1, idx + 1));
|
|
8218
8601
|
}
|
|
8219
8602
|
function isSeamTarget(seam, harnessDir, target) {
|
|
8220
|
-
const path =
|
|
8603
|
+
const path = resolve16(target.displayPath);
|
|
8221
8604
|
switch (seam) {
|
|
8222
8605
|
case "design-md": {
|
|
8223
|
-
const base =
|
|
8606
|
+
const base = basename10(path);
|
|
8224
8607
|
return base === "DESIGN.md" || base === "DESIGN.dark.md";
|
|
8225
8608
|
}
|
|
8226
8609
|
case "audit":
|
|
@@ -8228,7 +8611,7 @@ function isSeamTarget(seam, harnessDir, target) {
|
|
|
8228
8611
|
case "compound": {
|
|
8229
8612
|
if (harnessDir === null)
|
|
8230
8613
|
return false;
|
|
8231
|
-
return path.startsWith(
|
|
8614
|
+
return path.startsWith(resolve16(join22(harnessDir, "knowledge")) + sep8) && isMarkdownDoc(path);
|
|
8232
8615
|
}
|
|
8233
8616
|
case "roles":
|
|
8234
8617
|
return isRolesTarget(path);
|
|
@@ -8245,8 +8628,8 @@ function auditSecretsViolations(findings, file) {
|
|
|
8245
8628
|
}
|
|
8246
8629
|
function validateDesignDoc(doc, path) {
|
|
8247
8630
|
const violations = [...validateDesignTokenFrontmatter(doc).violations];
|
|
8248
|
-
const isDark =
|
|
8249
|
-
const sibling =
|
|
8631
|
+
const isDark = basename10(path) === "DESIGN.dark.md";
|
|
8632
|
+
const sibling = join22(dirname13(path), isDark ? "DESIGN.md" : "DESIGN.dark.md");
|
|
8250
8633
|
if (existsSync19(sibling)) {
|
|
8251
8634
|
const light = isDark ? readFileSync15(sibling, "utf8") : doc;
|
|
8252
8635
|
const dark = isDark ? doc : readFileSync15(sibling, "utf8");
|
|
@@ -8263,17 +8646,17 @@ function validateAuditDoc(doc, path) {
|
|
|
8263
8646
|
function validateCompoundDoc(doc, _path, harnessDir) {
|
|
8264
8647
|
const violations = [...validateSchemaYaml(doc).violations];
|
|
8265
8648
|
if (harnessDir !== null) {
|
|
8266
|
-
violations.push(...referenceExists(
|
|
8649
|
+
violations.push(...referenceExists(dirname13(harnessDir), doc).violations);
|
|
8267
8650
|
}
|
|
8268
8651
|
return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
|
|
8269
8652
|
}
|
|
8270
|
-
function validateRolesState(rolesDir, skillsRoot =
|
|
8653
|
+
function validateRolesState(rolesDir, skillsRoot = dirname13(rolesDir)) {
|
|
8271
8654
|
const violations = [...validateRoleMapping(rolesDir).violations];
|
|
8272
8655
|
const skillTexts = {};
|
|
8273
|
-
for (const entry of
|
|
8656
|
+
for (const entry of readdirSync11(skillsRoot, { withFileTypes: true })) {
|
|
8274
8657
|
if (!entry.isDirectory() || !entry.name.startsWith("mstar-"))
|
|
8275
8658
|
continue;
|
|
8276
|
-
const skillFile =
|
|
8659
|
+
const skillFile = join22(skillsRoot, entry.name, "SKILL.md");
|
|
8277
8660
|
if (!existsSync19(skillFile))
|
|
8278
8661
|
continue;
|
|
8279
8662
|
try {
|
|
@@ -8307,7 +8690,7 @@ function gateSeamIntent(ctx, harnessDir, config, seam, target) {
|
|
|
8307
8690
|
try {
|
|
8308
8691
|
if (!isSeamTarget(seam, harnessDir, target))
|
|
8309
8692
|
return;
|
|
8310
|
-
const path =
|
|
8693
|
+
const path = resolve16(target.displayPath);
|
|
8311
8694
|
if (!existsSync19(path))
|
|
8312
8695
|
return;
|
|
8313
8696
|
let doc;
|
|
@@ -8324,12 +8707,12 @@ function gateSeamIntent(ctx, harnessDir, config, seam, target) {
|
|
|
8324
8707
|
const hard = resolveSeamHard(harnessDir, config);
|
|
8325
8708
|
const verdict = applyEnforcement(result, { hard });
|
|
8326
8709
|
if (verdict.hardBlocked) {
|
|
8327
|
-
logger.error(`${
|
|
8710
|
+
logger.error(`${basename10(path)} write to ${target.displayPath} ALLOWED as repair (Enforcement: hard; the current on-disk document is already invalid — the intent carries no incoming content, so the vetoable signal is only the pre-write state):
|
|
8328
8711
|
${verdict.violations.map(formatViolation).join(`
|
|
8329
8712
|
`)}`);
|
|
8330
8713
|
emitSeamAdvisory(ctx, seam, target.displayPath, verdict, hard, { repair: true });
|
|
8331
8714
|
} else {
|
|
8332
|
-
logger.warn(`${
|
|
8715
|
+
logger.warn(`${basename10(path)} write to ${target.displayPath} (advisory):
|
|
8333
8716
|
${verdict.violations.map(formatViolation).join(`
|
|
8334
8717
|
`)}`);
|
|
8335
8718
|
emitSeamAdvisory(ctx, seam, target.displayPath, verdict, hard);
|
|
@@ -8379,7 +8762,7 @@ function lintRolesWrite(doc, options) {
|
|
|
8379
8762
|
|
|
8380
8763
|
// src/gates/tools.ts
|
|
8381
8764
|
import { existsSync as existsSync20, readFileSync as readFileSync16 } from "node:fs";
|
|
8382
|
-
import { join as
|
|
8765
|
+
import { join as join23, resolve as resolve18 } from "node:path";
|
|
8383
8766
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
8384
8767
|
var ITERATION_VIOLATION_SCHEMA = {
|
|
8385
8768
|
type: "object",
|
|
@@ -8599,8 +8982,8 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8599
8982
|
presentCall: (args) => ({ card: "generic", title: "Validate DESIGN.md", kind: "other", rawInput: args.dir }),
|
|
8600
8983
|
isConcurrencySafe: () => false,
|
|
8601
8984
|
async execute(args) {
|
|
8602
|
-
const abs =
|
|
8603
|
-
const lightPath =
|
|
8985
|
+
const abs = resolve18(args.dir);
|
|
8986
|
+
const lightPath = join23(abs, "DESIGN.md");
|
|
8604
8987
|
if (!existsSync20(lightPath))
|
|
8605
8988
|
throw new Error(`design file not found: ${lightPath}`);
|
|
8606
8989
|
const light = readFileSync16(lightPath, "utf8");
|
|
@@ -8653,7 +9036,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8653
9036
|
presentCall: (args) => ({ card: "generic", title: "Validate audit plan", kind: "other", rawInput: args.plan_path }),
|
|
8654
9037
|
isConcurrencySafe: () => false,
|
|
8655
9038
|
async execute(args) {
|
|
8656
|
-
const abs =
|
|
9039
|
+
const abs = resolve18(args.plan_path);
|
|
8657
9040
|
if (!existsSync20(abs))
|
|
8658
9041
|
throw new Error(`plan file not found: ${abs}`);
|
|
8659
9042
|
const text = readFileSync16(abs, "utf8");
|
|
@@ -8693,17 +9076,17 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8693
9076
|
presentCall: (args) => ({ card: "generic", title: "Validate knowledge doc", kind: "other", rawInput: args.doc_path }),
|
|
8694
9077
|
isConcurrencySafe: () => false,
|
|
8695
9078
|
async execute(args, exec) {
|
|
8696
|
-
const abs =
|
|
9079
|
+
const abs = resolve18(args.doc_path);
|
|
8697
9080
|
if (!existsSync20(abs))
|
|
8698
9081
|
throw new Error(`knowledge doc not found: ${abs}`);
|
|
8699
9082
|
const text = readFileSync16(abs, "utf8");
|
|
8700
9083
|
const base = validateCompoundDoc(text, abs, args.repo_root !== undefined ? null : resolver.forAgent(exec.agent));
|
|
8701
9084
|
const violations = [...base.violations];
|
|
8702
9085
|
if (args.repo_root !== undefined) {
|
|
8703
|
-
violations.push(...referenceExists(
|
|
9086
|
+
violations.push(...referenceExists(resolve18(args.repo_root), text).violations);
|
|
8704
9087
|
}
|
|
8705
9088
|
if (args.knowledge_dir !== undefined) {
|
|
8706
|
-
const knowledgeDir =
|
|
9089
|
+
const knowledgeDir = resolve18(args.knowledge_dir);
|
|
8707
9090
|
violations.push(...assertIndexRows(knowledgeDir).violations);
|
|
8708
9091
|
violations.push(...scopeGuard(abs, [knowledgeDir]).violations);
|
|
8709
9092
|
}
|
|
@@ -8738,10 +9121,10 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8738
9121
|
presentCall: (args) => ({ card: "generic", title: "Validate roles mapping", kind: "other", rawInput: args.roles_dir }),
|
|
8739
9122
|
isConcurrencySafe: () => false,
|
|
8740
9123
|
async execute(args) {
|
|
8741
|
-
const rolesDir =
|
|
8742
|
-
if (!existsSync20(
|
|
9124
|
+
const rolesDir = resolve18(args.roles_dir);
|
|
9125
|
+
if (!existsSync20(join23(rolesDir, "SKILL.md")))
|
|
8743
9126
|
throw new Error(`roles dir not found: ${rolesDir}`);
|
|
8744
|
-
const result = validateRolesState(rolesDir, args.skills_root !== undefined ?
|
|
9127
|
+
const result = validateRolesState(rolesDir, args.skills_root !== undefined ? resolve18(args.skills_root) : undefined);
|
|
8745
9128
|
return { ok: result.ok, violations: result.violations.map(iterationViolationView) };
|
|
8746
9129
|
}
|
|
8747
9130
|
}));
|
|
@@ -8750,7 +9133,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8750
9133
|
|
|
8751
9134
|
// src/gates/adapter.ts
|
|
8752
9135
|
import { existsSync as existsSync21 } from "node:fs";
|
|
8753
|
-
import { join as
|
|
9136
|
+
import { join as join24 } from "node:path";
|
|
8754
9137
|
import { Service as Service3 } from "@deepseek-ai/cordis";
|
|
8755
9138
|
var HOST_LOGGER = "mstar/host-adapter";
|
|
8756
9139
|
function mergeLeasesMatch(left, right) {
|
|
@@ -8865,7 +9248,7 @@ class DshHostAdapter extends Service3 {
|
|
|
8865
9248
|
if (harnessDir !== null) {
|
|
8866
9249
|
const selection = resolveActiveWorkflow(harnessDir);
|
|
8867
9250
|
if (selection.kind === "active") {
|
|
8868
|
-
const snapshotPath =
|
|
9251
|
+
const snapshotPath = join24(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
8869
9252
|
try {
|
|
8870
9253
|
const snapshot = readJson(snapshotPath);
|
|
8871
9254
|
const stored = snapshot?.integration_merge_lease;
|
|
@@ -8891,8 +9274,8 @@ class DshHostAdapter extends Service3 {
|
|
|
8891
9274
|
}
|
|
8892
9275
|
|
|
8893
9276
|
// src/gates/agent-personas.ts
|
|
8894
|
-
import { readFileSync as readFileSync17, readdirSync as
|
|
8895
|
-
import { join as
|
|
9277
|
+
import { readFileSync as readFileSync17, readdirSync as readdirSync13, statSync as statSync8 } from "node:fs";
|
|
9278
|
+
import { join as join25, resolve as resolve19, sep as sep9 } from "node:path";
|
|
8896
9279
|
var ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
|
|
8897
9280
|
var defaultCache = new Map;
|
|
8898
9281
|
function personaFor(roleId, lookup, warn) {
|
|
@@ -8909,7 +9292,7 @@ function personaFor(roleId, lookup, warn) {
|
|
|
8909
9292
|
function subagentRoleIds(agentsDir) {
|
|
8910
9293
|
let names;
|
|
8911
9294
|
try {
|
|
8912
|
-
names =
|
|
9295
|
+
names = readdirSync13(agentsDir);
|
|
8913
9296
|
} catch {
|
|
8914
9297
|
return [];
|
|
8915
9298
|
}
|
|
@@ -8920,7 +9303,7 @@ function subagentRoleIds(agentsDir) {
|
|
|
8920
9303
|
const roleId = name.slice(0, -3);
|
|
8921
9304
|
let content;
|
|
8922
9305
|
try {
|
|
8923
|
-
content = readFileSync17(
|
|
9306
|
+
content = readFileSync17(join25(agentsDir, name), "utf8");
|
|
8924
9307
|
} catch {
|
|
8925
9308
|
continue;
|
|
8926
9309
|
}
|
|
@@ -8933,9 +9316,9 @@ function subagentRoleIds(agentsDir) {
|
|
|
8933
9316
|
function defaultFromMirror(agentsDir, roleId, warn) {
|
|
8934
9317
|
if (!ROLE_ID_PATTERN.test(roleId))
|
|
8935
9318
|
return;
|
|
8936
|
-
const root =
|
|
8937
|
-
const file =
|
|
8938
|
-
if (!file.startsWith(root +
|
|
9319
|
+
const root = resolve19(agentsDir);
|
|
9320
|
+
const file = resolve19(agentsDir, `${roleId}.md`);
|
|
9321
|
+
if (!file.startsWith(root + sep9))
|
|
8939
9322
|
return;
|
|
8940
9323
|
let stat;
|
|
8941
9324
|
try {
|
|
@@ -9182,7 +9565,7 @@ function log2(level, message) {
|
|
|
9182
9565
|
|
|
9183
9566
|
// src/gates/workflow-ledger.ts
|
|
9184
9567
|
import { readFileSync as readFileSync18, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
9185
|
-
import { join as
|
|
9568
|
+
import { join as join26 } from "node:path";
|
|
9186
9569
|
var WORKFLOW_LEDGER_LOGGER = "mstar/workflow-ledger";
|
|
9187
9570
|
var TOOL_WORKFLOW_RUN_START = "tool-workflow/run-start";
|
|
9188
9571
|
var TOOL_WORKFLOW_AGENT_START = "tool-workflow/agent-start";
|
|
@@ -9242,7 +9625,7 @@ function loadWatermark(workflowDir, fresh = false) {
|
|
|
9242
9625
|
return cached;
|
|
9243
9626
|
const watermark = new Map;
|
|
9244
9627
|
try {
|
|
9245
|
-
const raw = readFileSync18(
|
|
9628
|
+
const raw = readFileSync18(join26(workflowDir, WORKFLOW_LEDGER_WATERMARK_FILE), "utf8");
|
|
9246
9629
|
const record = asRecord(JSON.parse(raw));
|
|
9247
9630
|
const cursors = asRecord(record?.cursors);
|
|
9248
9631
|
if (cursors !== undefined) {
|
|
@@ -9265,7 +9648,7 @@ function loadWatermark(workflowDir, fresh = false) {
|
|
|
9265
9648
|
}
|
|
9266
9649
|
function saveWatermark(workflowDir, watermark) {
|
|
9267
9650
|
try {
|
|
9268
|
-
const file =
|
|
9651
|
+
const file = join26(workflowDir, WORKFLOW_LEDGER_WATERMARK_FILE);
|
|
9269
9652
|
const tmp = `${file}.tmp`;
|
|
9270
9653
|
writeFileSync7(tmp, JSON.stringify({ v: 1, cursors: Object.fromEntries(watermark) }));
|
|
9271
9654
|
renameSync4(tmp, file);
|
|
@@ -9584,8 +9967,8 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
|
|
|
9584
9967
|
}
|
|
9585
9968
|
|
|
9586
9969
|
// src/gates/goal-bridge.ts
|
|
9587
|
-
import { existsSync as existsSync22, readdirSync as
|
|
9588
|
-
import { join as
|
|
9970
|
+
import { existsSync as existsSync22, readdirSync as readdirSync14 } from "node:fs";
|
|
9971
|
+
import { join as join27 } from "node:path";
|
|
9589
9972
|
var GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
|
|
9590
9973
|
var GOAL_CHANGE_EVENT_TYPE = "goal/change";
|
|
9591
9974
|
var GOAL_CHANGE_VERSION = 1;
|
|
@@ -9607,15 +9990,15 @@ function errorMessage4(error) {
|
|
|
9607
9990
|
function projectRegisterPointer(harnessDir) {
|
|
9608
9991
|
const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
9609
9992
|
try {
|
|
9610
|
-
for (const entry of
|
|
9993
|
+
for (const entry of readdirSync14(projectsDir, { withFileTypes: true })) {
|
|
9611
9994
|
if (!entry.isDirectory())
|
|
9612
9995
|
continue;
|
|
9613
|
-
const registerPath =
|
|
9996
|
+
const registerPath = join27(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
9614
9997
|
if (existsSync22(registerPath))
|
|
9615
9998
|
return registerPath;
|
|
9616
9999
|
}
|
|
9617
10000
|
} catch {}
|
|
9618
|
-
return
|
|
10001
|
+
return join27(projectsDir, _DEFAULT_PROJECT, PROJECT_REGISTER_FILE);
|
|
9619
10002
|
}
|
|
9620
10003
|
function blockedAdvisoryOf(envelope) {
|
|
9621
10004
|
const record = asRecord(envelope);
|
|
@@ -9667,7 +10050,7 @@ function registerGoalBridge(ctx, resolver) {
|
|
|
9667
10050
|
|
|
9668
10051
|
// src/gates/plan-mode-bridge.ts
|
|
9669
10052
|
import { existsSync as existsSync23 } from "node:fs";
|
|
9670
|
-
import { isAbsolute as
|
|
10053
|
+
import { isAbsolute as isAbsolute12, join as join28, relative as relative10, sep as sep10 } from "node:path";
|
|
9671
10054
|
|
|
9672
10055
|
// src/gates/steering.ts
|
|
9673
10056
|
function isRootLikeAgent(agent) {
|
|
@@ -9711,7 +10094,7 @@ function errorMessage5(error) {
|
|
|
9711
10094
|
return error instanceof Error ? error.message : String(error);
|
|
9712
10095
|
}
|
|
9713
10096
|
function selectedLifecycle(harnessDir, hint) {
|
|
9714
|
-
if (!existsSync23(
|
|
10097
|
+
if (!existsSync23(join28(harnessDir, STATUS_FILE)))
|
|
9715
10098
|
return { kind: "none" };
|
|
9716
10099
|
const selection = resolveReadWorkflow(harnessDir, hint);
|
|
9717
10100
|
if (selection.kind === "error") {
|
|
@@ -9719,7 +10102,7 @@ function selectedLifecycle(harnessDir, hint) {
|
|
|
9719
10102
|
}
|
|
9720
10103
|
let doc;
|
|
9721
10104
|
try {
|
|
9722
|
-
doc = readJson(
|
|
10105
|
+
doc = readJson(join28(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE));
|
|
9723
10106
|
} catch {
|
|
9724
10107
|
return { kind: "none" };
|
|
9725
10108
|
}
|
|
@@ -9729,11 +10112,11 @@ function selectedLifecycle(harnessDir, hint) {
|
|
|
9729
10112
|
}
|
|
9730
10113
|
function selectedCompassSteers(harnessDir, lifecycle) {
|
|
9731
10114
|
const ref = lifecycle.doc.compass_ref;
|
|
9732
|
-
if (typeof ref !== "string" || ref.trim() === "" ||
|
|
10115
|
+
if (typeof ref !== "string" || ref.trim() === "" || isAbsolute12(ref))
|
|
9733
10116
|
return false;
|
|
9734
|
-
const compassPath =
|
|
10117
|
+
const compassPath = join28(harnessDir, ref);
|
|
9735
10118
|
const inside = relative10(harnessDir, compassPath);
|
|
9736
|
-
if (inside === "" || inside === ".." || inside.startsWith(`..${
|
|
10119
|
+
if (inside === "" || inside === ".." || inside.startsWith(`..${sep10}`) || isAbsolute12(inside))
|
|
9737
10120
|
return false;
|
|
9738
10121
|
if (!existsSync23(compassPath))
|
|
9739
10122
|
return false;
|
|
@@ -10301,10 +10684,10 @@ function registerMstarCommands(ctx) {
|
|
|
10301
10684
|
if (dir === undefined)
|
|
10302
10685
|
return;
|
|
10303
10686
|
ctx.inject(["commands"], (commandsCtx) => {
|
|
10304
|
-
for (const file of
|
|
10687
|
+
for (const file of readdirSync15(dir).sort()) {
|
|
10305
10688
|
if (!file.endsWith(".md"))
|
|
10306
10689
|
continue;
|
|
10307
|
-
const parsed = parseCommandMarkdown(readFileSync19(
|
|
10690
|
+
const parsed = parseCommandMarkdown(readFileSync19(join29(dir, file), "utf8"));
|
|
10308
10691
|
if (parsed === undefined)
|
|
10309
10692
|
continue;
|
|
10310
10693
|
commandsCtx.commands.register({
|