@mstar-harness/dsh 3.9.3 → 3.9.4
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/index.js +631 -334
- package/harness-commands/iteration-drive.md +16 -2
- package/harness-skills/mstar-artifacts/SKILL.md +6 -4
- package/harness-skills/mstar-artifacts/references/status-and-residuals.md +61 -9
- package/harness-skills/mstar-branch-worktree/SKILL.md +5 -4
- package/harness-skills/mstar-dispatch-gates/SKILL.md +10 -1
- package/harness-skills/mstar-host/SKILL.md +1 -0
- package/harness-skills/mstar-host/references/codex.md +1 -1
- package/harness-skills/mstar-host/references/dsh.md +6 -0
- package/harness-skills/mstar-host/references/omp.md +1 -1
- package/harness-skills/mstar-iteration/SKILL.md +12 -0
- package/harness-skills/mstar-iteration/references/command-shared-invariants.md +4 -0
- package/harness-skills/mstar-iteration/references/phase-2-worktree-lease.md +83 -12
- package/harness-skills/mstar-iteration/references/plan-scoped-pm.md +179 -0
- package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +7 -0
- package/harness-skills/mstar-roles/references/project-manager.md +10 -1
- package/harness-skills/mstar-sdd/SKILL.md +3 -1
- package/harness-skills/mstar-sdd/references/file-handoffs.md +1 -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))
|
|
@@ -1123,7 +1123,7 @@ var require_lib2 = __commonJS(function(exports, module) {
|
|
|
1123
1123
|
|
|
1124
1124
|
// src/index.ts
|
|
1125
1125
|
import { existsSync as existsSync24, readFileSync as readFileSync19, readdirSync as readdirSync14 } from "node:fs";
|
|
1126
|
-
import { join as
|
|
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 readdirSync5, 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 resolve8 } from "node:path";
|
|
1144
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "node:fs";
|
|
1145
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
1146
|
+
import { dirname as dirname5, join as join7, resolve as resolve6, 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 } from "node:fs";
|
|
1152
|
+
import { isAbsolute as isAbsolute5, join as join6 } 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 resolve9 } 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 readdirSync6, readFileSync 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 readdirSync6, 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 resolve10 } from "node:path";
|
|
1161
|
+
import { existsSync as existsSync13, readdirSync as readdirSync9, readFileSync as readFileSync12 } from "node:fs";
|
|
1162
|
+
import { basename as basename11, isAbsolute as isAbsolute10, join as join17, relative as relative6, resolve as resolve14, 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" },
|
|
@@ -1665,9 +1963,6 @@ var WORKFLOW_SNAPSHOT_FILE = "snapshot.json";
|
|
|
1665
1963
|
var WORKFLOW_LIFECYCLE_STATUSES = ["running", "paused", "completed", "failed", "stopped"];
|
|
1666
1964
|
var WORKFLOW_TERMINAL_STATUSES = ["completed", "failed", "stopped"];
|
|
1667
1965
|
var WORKFLOW_LIFECYCLE_TYPES = ["plan", "iteration"];
|
|
1668
|
-
function isPlainObject2(value) {
|
|
1669
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1670
|
-
}
|
|
1671
1966
|
function violation3(severity, code, message, fix) {
|
|
1672
1967
|
return { ok: false, severity, code, message, fix };
|
|
1673
1968
|
}
|
|
@@ -1679,7 +1974,7 @@ function validateNonEmptyString2(violations, value, field, missingCode, invalidC
|
|
|
1679
1974
|
}
|
|
1680
1975
|
}
|
|
1681
1976
|
function validateWorktreePathValue(violations, value, field) {
|
|
1682
|
-
if (typeof value !== "string" || value.trim() === "" || !
|
|
1977
|
+
if (typeof value !== "string" || value.trim() === "" || !isAbsolute5(value)) {
|
|
1683
1978
|
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
1979
|
}
|
|
1685
1980
|
}
|
|
@@ -1728,8 +2023,14 @@ function validateWorkflowSnapshot(doc) {
|
|
|
1728
2023
|
if (isPlainObject2(row) && row.execution_lease !== undefined) {
|
|
1729
2024
|
violations.push(...validateExecutionLease(row.execution_lease).violations);
|
|
1730
2025
|
}
|
|
2026
|
+
if (isPlainObject2(row) && row.coordination !== undefined) {
|
|
2027
|
+
violations.push(...validateRowCoordination(row.coordination, `plans[${String(row.id)}].coordination`));
|
|
2028
|
+
}
|
|
1731
2029
|
}
|
|
1732
2030
|
}
|
|
2031
|
+
if (doc.coordination !== undefined) {
|
|
2032
|
+
violations.push(...validateSnapshotCoordination(doc.coordination));
|
|
2033
|
+
}
|
|
1733
2034
|
if (doc.execution_policy !== undefined) {
|
|
1734
2035
|
if (!isPlainObject2(doc.execution_policy)) {
|
|
1735
2036
|
violations.push(violation3("medium", "workflow.snapshot.invalid-execution-policy", "execution_policy must be an object"));
|
|
@@ -1796,13 +2097,13 @@ class WorkflowSnapshotValidationError extends Error {
|
|
|
1796
2097
|
}
|
|
1797
2098
|
}
|
|
1798
2099
|
function readWorkflowSnapshot(dir) {
|
|
1799
|
-
const snapshotPath =
|
|
1800
|
-
if (!
|
|
2100
|
+
const snapshotPath = join6(dir, WORKFLOW_SNAPSHOT_FILE);
|
|
2101
|
+
if (!existsSync4(snapshotPath)) {
|
|
1801
2102
|
throw new Error(`workflow snapshot not found: ${snapshotPath}`);
|
|
1802
2103
|
}
|
|
1803
2104
|
let doc;
|
|
1804
2105
|
try {
|
|
1805
|
-
doc = JSON.parse(
|
|
2106
|
+
doc = JSON.parse(readFileSync4(snapshotPath, "utf8"));
|
|
1806
2107
|
} catch (error) {
|
|
1807
2108
|
throw new Error(`Invalid JSON in ${snapshotPath}: ${error.message}`);
|
|
1808
2109
|
}
|
|
@@ -1833,9 +2134,6 @@ var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
|
1833
2134
|
var PLAN_STATUSES = ["Todo", "InProgress", "InReview", "Blocked", "Done"];
|
|
1834
2135
|
var RESIDUAL_DECISIONS = ["defer", "accept", "risk-accepted"];
|
|
1835
2136
|
var RESIDUAL_LIFECYCLES = ["open", "resolved", "waived", "superseded", "duplicate"];
|
|
1836
|
-
function isPlainObject3(value) {
|
|
1837
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1838
|
-
}
|
|
1839
2137
|
function violation4(severity, code, message, fix) {
|
|
1840
2138
|
return { ok: false, severity, code, message, fix };
|
|
1841
2139
|
}
|
|
@@ -1860,7 +2158,7 @@ function validateNonEmptyString3(violations, value, field, missingCode, invalidC
|
|
|
1860
2158
|
}
|
|
1861
2159
|
function validatePlanRow(row) {
|
|
1862
2160
|
const violations = [];
|
|
1863
|
-
if (!
|
|
2161
|
+
if (!isPlainObject2(row)) {
|
|
1864
2162
|
return { ok: false, violations: [violation4("high", "status.plan-row.invalid", "plan row must be an object")] };
|
|
1865
2163
|
}
|
|
1866
2164
|
const { id, plan_id: planId, title, file, status, metadata, execution_lease } = row;
|
|
@@ -1884,10 +2182,10 @@ function validatePlanRow(row) {
|
|
|
1884
2182
|
} else if (typeof status !== "string" || !PLAN_STATUSES.includes(status)) {
|
|
1885
2183
|
violations.push(violation4("medium", "status.plan-row.invalid-status", `status must be one of ${PLAN_STATUSES.join(" | ")} — got ${JSON.stringify(status)}`));
|
|
1886
2184
|
}
|
|
1887
|
-
if (metadata !== undefined && !
|
|
2185
|
+
if (metadata !== undefined && !isPlainObject2(metadata)) {
|
|
1888
2186
|
violations.push(violation4("medium", "status.plan-row.invalid-metadata", "metadata must be an object"));
|
|
1889
2187
|
}
|
|
1890
|
-
if (execution_lease !== undefined && !
|
|
2188
|
+
if (execution_lease !== undefined && !isPlainObject2(execution_lease)) {
|
|
1891
2189
|
violations.push(violation4("medium", "status.plan-row.invalid-execution-lease", "execution_lease must be an object"));
|
|
1892
2190
|
}
|
|
1893
2191
|
if (status === "Done" && execution_lease !== undefined) {
|
|
@@ -1897,7 +2195,7 @@ function validatePlanRow(row) {
|
|
|
1897
2195
|
}
|
|
1898
2196
|
function validateResidual(entry) {
|
|
1899
2197
|
const violations = [];
|
|
1900
|
-
if (!
|
|
2198
|
+
if (!isPlainObject2(entry)) {
|
|
1901
2199
|
return { ok: false, violations: [violation4("high", "status.residual.invalid", "residual entry must be an object")] };
|
|
1902
2200
|
}
|
|
1903
2201
|
const { id, title, severity, source, scope, decision, owner, target, tracking, detail_doc, lifecycle, closed_at } = entry;
|
|
@@ -1957,7 +2255,7 @@ function isHarnessRelativePath(dir) {
|
|
|
1957
2255
|
}
|
|
1958
2256
|
function validateWorkflowEntry(entry) {
|
|
1959
2257
|
const violations = [];
|
|
1960
|
-
if (!
|
|
2258
|
+
if (!isPlainObject2(entry)) {
|
|
1961
2259
|
return {
|
|
1962
2260
|
ok: false,
|
|
1963
2261
|
violations: [violation4("high", "status.workflow.invalid", "workflow entry must be an object")]
|
|
@@ -1985,7 +2283,7 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
1985
2283
|
if (typeof docOrPath === "string") {
|
|
1986
2284
|
try {
|
|
1987
2285
|
doc = readJson(docOrPath);
|
|
1988
|
-
harnessDir =
|
|
2286
|
+
harnessDir = dirname5(resolve6(docOrPath));
|
|
1989
2287
|
} catch (error) {
|
|
1990
2288
|
return {
|
|
1991
2289
|
ok: false,
|
|
@@ -1995,7 +2293,7 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
1995
2293
|
} else {
|
|
1996
2294
|
doc = docOrPath;
|
|
1997
2295
|
}
|
|
1998
|
-
if (!
|
|
2296
|
+
if (!isPlainObject2(doc)) {
|
|
1999
2297
|
return { ok: false, violations: [violation4("high", "status.invalid-doc", "status document must be an object")] };
|
|
2000
2298
|
}
|
|
2001
2299
|
if (doc.version !== 2) {
|
|
@@ -2036,7 +2334,7 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
2036
2334
|
const seen = new Set;
|
|
2037
2335
|
for (const entry of doc.workflows) {
|
|
2038
2336
|
violations.push(...validateWorkflowEntry(entry).violations);
|
|
2039
|
-
if (
|
|
2337
|
+
if (isPlainObject2(entry) && typeof entry.id === "string") {
|
|
2040
2338
|
if (seen.has(entry.id)) {
|
|
2041
2339
|
violations.push(violation4("medium", "status.workflow.duplicate-id", `duplicate workflow id in workflows[]: ${JSON.stringify(entry.id)}`));
|
|
2042
2340
|
}
|
|
@@ -2047,22 +2345,22 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
2047
2345
|
if (harnessDir !== undefined && Array.isArray(doc.workflows)) {
|
|
2048
2346
|
let realHarnessDir = null;
|
|
2049
2347
|
try {
|
|
2050
|
-
realHarnessDir =
|
|
2348
|
+
realHarnessDir = realpathSync2(harnessDir);
|
|
2051
2349
|
} catch {}
|
|
2052
2350
|
for (const entry of doc.workflows) {
|
|
2053
|
-
if (!
|
|
2351
|
+
if (!isPlainObject2(entry) || typeof entry.dir !== "string")
|
|
2054
2352
|
continue;
|
|
2055
|
-
const relSnapshot =
|
|
2056
|
-
const snapshotPath =
|
|
2353
|
+
const relSnapshot = join7(entry.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
2354
|
+
const snapshotPath = join7(harnessDir, relSnapshot);
|
|
2057
2355
|
const label = typeof entry.id === "string" ? entry.id : relSnapshot;
|
|
2058
2356
|
let physical;
|
|
2059
2357
|
try {
|
|
2060
|
-
physical =
|
|
2358
|
+
physical = realpathSync2(snapshotPath);
|
|
2061
2359
|
} catch {
|
|
2062
2360
|
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
2361
|
continue;
|
|
2064
2362
|
}
|
|
2065
|
-
if (realHarnessDir !== null && physical !== realHarnessDir && !physical.startsWith(`${realHarnessDir}${
|
|
2363
|
+
if (realHarnessDir !== null && physical !== realHarnessDir && !physical.startsWith(`${realHarnessDir}${sep2}`)) {
|
|
2066
2364
|
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
2365
|
continue;
|
|
2068
2366
|
}
|
|
@@ -2089,7 +2387,7 @@ function validateStatusV2(docOrPath, opts = {}) {
|
|
|
2089
2387
|
var validateStatus = validateStatusV2;
|
|
2090
2388
|
function resolveCompassEnforcement(harnessDir) {
|
|
2091
2389
|
const iterationsDir = resolveIterationDir(harnessDir);
|
|
2092
|
-
if (!
|
|
2390
|
+
if (!existsSync5(iterationsDir))
|
|
2093
2391
|
return { hard: false, source: "none" };
|
|
2094
2392
|
let entries;
|
|
2095
2393
|
try {
|
|
@@ -2100,12 +2398,12 @@ function resolveCompassEnforcement(harnessDir) {
|
|
|
2100
2398
|
for (const entry of entries) {
|
|
2101
2399
|
if (!entry.isDirectory())
|
|
2102
2400
|
continue;
|
|
2103
|
-
const compassPath =
|
|
2104
|
-
if (!
|
|
2401
|
+
const compassPath = join7(iterationsDir, entry.name, "delivery-compass.md");
|
|
2402
|
+
if (!existsSync5(compassPath))
|
|
2105
2403
|
continue;
|
|
2106
2404
|
let content;
|
|
2107
2405
|
try {
|
|
2108
|
-
content =
|
|
2406
|
+
content = readFileSync5(compassPath, "utf8");
|
|
2109
2407
|
} catch {
|
|
2110
2408
|
continue;
|
|
2111
2409
|
}
|
|
@@ -2120,8 +2418,8 @@ function resolveCompassEnforcement(harnessDir) {
|
|
|
2120
2418
|
return { hard: false, source: "none" };
|
|
2121
2419
|
}
|
|
2122
2420
|
function resolveMstarcEnforcement(harnessDir) {
|
|
2123
|
-
const dir =
|
|
2124
|
-
const rc = loadMstarc(dir,
|
|
2421
|
+
const dir = resolve6(harnessDir);
|
|
2422
|
+
const rc = loadMstarc(dir, dirname5(dir));
|
|
2125
2423
|
const value = rc?.config.enforcement;
|
|
2126
2424
|
if (value === "hard")
|
|
2127
2425
|
return { hard: true, source: "mstarc" };
|
|
@@ -2212,11 +2510,11 @@ function validateCompassShape(doc) {
|
|
|
2212
2510
|
function violation5(severity, code, message, fix) {
|
|
2213
2511
|
return { ok: false, severity, code, message, fix };
|
|
2214
2512
|
}
|
|
2215
|
-
function
|
|
2513
|
+
function isPlainObject3(value) {
|
|
2216
2514
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2217
2515
|
}
|
|
2218
2516
|
function validateCompassFrontmatter(doc) {
|
|
2219
|
-
if (!
|
|
2517
|
+
if (!isPlainObject3(doc)) {
|
|
2220
2518
|
return {
|
|
2221
2519
|
ok: false,
|
|
2222
2520
|
violations: [
|
|
@@ -2253,7 +2551,7 @@ function findPlanRow(snapshotDoc, planId) {
|
|
|
2253
2551
|
if (!Array.isArray(snapshotDoc.plans))
|
|
2254
2552
|
return null;
|
|
2255
2553
|
for (const row of snapshotDoc.plans) {
|
|
2256
|
-
if (!
|
|
2554
|
+
if (!isPlainObject3(row))
|
|
2257
2555
|
continue;
|
|
2258
2556
|
const rowId = typeof row.id === "string" ? row.id : typeof row.plan_id === "string" ? row.plan_id : null;
|
|
2259
2557
|
if (rowId === planId)
|
|
@@ -2349,7 +2647,7 @@ function evaluatePhaseGate(snapshotDoc, compassDoc, opts = {}) {
|
|
|
2349
2647
|
};
|
|
2350
2648
|
}
|
|
2351
2649
|
function parseCompassFrontmatter(filePath) {
|
|
2352
|
-
return parseCompassFrontmatterText(
|
|
2650
|
+
return parseCompassFrontmatterText(readFileSync6(filePath, "utf8"), filePath);
|
|
2353
2651
|
}
|
|
2354
2652
|
function parseCompassFrontmatterText(content, filePath) {
|
|
2355
2653
|
const lines = content.split(/\r?\n/);
|
|
@@ -2416,9 +2714,6 @@ var PROJECT_ROADMAP_FILE = "roadmap.md";
|
|
|
2416
2714
|
var PROJECT_REGISTER_FILE = "residuals.json";
|
|
2417
2715
|
var _DEFAULT_PROJECT = "_default";
|
|
2418
2716
|
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
2717
|
function violation6(severity, code, message, fix) {
|
|
2423
2718
|
return { ok: false, severity, code, message, fix };
|
|
2424
2719
|
}
|
|
@@ -2431,7 +2726,7 @@ function validateNonEmptyString4(violations, value, field, missingCode, invalidC
|
|
|
2431
2726
|
}
|
|
2432
2727
|
function validateProjectRegister(doc) {
|
|
2433
2728
|
const violations = [];
|
|
2434
|
-
if (!
|
|
2729
|
+
if (!isPlainObject2(doc)) {
|
|
2435
2730
|
return {
|
|
2436
2731
|
ok: false,
|
|
2437
2732
|
violations: [violation6("high", "project.register.invalid", "project register must be an object")]
|
|
@@ -2439,7 +2734,7 @@ function validateProjectRegister(doc) {
|
|
|
2439
2734
|
}
|
|
2440
2735
|
if (doc.entries === undefined) {
|
|
2441
2736
|
violations.push(violation6("high", "project.register.missing-entries", "missing required field: entries"));
|
|
2442
|
-
} else if (!
|
|
2737
|
+
} else if (!isPlainObject2(doc.entries)) {
|
|
2443
2738
|
violations.push(violation6("high", "project.register.invalid-entries", "entries must be an object keyed by plan id"));
|
|
2444
2739
|
} else {
|
|
2445
2740
|
for (const [key, entries] of Object.entries(doc.entries)) {
|
|
@@ -2452,7 +2747,7 @@ function validateProjectRegister(doc) {
|
|
|
2452
2747
|
}
|
|
2453
2748
|
for (const entry of entries) {
|
|
2454
2749
|
violations.push(...validateResidual(entry).violations);
|
|
2455
|
-
if (!
|
|
2750
|
+
if (!isPlainObject2(entry))
|
|
2456
2751
|
continue;
|
|
2457
2752
|
validateNonEmptyString4(violations, entry.source_plan, "source_plan", "project.register.missing-source-plan", "project.register.invalid-source-plan");
|
|
2458
2753
|
if (entry.registered_at === undefined) {
|
|
@@ -2474,7 +2769,7 @@ function validateProjectRegister(doc) {
|
|
|
2474
2769
|
function findingsCleanupGate(register, planId, opts) {
|
|
2475
2770
|
const mode = opts?.mode ?? "allow-residual";
|
|
2476
2771
|
const violations = [];
|
|
2477
|
-
const entries =
|
|
2772
|
+
const entries = isPlainObject2(register.entries) ? register.entries[planId] : undefined;
|
|
2478
2773
|
if (entries === undefined) {
|
|
2479
2774
|
return { ok: true, violations };
|
|
2480
2775
|
}
|
|
@@ -2511,25 +2806,25 @@ function findingsCleanupGate(register, planId, opts) {
|
|
|
2511
2806
|
return { ok: violations.length === 0, violations };
|
|
2512
2807
|
}
|
|
2513
2808
|
function resolveHarnessDir(startDir = process.cwd(), opts = {}) {
|
|
2514
|
-
const start =
|
|
2809
|
+
const start = resolve8(startDir);
|
|
2515
2810
|
const explicit = opts.harnessDir ?? process.env.MSTAR_HARNESS_DIR;
|
|
2516
2811
|
if (explicit)
|
|
2517
|
-
return
|
|
2518
|
-
const boundary =
|
|
2812
|
+
return resolve8(start, explicit);
|
|
2813
|
+
const boundary = resolve8(start, opts.workspaceRoot ?? defaultWorkspaceRoot(start));
|
|
2519
2814
|
const rc = loadMstarc(start, boundary);
|
|
2520
2815
|
if (rc !== null && rc.config.harnessDir)
|
|
2521
|
-
return
|
|
2816
|
+
return resolve8(rc.dir, rc.config.harnessDir);
|
|
2522
2817
|
let dir = start;
|
|
2523
2818
|
for (;; ) {
|
|
2524
2819
|
if (!isAtOrBelow2(dir, boundary))
|
|
2525
2820
|
return null;
|
|
2526
|
-
for (const candidate of [
|
|
2821
|
+
for (const candidate of [join10(dir, ".mstar"), join10(dir, ".agents"), join10(dir, ".plans"), join10(dir, "plans")]) {
|
|
2527
2822
|
if (isDirectory(candidate))
|
|
2528
2823
|
return candidate;
|
|
2529
2824
|
}
|
|
2530
2825
|
if (dir === boundary)
|
|
2531
2826
|
return null;
|
|
2532
|
-
const parent =
|
|
2827
|
+
const parent = dirname6(dir);
|
|
2533
2828
|
if (parent === dir)
|
|
2534
2829
|
return null;
|
|
2535
2830
|
dir = parent;
|
|
@@ -2547,21 +2842,21 @@ function defaultWorkspaceRoot(startDir) {
|
|
|
2547
2842
|
let boundary = startDir;
|
|
2548
2843
|
for (const segment of cdup.split(/[\\/]/)) {
|
|
2549
2844
|
if (segment && segment !== ".")
|
|
2550
|
-
boundary =
|
|
2845
|
+
boundary = dirname6(boundary);
|
|
2551
2846
|
}
|
|
2552
|
-
return
|
|
2847
|
+
return resolve8(boundary);
|
|
2553
2848
|
} catch {}
|
|
2554
2849
|
return startDir;
|
|
2555
2850
|
}
|
|
2556
2851
|
function isAtOrBelow2(dir, root) {
|
|
2557
2852
|
const rel = relative2(root, dir);
|
|
2558
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
2853
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
|
|
2559
2854
|
}
|
|
2560
2855
|
function mstarcDirOverride(harnessDir, key) {
|
|
2561
|
-
const dir =
|
|
2562
|
-
const rc = loadMstarc(dir,
|
|
2856
|
+
const dir = resolve8(harnessDir);
|
|
2857
|
+
const rc = loadMstarc(dir, dirname6(dir));
|
|
2563
2858
|
const declared = rc?.config[key];
|
|
2564
|
-
return declared ?
|
|
2859
|
+
return declared ? resolve8(rc.dir, declared) : null;
|
|
2565
2860
|
}
|
|
2566
2861
|
function assertSafePathComponent(value, what) {
|
|
2567
2862
|
if (value === "" || value === "." || value === ".." || !/^[A-Za-z0-9._-]+$/.test(value)) {
|
|
@@ -2569,44 +2864,44 @@ function assertSafePathComponent(value, what) {
|
|
|
2569
2864
|
}
|
|
2570
2865
|
}
|
|
2571
2866
|
function canonicalizeNearestExisting(path) {
|
|
2572
|
-
const abs =
|
|
2867
|
+
const abs = resolve8(path);
|
|
2573
2868
|
let dir = abs;
|
|
2574
2869
|
const tail = [];
|
|
2575
2870
|
for (;; ) {
|
|
2576
|
-
if (
|
|
2871
|
+
if (existsSync8(dir)) {
|
|
2577
2872
|
try {
|
|
2578
|
-
return
|
|
2873
|
+
return join10(realpathSync3(dir), ...tail);
|
|
2579
2874
|
} catch {
|
|
2580
2875
|
return abs;
|
|
2581
2876
|
}
|
|
2582
2877
|
}
|
|
2583
|
-
const parent =
|
|
2878
|
+
const parent = dirname6(dir);
|
|
2584
2879
|
if (parent === dir)
|
|
2585
2880
|
return abs;
|
|
2586
|
-
tail.unshift(
|
|
2881
|
+
tail.unshift(basename5(dir));
|
|
2587
2882
|
dir = parent;
|
|
2588
2883
|
}
|
|
2589
2884
|
}
|
|
2590
2885
|
function resolveSddDir(harnessDir, planId) {
|
|
2591
2886
|
assertSafePathComponent(planId, "planId");
|
|
2592
|
-
const base =
|
|
2887
|
+
const base = resolve8(harnessDir);
|
|
2593
2888
|
const declared = mstarcDirOverride(base, "sddDir");
|
|
2594
|
-
const sddBase = declared !== null ? declared :
|
|
2595
|
-
return
|
|
2889
|
+
const sddBase = declared !== null ? declared : join10(base, "sdd");
|
|
2890
|
+
return join10(sddBase, planId);
|
|
2596
2891
|
}
|
|
2597
2892
|
function resolveIterationDir(harnessDir) {
|
|
2598
2893
|
const declared = mstarcDirOverride(harnessDir, "iterationDir");
|
|
2599
2894
|
if (declared !== null)
|
|
2600
2895
|
return declared;
|
|
2601
|
-
return
|
|
2896
|
+
return join10(resolve8(harnessDir), "iterations");
|
|
2602
2897
|
}
|
|
2603
2898
|
function resolveHarnessSubdir(startDir, opts, key, fallback) {
|
|
2604
2899
|
const harness = resolveHarnessDir(startDir, opts);
|
|
2605
2900
|
if (harness === null) {
|
|
2606
|
-
throw new Error(`harness dir not found from ${
|
|
2901
|
+
throw new Error(`harness dir not found from ${resolve8(startDir)} — cannot resolve the ${fallback} dir (run \`mstar harness scaffold\`, pass opts.harnessDir, or set MSTAR_HARNESS_DIR)`);
|
|
2607
2902
|
}
|
|
2608
2903
|
const declared = mstarcDirOverride(harness, key);
|
|
2609
|
-
return declared !== null ? declared :
|
|
2904
|
+
return declared !== null ? declared : join10(resolve8(harness), fallback);
|
|
2610
2905
|
}
|
|
2611
2906
|
function resolveWorkflowDir(startDir = process.cwd(), opts = {}) {
|
|
2612
2907
|
return resolveHarnessSubdir(startDir, opts, "workflowDir", "workflows");
|
|
@@ -2686,7 +2981,7 @@ function parseMainWorktree(out) {
|
|
|
2686
2981
|
if (branch === null)
|
|
2687
2982
|
return null;
|
|
2688
2983
|
try {
|
|
2689
|
-
return { root:
|
|
2984
|
+
return { root: realpathSync4(rawPath), branch };
|
|
2690
2985
|
} catch {
|
|
2691
2986
|
return null;
|
|
2692
2987
|
}
|
|
@@ -2754,8 +3049,8 @@ function probeCheckout(worktreePath, opts) {
|
|
|
2754
3049
|
const raw = stdout.trim();
|
|
2755
3050
|
if (raw === "")
|
|
2756
3051
|
return { error: `no git dir reported at "${worktreePath}"` };
|
|
2757
|
-
const abs =
|
|
2758
|
-
return { gitDir:
|
|
3052
|
+
const abs = isAbsolute7(raw) ? raw : join11(worktreePath, raw);
|
|
3053
|
+
return { gitDir: realpathSync4(abs) };
|
|
2759
3054
|
} catch (err) {
|
|
2760
3055
|
const e = err;
|
|
2761
3056
|
if (e.killed === true || e.signal !== undefined) {
|
|
@@ -2776,7 +3071,7 @@ function probeCheckoutRoot(path, opts = {}) {
|
|
|
2776
3071
|
const raw = stdout.trim();
|
|
2777
3072
|
if (raw === "")
|
|
2778
3073
|
return null;
|
|
2779
|
-
return
|
|
3074
|
+
return realpathSync4(raw);
|
|
2780
3075
|
} catch {
|
|
2781
3076
|
return null;
|
|
2782
3077
|
}
|
|
@@ -2785,7 +3080,7 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
2785
3080
|
const violations = [];
|
|
2786
3081
|
const checkoutCache = new Map;
|
|
2787
3082
|
const checkout = (path) => {
|
|
2788
|
-
const key =
|
|
3083
|
+
const key = resolve9(path);
|
|
2789
3084
|
if (!checkoutCache.has(key))
|
|
2790
3085
|
checkoutCache.set(key, probeCheckout(path, opts));
|
|
2791
3086
|
return checkoutCache.get(key);
|
|
@@ -2824,7 +3119,7 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
2824
3119
|
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
3120
|
}
|
|
2826
3121
|
if (integrationRequired && integrationWorktreePath.trim() !== "" && integrationBranch.trim() !== "") {
|
|
2827
|
-
if (!
|
|
3122
|
+
if (!existsSync9(integrationWorktreePath)) {
|
|
2828
3123
|
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
3124
|
} else {
|
|
2830
3125
|
const probe = probeBranch(integrationWorktreePath, opts);
|
|
@@ -2836,10 +3131,10 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
2836
3131
|
}
|
|
2837
3132
|
}
|
|
2838
3133
|
const identityViolation = (aPath, bPath, code, describe, fix) => {
|
|
2839
|
-
if (
|
|
3134
|
+
if (resolve9(aPath) === resolve9(bPath)) {
|
|
2840
3135
|
return violation8("critical", code, `${describe} — the same checkout (normalized path equality) is not isolation`, fix);
|
|
2841
3136
|
}
|
|
2842
|
-
if (!
|
|
3137
|
+
if (!existsSync9(aPath) || !existsSync9(bPath))
|
|
2843
3138
|
return null;
|
|
2844
3139
|
const a = checkout(aPath);
|
|
2845
3140
|
const b = checkout(bPath);
|
|
@@ -2869,7 +3164,7 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
2869
3164
|
if (v !== null)
|
|
2870
3165
|
violations.push(v);
|
|
2871
3166
|
}
|
|
2872
|
-
if (leaseWorktreePath.trim() !== "" && !
|
|
3167
|
+
if (leaseWorktreePath.trim() !== "" && !existsSync9(leaseWorktreePath)) {
|
|
2873
3168
|
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
3169
|
} else if (leaseWorktreePath.trim() !== "" && leaseWorkingBranch.trim() !== "") {
|
|
2875
3170
|
const probe = probeBranch(leaseWorktreePath, opts);
|
|
@@ -2893,17 +3188,17 @@ function l2PreDispatchCheck(input, opts = {}) {
|
|
|
2893
3188
|
violations.push(violation8("high", "worktree.l2.track-invalid", `track ${index + 1} is missing worktreePath and/or workingBranch`, "fill both fields for every track"));
|
|
2894
3189
|
return;
|
|
2895
3190
|
}
|
|
2896
|
-
if (!
|
|
3191
|
+
if (!isAbsolute7(track.worktreePath)) {
|
|
2897
3192
|
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
3193
|
return;
|
|
2899
3194
|
}
|
|
2900
|
-
const normalized =
|
|
3195
|
+
const normalized = resolve9(track.worktreePath);
|
|
2901
3196
|
if (seenPaths.has(normalized)) {
|
|
2902
3197
|
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
3198
|
return;
|
|
2904
3199
|
}
|
|
2905
3200
|
seenPaths.add(normalized);
|
|
2906
|
-
if (!
|
|
3201
|
+
if (!existsSync9(track.worktreePath)) {
|
|
2907
3202
|
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
3203
|
return;
|
|
2909
3204
|
}
|
|
@@ -2929,33 +3224,33 @@ function assertBranchAlignment(worktreePath, expectedBranch, opts = {}) {
|
|
|
2929
3224
|
function shellQuote(value) {
|
|
2930
3225
|
return "'" + value.replaceAll("'", `'"'"'`) + "'";
|
|
2931
3226
|
}
|
|
2932
|
-
function
|
|
3227
|
+
function isPlainObject6(value) {
|
|
2933
3228
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2934
3229
|
}
|
|
2935
3230
|
function collectActiveLifecycleBranches(snapshots) {
|
|
2936
3231
|
const owned = new Set;
|
|
2937
3232
|
for (const doc of snapshots) {
|
|
2938
3233
|
const branch = doc.branch;
|
|
2939
|
-
if (
|
|
3234
|
+
if (isPlainObject6(branch) && typeof branch.integration === "string" && branch.integration.trim() !== "") {
|
|
2940
3235
|
owned.add(branch.integration);
|
|
2941
3236
|
}
|
|
2942
3237
|
if (!Array.isArray(doc.plans))
|
|
2943
3238
|
continue;
|
|
2944
3239
|
for (const row of doc.plans) {
|
|
2945
|
-
if (!
|
|
3240
|
+
if (!isPlainObject6(row))
|
|
2946
3241
|
continue;
|
|
2947
3242
|
const lease = row.execution_lease;
|
|
2948
|
-
if (
|
|
3243
|
+
if (isPlainObject6(lease) && typeof lease.working_branch === "string" && lease.working_branch.trim() !== "") {
|
|
2949
3244
|
owned.add(lease.working_branch);
|
|
2950
3245
|
}
|
|
2951
3246
|
const meta = row.metadata;
|
|
2952
|
-
if (
|
|
3247
|
+
if (isPlainObject6(meta) && Array.isArray(meta.track_branches)) {
|
|
2953
3248
|
for (const branch2 of meta.track_branches) {
|
|
2954
3249
|
if (typeof branch2 === "string" && branch2.trim() !== "")
|
|
2955
3250
|
owned.add(branch2);
|
|
2956
3251
|
}
|
|
2957
3252
|
}
|
|
2958
|
-
if (
|
|
3253
|
+
if (isPlainObject6(meta) && typeof meta.working_branch === "string" && meta.working_branch.trim() !== "") {
|
|
2959
3254
|
owned.add(meta.working_branch);
|
|
2960
3255
|
}
|
|
2961
3256
|
}
|
|
@@ -2963,8 +3258,8 @@ function collectActiveLifecycleBranches(snapshots) {
|
|
|
2963
3258
|
return [...owned];
|
|
2964
3259
|
}
|
|
2965
3260
|
function scanActiveLifecycleBranches(harnessDir, governingWorkflowId) {
|
|
2966
|
-
const registerPath =
|
|
2967
|
-
if (!
|
|
3261
|
+
const registerPath = join12(harnessDir, "status.json");
|
|
3262
|
+
if (!existsSync10(registerPath))
|
|
2968
3263
|
return { kind: "ok", branches: [], notes: [] };
|
|
2969
3264
|
let register;
|
|
2970
3265
|
try {
|
|
@@ -2972,7 +3267,7 @@ function scanActiveLifecycleBranches(harnessDir, governingWorkflowId) {
|
|
|
2972
3267
|
} catch (error) {
|
|
2973
3268
|
return { kind: "refusal", code: "worktree.l1.lifecycle-register-unreadable", detail: `${registerPath}: ${error.message}` };
|
|
2974
3269
|
}
|
|
2975
|
-
if (!
|
|
3270
|
+
if (!isPlainObject6(register) || register.version !== 2 || !Array.isArray(register.workflows)) {
|
|
2976
3271
|
return {
|
|
2977
3272
|
kind: "refusal",
|
|
2978
3273
|
code: "worktree.l1.lifecycle-register-unreadable",
|
|
@@ -3003,10 +3298,10 @@ function scanActiveLifecycleBranches(harnessDir, governingWorkflowId) {
|
|
|
3003
3298
|
}
|
|
3004
3299
|
if (id === governingWorkflowId)
|
|
3005
3300
|
continue;
|
|
3006
|
-
const snapshotPath =
|
|
3301
|
+
const snapshotPath = join12(workflowsDir, id, WORKFLOW_SNAPSHOT_FILE);
|
|
3007
3302
|
let snapshot;
|
|
3008
3303
|
try {
|
|
3009
|
-
const read = readWorkflowSnapshot(
|
|
3304
|
+
const read = readWorkflowSnapshot(dirname8(snapshotPath));
|
|
3010
3305
|
snapshot = read.snapshot;
|
|
3011
3306
|
notes.push(...read.diagnostics);
|
|
3012
3307
|
} catch (error) {
|
|
@@ -3059,14 +3354,14 @@ function gitOut(cwd, args) {
|
|
|
3059
3354
|
}
|
|
3060
3355
|
}
|
|
3061
3356
|
function probeHarnessWithStatus(root) {
|
|
3062
|
-
if (isFile2(
|
|
3063
|
-
return
|
|
3064
|
-
if (isFile2(
|
|
3065
|
-
return
|
|
3066
|
-
if (hasWorkflowSnapshot(
|
|
3067
|
-
return
|
|
3068
|
-
if (hasWorkflowSnapshot(
|
|
3069
|
-
return
|
|
3357
|
+
if (isFile2(join13(root, ".mstar", "status.json")))
|
|
3358
|
+
return join13(root, ".mstar");
|
|
3359
|
+
if (isFile2(join13(root, ".agents", "status.json")))
|
|
3360
|
+
return join13(root, ".agents");
|
|
3361
|
+
if (hasWorkflowSnapshot(join13(root, ".mstar")))
|
|
3362
|
+
return join13(root, ".mstar");
|
|
3363
|
+
if (hasWorkflowSnapshot(join13(root, ".agents")))
|
|
3364
|
+
return join13(root, ".agents");
|
|
3070
3365
|
return null;
|
|
3071
3366
|
}
|
|
3072
3367
|
function hasWorkflowSnapshot(harnessDir) {
|
|
@@ -3074,13 +3369,13 @@ function hasWorkflowSnapshot(harnessDir) {
|
|
|
3074
3369
|
try {
|
|
3075
3370
|
workflowsDir = resolveWorkflowDir(harnessDir, { harnessDir });
|
|
3076
3371
|
} catch {
|
|
3077
|
-
workflowsDir =
|
|
3372
|
+
workflowsDir = join13(harnessDir, "workflows");
|
|
3078
3373
|
}
|
|
3079
3374
|
if (!isDirectory2(workflowsDir))
|
|
3080
3375
|
return false;
|
|
3081
3376
|
try {
|
|
3082
3377
|
for (const entry of readdirSync6(workflowsDir, { withFileTypes: true })) {
|
|
3083
|
-
if (entry.isDirectory() && isFile2(
|
|
3378
|
+
if (entry.isDirectory() && isFile2(join13(workflowsDir, entry.name, "snapshot.json")))
|
|
3084
3379
|
return true;
|
|
3085
3380
|
}
|
|
3086
3381
|
} catch {
|
|
@@ -3089,16 +3384,16 @@ function hasWorkflowSnapshot(harnessDir) {
|
|
|
3089
3384
|
return false;
|
|
3090
3385
|
}
|
|
3091
3386
|
function checkoutRootNearestExisting(dir, boundary) {
|
|
3092
|
-
const stop =
|
|
3093
|
-
const startInside = isInside(
|
|
3094
|
-
let current =
|
|
3387
|
+
const stop = resolve10(boundary);
|
|
3388
|
+
const startInside = isInside(resolve10(dir), stop);
|
|
3389
|
+
let current = resolve10(dir);
|
|
3095
3390
|
for (;; ) {
|
|
3096
3391
|
const probed = probeCheckoutRoot(current);
|
|
3097
3392
|
if (probed !== null)
|
|
3098
3393
|
return probed;
|
|
3099
3394
|
if (startInside && current === stop)
|
|
3100
3395
|
return null;
|
|
3101
|
-
const parent =
|
|
3396
|
+
const parent = dirname9(current);
|
|
3102
3397
|
if (parent === current)
|
|
3103
3398
|
return null;
|
|
3104
3399
|
if (startInside && !isInside(parent, stop))
|
|
@@ -3118,7 +3413,7 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
3118
3413
|
if (!isDirectory2(controlRoot)) {
|
|
3119
3414
|
throw new SddScriptError(`mstar sdd workspace: CONTROL_ROOT / MSTAR_CONTROL_ROOT is not a directory: ${controlRoot}`, 1);
|
|
3120
3415
|
}
|
|
3121
|
-
const supplied =
|
|
3416
|
+
const supplied = realpathSync5(controlRoot);
|
|
3122
3417
|
const main = readMainWorktree(supplied);
|
|
3123
3418
|
if (main !== null) {
|
|
3124
3419
|
if (main.root !== supplied) {
|
|
@@ -3127,7 +3422,7 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
3127
3422
|
` + ` Re-run with MSTAR_CONTROL_ROOT=${main.root}`, 1);
|
|
3128
3423
|
}
|
|
3129
3424
|
root = main.root;
|
|
3130
|
-
} else if (isFile2(
|
|
3425
|
+
} else if (isFile2(join13(supplied, ".git")) || gitOut(supplied, ["rev-parse", "--is-inside-work-tree"]) === "true") {
|
|
3131
3426
|
throw new SddScriptError(`mstar sdd workspace: cannot verify the main worktree for CONTROL_ROOT "${supplied}" (git worktree discovery failed).
|
|
3132
3427
|
Refusing to resolve or create any SDD tree without a verified main worktree.`, 1);
|
|
3133
3428
|
} else {
|
|
@@ -3145,22 +3440,22 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
3145
3440
|
const harnessOverride = opts.harnessDir ?? (process.env.MSTAR_HARNESS_DIR || undefined);
|
|
3146
3441
|
let harnessDir;
|
|
3147
3442
|
if (harnessOverride) {
|
|
3148
|
-
harnessDir =
|
|
3443
|
+
harnessDir = resolve10(root, harnessOverride);
|
|
3149
3444
|
} else {
|
|
3150
3445
|
const rc = findMstarc(root, root);
|
|
3151
|
-
const rcHarnessDir = rc !== null ? parseMstarc(
|
|
3446
|
+
const rcHarnessDir = rc !== null ? parseMstarc(readFileSync9(rc, "utf8")).harnessDir : undefined;
|
|
3152
3447
|
if (rcHarnessDir) {
|
|
3153
|
-
harnessDir =
|
|
3448
|
+
harnessDir = resolve10(rc !== null ? dirname9(rc) : root, rcHarnessDir);
|
|
3154
3449
|
} else {
|
|
3155
3450
|
const probed = probeHarnessWithStatus(root);
|
|
3156
3451
|
if (probed) {
|
|
3157
3452
|
harnessDir = probed;
|
|
3158
|
-
} else if (isDirectory2(
|
|
3159
|
-
harnessDir =
|
|
3160
|
-
} else if (isDirectory2(
|
|
3161
|
-
harnessDir =
|
|
3453
|
+
} else if (isDirectory2(join13(root, ".mstar"))) {
|
|
3454
|
+
harnessDir = join13(root, ".mstar");
|
|
3455
|
+
} else if (isDirectory2(join13(root, ".agents"))) {
|
|
3456
|
+
harnessDir = join13(root, ".agents");
|
|
3162
3457
|
} else {
|
|
3163
|
-
harnessDir =
|
|
3458
|
+
harnessDir = join13(root, ".mstar");
|
|
3164
3459
|
}
|
|
3165
3460
|
}
|
|
3166
3461
|
}
|
|
@@ -3171,9 +3466,9 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
3171
3466
|
}
|
|
3172
3467
|
const sddDir = resolveSddDir(harnessDir, planId);
|
|
3173
3468
|
mkdirSync6(sddDir, { recursive: true });
|
|
3174
|
-
writeFileSync4(
|
|
3469
|
+
writeFileSync4(join13(sddDir, ".gitignore"), `*
|
|
3175
3470
|
`);
|
|
3176
|
-
return
|
|
3471
|
+
return realpathSync5(sddDir);
|
|
3177
3472
|
}
|
|
3178
3473
|
function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
3179
3474
|
if (!planFile || !Number.isInteger(taskN) || taskN < 1) {
|
|
@@ -3182,7 +3477,7 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
|
3182
3477
|
const bound = opts.context;
|
|
3183
3478
|
const observedCwd = opts.cwd ?? process.cwd();
|
|
3184
3479
|
if (bound) {
|
|
3185
|
-
const inputPlan = canonicalizeNearestExisting(
|
|
3480
|
+
const inputPlan = canonicalizeNearestExisting(resolve10(observedCwd, planFile));
|
|
3186
3481
|
if (inputPlan !== canonicalizeNearestExisting(bound.planFile)) {
|
|
3187
3482
|
throwGateFail([
|
|
3188
3483
|
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 +3486,16 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
|
3191
3486
|
}
|
|
3192
3487
|
let content;
|
|
3193
3488
|
try {
|
|
3194
|
-
content =
|
|
3489
|
+
content = readFileSync9(planFile, "utf8");
|
|
3195
3490
|
} catch {
|
|
3196
3491
|
throw new SddScriptError(`no such plan file: ${planFile}`, 2);
|
|
3197
3492
|
}
|
|
3198
3493
|
let out;
|
|
3199
3494
|
let mkdirAfterGate = null;
|
|
3200
3495
|
if (outFile) {
|
|
3201
|
-
out = bound ?
|
|
3496
|
+
out = bound ? resolve10(observedCwd, outFile) : outFile;
|
|
3202
3497
|
} else if (bound) {
|
|
3203
|
-
out =
|
|
3498
|
+
out = join13(bound.sddDir, `task-${taskN}-brief.md`);
|
|
3204
3499
|
mkdirAfterGate = bound.sddDir;
|
|
3205
3500
|
} else {
|
|
3206
3501
|
const sddDir = opts.sddDir ?? process.env.SDD_DIR;
|
|
@@ -3208,7 +3503,7 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
|
3208
3503
|
throw new SddScriptError("mstar sdd task-brief: set SDD_DIR or pass OUTFILE (run mstar sdd workspace PLAN_ID first)", 2);
|
|
3209
3504
|
}
|
|
3210
3505
|
mkdirSync6(sddDir, { recursive: true });
|
|
3211
|
-
out =
|
|
3506
|
+
out = join13(sddDir, `task-${taskN}-brief.md`);
|
|
3212
3507
|
}
|
|
3213
3508
|
if (bound) {
|
|
3214
3509
|
const gate2 = checkSddAction(bound, { kind: "artifact", cwd: observedCwd, target: out });
|
|
@@ -3241,15 +3536,15 @@ function taskBrief(planFile, taskN, outFile, opts = {}) {
|
|
|
3241
3536
|
if (printed.length === 0) {
|
|
3242
3537
|
throw new SddScriptError(`task ${taskN} not found in ${planFile} (no heading matching Task ${taskN})`, 3);
|
|
3243
3538
|
}
|
|
3244
|
-
return bound ?
|
|
3539
|
+
return bound ? resolve10(observedCwd, out) : out;
|
|
3245
3540
|
}
|
|
3246
3541
|
function isInside(child, ancestor) {
|
|
3247
3542
|
const rel = relative3(ancestor, child);
|
|
3248
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
3543
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute8(rel);
|
|
3249
3544
|
}
|
|
3250
3545
|
function canonicalDir(path) {
|
|
3251
3546
|
try {
|
|
3252
|
-
return statSync4(path).isDirectory() ?
|
|
3547
|
+
return statSync4(path).isDirectory() ? realpathSync5(path) : null;
|
|
3253
3548
|
} catch {
|
|
3254
3549
|
return null;
|
|
3255
3550
|
}
|
|
@@ -3279,11 +3574,11 @@ function checkSddAction(context, action) {
|
|
|
3279
3574
|
const featureReal = canonicalDir(context.featureCwd);
|
|
3280
3575
|
const canonicalSddDir = canonicalizeNearestExisting(context.sddDir);
|
|
3281
3576
|
const canonicalPlanFile = canonicalizeNearestExisting(context.planFile);
|
|
3282
|
-
const cwdResolved =
|
|
3577
|
+
const cwdResolved = resolve10(action.cwd);
|
|
3283
3578
|
const checkTarget = (baseDir, target, kind) => {
|
|
3284
3579
|
if (featureReal === null)
|
|
3285
3580
|
return;
|
|
3286
|
-
const targetAbs =
|
|
3581
|
+
const targetAbs = resolve10(baseDir, target);
|
|
3287
3582
|
const canonical = canonicalizeNearestExisting(targetAbs);
|
|
3288
3583
|
if (!isInside(canonical, featureReal)) {
|
|
3289
3584
|
const declaredPrefix = targetAbs === featureReal || targetAbs.startsWith(`${featureReal}/`);
|
|
@@ -3311,10 +3606,10 @@ function checkSddAction(context, action) {
|
|
|
3311
3606
|
add("sdd.context.target-missing", "artifact checks require the destination target");
|
|
3312
3607
|
return { ok: false, violations };
|
|
3313
3608
|
}
|
|
3314
|
-
const targetAbs =
|
|
3609
|
+
const targetAbs = resolve10(cwdResolved, action.target);
|
|
3315
3610
|
const canonical = canonicalizeNearestExisting(targetAbs);
|
|
3316
3611
|
if (!isInside(canonical, canonicalSddDir) && canonical !== canonicalPlanFile) {
|
|
3317
|
-
const rawSddDir =
|
|
3612
|
+
const rawSddDir = resolve10(context.sddDir);
|
|
3318
3613
|
const declaredPrefix = targetAbs === rawSddDir || targetAbs.startsWith(`${rawSddDir}/`) || targetAbs === canonicalSddDir || targetAbs.startsWith(`${canonicalSddDir}/`) || targetAbs === canonicalPlanFile;
|
|
3319
3614
|
if (declaredPrefix) {
|
|
3320
3615
|
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 +4342,7 @@ function referenceExists(repoRoot, docText) {
|
|
|
4047
4342
|
if (ref === "" || seen.has(ref))
|
|
4048
4343
|
continue;
|
|
4049
4344
|
seen.add(ref);
|
|
4050
|
-
if (SCHEME_RE.test(ref) || ref.startsWith("{") || ref.startsWith("#") || ref.includes("*") || ref.includes("?") || ref.startsWith("~") ||
|
|
4345
|
+
if (SCHEME_RE.test(ref) || ref.startsWith("{") || ref.startsWith("#") || ref.includes("*") || ref.includes("?") || ref.startsWith("~") || isAbsolute10(ref)) {
|
|
4051
4346
|
continue;
|
|
4052
4347
|
}
|
|
4053
4348
|
if (ref.includes("/") || REF_EXT_RE.test(ref)) {
|
|
@@ -4075,7 +4370,7 @@ function referenceExists(repoRoot, docText) {
|
|
|
4075
4370
|
break;
|
|
4076
4371
|
if (entry.isDirectory()) {
|
|
4077
4372
|
if (!WALK_SKIP_DIRS.has(entry.name))
|
|
4078
|
-
stack.push(
|
|
4373
|
+
stack.push(join17(dir, entry.name));
|
|
4079
4374
|
} else if (!entry.isSymbolicLink()) {
|
|
4080
4375
|
const base = entry.name.replace(/\.(?:ts|tsx|js|jsx|mjs|cjs)$/, "");
|
|
4081
4376
|
if (moduleNames.has(base))
|
|
@@ -4087,7 +4382,7 @@ function referenceExists(repoRoot, docText) {
|
|
|
4087
4382
|
for (const { ref, isSymbol, module } of refs) {
|
|
4088
4383
|
if (!isSymbol || module === undefined) {
|
|
4089
4384
|
const candidate = ref.replace(LINE_SUFFIX_RE, "").replace(ANCHOR_RE, "");
|
|
4090
|
-
if (
|
|
4385
|
+
if (existsSync13(resolve14(repoRoot, candidate))) {
|
|
4091
4386
|
checked++;
|
|
4092
4387
|
} else {
|
|
4093
4388
|
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"));
|
|
@@ -4114,11 +4409,11 @@ function collectKnowledgeDocs(dir) {
|
|
|
4114
4409
|
for (const entry of entries) {
|
|
4115
4410
|
if (entry.isSymbolicLink())
|
|
4116
4411
|
continue;
|
|
4117
|
-
const full =
|
|
4412
|
+
const full = join17(current, entry.name);
|
|
4118
4413
|
if (entry.isDirectory()) {
|
|
4119
4414
|
stack.push(full);
|
|
4120
4415
|
} else if (entry.name.endsWith(".md") && entry.name !== "README.md" && entry.name !== "index.md") {
|
|
4121
|
-
docs.push(relative6(dir, full).split(
|
|
4416
|
+
docs.push(relative6(dir, full).split(sep5).join("/"));
|
|
4122
4417
|
}
|
|
4123
4418
|
}
|
|
4124
4419
|
}
|
|
@@ -4134,14 +4429,14 @@ function normalizeIndexRef(cell) {
|
|
|
4134
4429
|
}
|
|
4135
4430
|
function assertIndexRows(knowledgeDir) {
|
|
4136
4431
|
const violations = [];
|
|
4137
|
-
const readmePath =
|
|
4138
|
-
if (!
|
|
4432
|
+
const readmePath = join17(knowledgeDir, "README.md");
|
|
4433
|
+
if (!existsSync13(readmePath)) {
|
|
4139
4434
|
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
4435
|
return { ok: false, violations };
|
|
4141
4436
|
}
|
|
4142
4437
|
const docs = collectKnowledgeDocs(knowledgeDir);
|
|
4143
4438
|
const rows = new Set;
|
|
4144
|
-
for (const line of
|
|
4439
|
+
for (const line of readFileSync12(readmePath, "utf8").split(/\r?\n/)) {
|
|
4145
4440
|
if (!line.trim().startsWith("|"))
|
|
4146
4441
|
continue;
|
|
4147
4442
|
const cells = line.split("|").map((c) => c.trim());
|
|
@@ -4159,16 +4454,16 @@ function assertIndexRows(knowledgeDir) {
|
|
|
4159
4454
|
return { ok: violations.length === 0, violations };
|
|
4160
4455
|
}
|
|
4161
4456
|
function isFileLikeRoot(root) {
|
|
4162
|
-
return /^[^.]*\.[A-Za-z0-9]{1,10}$/.test(
|
|
4457
|
+
return /^[^.]*\.[A-Za-z0-9]{1,10}$/.test(basename11(root));
|
|
4163
4458
|
}
|
|
4164
4459
|
function scopeGuard(path, allowedRoots) {
|
|
4165
|
-
const resolved =
|
|
4460
|
+
const resolved = resolve14(path);
|
|
4166
4461
|
for (const root of allowedRoots) {
|
|
4167
|
-
const r =
|
|
4462
|
+
const r = resolve14(root);
|
|
4168
4463
|
if (isFileLikeRoot(r)) {
|
|
4169
4464
|
if (resolved === r)
|
|
4170
4465
|
return { ok: true, violations: [] };
|
|
4171
|
-
} else if (resolved === r || resolved.startsWith(r +
|
|
4466
|
+
} else if (resolved === r || resolved.startsWith(r + sep5)) {
|
|
4172
4467
|
return { ok: true, violations: [] };
|
|
4173
4468
|
}
|
|
4174
4469
|
}
|
|
@@ -4324,8 +4619,8 @@ function validateRoleMapping(rolesDir, options = {}) {
|
|
|
4324
4619
|
const violations = [];
|
|
4325
4620
|
const referenceById = new Map(mapping.map((m) => [m.agentId, m.reference]));
|
|
4326
4621
|
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 ${
|
|
4622
|
+
if (!existsSync14(join18(rolesDir, reference))) {
|
|
4623
|
+
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
4624
|
}
|
|
4330
4625
|
}
|
|
4331
4626
|
for (const { family, memberIds } of families) {
|
|
@@ -4584,7 +4879,7 @@ class DshMstar extends Service {
|
|
|
4584
4879
|
// src/gates/_shared.ts
|
|
4585
4880
|
var import_schemastery = __toESM(require_lib2(), 1);
|
|
4586
4881
|
import { existsSync as existsSync2 } from "node:fs";
|
|
4587
|
-
import { resolve as
|
|
4882
|
+
import { resolve as resolve5 } from "node:path";
|
|
4588
4883
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4589
4884
|
var STATUS_FILE = "status.json";
|
|
4590
4885
|
var PERSONA_INTERPOLATION_HAZARD = /\{\{[\s\S]*\}\}/;
|
|
@@ -4683,14 +4978,14 @@ class HarnessResolver {
|
|
|
4683
4978
|
explicit;
|
|
4684
4979
|
cache = new Map;
|
|
4685
4980
|
constructor(explicit) {
|
|
4686
|
-
this.explicit = explicit !== undefined && explicit.trim() !== "" ?
|
|
4981
|
+
this.explicit = explicit !== undefined && explicit.trim() !== "" ? resolve5(process.cwd(), explicit) : null;
|
|
4687
4982
|
}
|
|
4688
4983
|
forWorkspace(cwd) {
|
|
4689
4984
|
if (this.explicit !== null)
|
|
4690
4985
|
return this.explicit;
|
|
4691
4986
|
if (cwd === undefined || cwd.trim() === "")
|
|
4692
4987
|
return null;
|
|
4693
|
-
const abs =
|
|
4988
|
+
const abs = resolve5(cwd);
|
|
4694
4989
|
const hit = this.cache.get(abs);
|
|
4695
4990
|
if (hit !== undefined)
|
|
4696
4991
|
return hit;
|
|
@@ -4761,21 +5056,21 @@ function skillLocalConfig(config) {
|
|
|
4761
5056
|
}
|
|
4762
5057
|
|
|
4763
5058
|
// src/gates/catalog.ts
|
|
4764
|
-
import { existsSync as
|
|
4765
|
-
import { basename as
|
|
5059
|
+
import { existsSync as existsSync15, readFileSync as readFileSync10, readdirSync as readdirSync4, realpathSync as realpathSync6 } from "node:fs";
|
|
5060
|
+
import { basename as basename6, isAbsolute as isAbsolute9, join as join19, relative as relative7, sep as sep3 } from "node:path";
|
|
4766
5061
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
4767
5062
|
|
|
4768
5063
|
// src/gates/agent-flow.ts
|
|
4769
|
-
import { appendFileSync, closeSync, existsSync as
|
|
4770
|
-
import { join as
|
|
5064
|
+
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";
|
|
5065
|
+
import { join as join14 } from "node:path";
|
|
4771
5066
|
|
|
4772
5067
|
// src/gates/dispatch.ts
|
|
4773
|
-
import { existsSync as
|
|
4774
|
-
import { basename as
|
|
5068
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
5069
|
+
import { basename as basename3, join as join8, resolve as resolve7 } from "node:path";
|
|
4775
5070
|
|
|
4776
5071
|
// src/gates/workflow-selection.ts
|
|
4777
|
-
import { existsSync as
|
|
4778
|
-
import { dirname as
|
|
5072
|
+
import { existsSync as existsSync3, readdirSync, realpathSync, statSync as statSync2 } from "node:fs";
|
|
5073
|
+
import { dirname as dirname7, isAbsolute as isAbsolute4, join as join5, normalize, relative as relative4, sep } from "node:path";
|
|
4779
5074
|
var TERMINAL_STATUS_CACHE_MAX = 64;
|
|
4780
5075
|
var terminalStatusCache = new Map;
|
|
4781
5076
|
function terminalStatusOf(snapshotPath) {
|
|
@@ -4811,7 +5106,7 @@ function terminalStatusOf(snapshotPath) {
|
|
|
4811
5106
|
}
|
|
4812
5107
|
function stripTrailingSeparators(path) {
|
|
4813
5108
|
let out = path;
|
|
4814
|
-
while (out.length > 1 && out.endsWith(
|
|
5109
|
+
while (out.length > 1 && out.endsWith(sep))
|
|
4815
5110
|
out = out.slice(0, -1);
|
|
4816
5111
|
return out;
|
|
4817
5112
|
}
|
|
@@ -4821,13 +5116,13 @@ function lexical(path) {
|
|
|
4821
5116
|
function canonicalPath(path) {
|
|
4822
5117
|
const fallback = lexical(path);
|
|
4823
5118
|
try {
|
|
4824
|
-
return { kind: "ok", path: lexical(
|
|
5119
|
+
return { kind: "ok", path: lexical(realpathSync(path)) };
|
|
4825
5120
|
} catch (error) {
|
|
4826
5121
|
return error.code === "ENOENT" ? { kind: "missing", path: fallback } : { kind: "error" };
|
|
4827
5122
|
}
|
|
4828
5123
|
}
|
|
4829
5124
|
function within(root, cwd) {
|
|
4830
|
-
if (root === "" || cwd === "" || !
|
|
5125
|
+
if (root === "" || cwd === "" || !isAbsolute4(root) || !isAbsolute4(cwd))
|
|
4831
5126
|
return false;
|
|
4832
5127
|
const rootCanonical = canonicalPath(root);
|
|
4833
5128
|
if (rootCanonical.kind === "error")
|
|
@@ -4841,7 +5136,7 @@ function within(root, cwd) {
|
|
|
4841
5136
|
if (rootPath === cwdPath)
|
|
4842
5137
|
return true;
|
|
4843
5138
|
const rel = relative4(rootPath, cwdPath);
|
|
4844
|
-
return rel !== "" && !
|
|
5139
|
+
return rel !== "" && !isAbsolute4(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
4845
5140
|
}
|
|
4846
5141
|
function validLeases(snapshot) {
|
|
4847
5142
|
if (!Array.isArray(snapshot.plans))
|
|
@@ -4856,8 +5151,8 @@ function validLeases(snapshot) {
|
|
|
4856
5151
|
return leases;
|
|
4857
5152
|
}
|
|
4858
5153
|
function readSnapshot(harnessDir, entry) {
|
|
4859
|
-
const snapshotPath =
|
|
4860
|
-
if (!
|
|
5154
|
+
const snapshotPath = join5(harnessDir, entry.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
5155
|
+
if (!existsSync3(snapshotPath))
|
|
4861
5156
|
return;
|
|
4862
5157
|
try {
|
|
4863
5158
|
const snapshot = asRecord(readJson(snapshotPath));
|
|
@@ -4913,8 +5208,8 @@ function automaticBinding(harnessDir, entries, hint) {
|
|
|
4913
5208
|
return byCwd === undefined ? undefined : { kind: "active", workflowId: byCwd.id, dir: byCwd.dir };
|
|
4914
5209
|
}
|
|
4915
5210
|
function resolveActiveWorkflow(harnessDir, hint) {
|
|
4916
|
-
const statusPath =
|
|
4917
|
-
if (!
|
|
5211
|
+
const statusPath = join5(harnessDir, STATUS_FILE);
|
|
5212
|
+
if (!existsSync3(statusPath)) {
|
|
4918
5213
|
return {
|
|
4919
5214
|
kind: "error",
|
|
4920
5215
|
code: "status.missing",
|
|
@@ -5004,7 +5299,7 @@ function resolveReadWorkflow(harnessDir, hint) {
|
|
|
5004
5299
|
if (active.kind === "error" && active.code !== "workflow.selection.no-active")
|
|
5005
5300
|
return active;
|
|
5006
5301
|
const workflowsDir = resolveWorkflowDir(harnessDir, { harnessDir });
|
|
5007
|
-
if (!
|
|
5302
|
+
if (!existsSync3(workflowsDir)) {
|
|
5008
5303
|
return {
|
|
5009
5304
|
kind: "error",
|
|
5010
5305
|
code: "workflow.selection.no-snapshot",
|
|
@@ -5025,8 +5320,8 @@ function resolveReadWorkflow(harnessDir, hint) {
|
|
|
5025
5320
|
for (const entry of entries) {
|
|
5026
5321
|
if (!entry.isDirectory())
|
|
5027
5322
|
continue;
|
|
5028
|
-
const snapshotPath =
|
|
5029
|
-
if (!
|
|
5323
|
+
const snapshotPath = join5(workflowsDir, entry.name, WORKFLOW_SNAPSHOT_FILE);
|
|
5324
|
+
if (!existsSync3(snapshotPath)) {
|
|
5030
5325
|
terminalStatusCache.delete(snapshotPath);
|
|
5031
5326
|
continue;
|
|
5032
5327
|
}
|
|
@@ -5034,11 +5329,11 @@ function resolveReadWorkflow(harnessDir, hint) {
|
|
|
5034
5329
|
if (verdict === undefined || !verdict.terminal)
|
|
5035
5330
|
continue;
|
|
5036
5331
|
if (best === undefined || verdict.mtimeMs > best.mtimeMs) {
|
|
5037
|
-
best = { workflowId: entry.name, dir:
|
|
5332
|
+
best = { workflowId: entry.name, dir: join5(relative4(harnessDir, workflowsDir), entry.name), mtimeMs: verdict.mtimeMs };
|
|
5038
5333
|
}
|
|
5039
5334
|
}
|
|
5040
5335
|
for (const cachedPath of terminalStatusCache.keys()) {
|
|
5041
|
-
if (!
|
|
5336
|
+
if (!existsSync3(join5(dirname7(cachedPath), WORKFLOW_SNAPSHOT_FILE))) {
|
|
5042
5337
|
terminalStatusCache.delete(cachedPath);
|
|
5043
5338
|
}
|
|
5044
5339
|
}
|
|
@@ -5207,12 +5502,12 @@ function isNaValue(value) {
|
|
|
5207
5502
|
function planIdOf(headerRegion) {
|
|
5208
5503
|
const planPath = assignmentHeaderValue(headerRegion, "Plan Path");
|
|
5209
5504
|
if (!isNaValue(planPath)) {
|
|
5210
|
-
const id =
|
|
5505
|
+
const id = basename3(firstToken(planPath) ?? "");
|
|
5211
5506
|
return id.endsWith(".md") ? id.slice(0, -3) : id;
|
|
5212
5507
|
}
|
|
5213
5508
|
const sddDir = assignmentHeaderValue(headerRegion, "SDD dir");
|
|
5214
5509
|
if (!isNaValue(sddDir)) {
|
|
5215
|
-
const id =
|
|
5510
|
+
const id = basename3(firstToken(sddDir) ?? "");
|
|
5216
5511
|
return id === "" ? undefined : id;
|
|
5217
5512
|
}
|
|
5218
5513
|
const planId = assignmentHeaderValue(headerRegion, "plan_id");
|
|
@@ -5237,12 +5532,12 @@ function leaseGateViolations(harnessDir, exec, writable, prompt, hint) {
|
|
|
5237
5532
|
if (!sdd)
|
|
5238
5533
|
return [];
|
|
5239
5534
|
if (selection.kind === "error" && selection.code === "status.unreadable") {
|
|
5240
|
-
return [leaseViolation("lease.dispatch.unreadable", `cannot read ${
|
|
5535
|
+
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
5536
|
}
|
|
5242
|
-
return [leaseViolation("lease.dispatch.unverifiable", `${
|
|
5537
|
+
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
5538
|
}
|
|
5244
|
-
const snapshotPath =
|
|
5245
|
-
if (!
|
|
5539
|
+
const snapshotPath = join8(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
5540
|
+
if (!existsSync7(snapshotPath)) {
|
|
5246
5541
|
if (!sdd)
|
|
5247
5542
|
return [];
|
|
5248
5543
|
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 +5570,7 @@ function leaseGateViolations(harnessDir, exec, writable, prompt, hint) {
|
|
|
5275
5570
|
const wt = worktree === undefined ? undefined : firstToken(worktree);
|
|
5276
5571
|
if (isNaValue(wt)) {
|
|
5277
5572
|
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 (
|
|
5573
|
+
} else if (resolve7(wt) !== resolve7(String(lease.worktree_path ?? ""))) {
|
|
5279
5574
|
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
5575
|
}
|
|
5281
5576
|
const forms = parseAssignmentBranchForms(header);
|
|
@@ -5316,7 +5611,7 @@ function worktreeL2Violations(header) {
|
|
|
5316
5611
|
return violations;
|
|
5317
5612
|
}
|
|
5318
5613
|
function activeSnapshotRows(harnessDir, hint) {
|
|
5319
|
-
if (!
|
|
5614
|
+
if (!existsSync7(join8(harnessDir, STATUS_FILE)))
|
|
5320
5615
|
return { rows: null, snapshot: null, workflowId: null, unreadable: false };
|
|
5321
5616
|
const selection = resolveActiveWorkflow(harnessDir, hint);
|
|
5322
5617
|
if (selection.kind !== "active") {
|
|
@@ -5327,8 +5622,8 @@ function activeSnapshotRows(harnessDir, hint) {
|
|
|
5327
5622
|
unreadable: selection.kind === "error" && selection.code !== "workflow.selection.no-active"
|
|
5328
5623
|
};
|
|
5329
5624
|
}
|
|
5330
|
-
const snapshotDir =
|
|
5331
|
-
if (!
|
|
5625
|
+
const snapshotDir = join8(harnessDir, selection.dir);
|
|
5626
|
+
if (!existsSync7(join8(snapshotDir, WORKFLOW_SNAPSHOT_FILE))) {
|
|
5332
5627
|
return { rows: null, snapshot: null, workflowId: selection.workflowId, unreadable: true };
|
|
5333
5628
|
}
|
|
5334
5629
|
let snapshot;
|
|
@@ -5432,7 +5727,7 @@ function gateWorkflow(ctx, harnessDir, config, adapter, exec, hintReadOf) {
|
|
|
5432
5727
|
}
|
|
5433
5728
|
const pb = writableFanOutUncovered(harnessDir, hintReadOf().hint);
|
|
5434
5729
|
if (pb.unreadable) {
|
|
5435
|
-
const statusLabel = harnessDir === null ? STATUS_FILE :
|
|
5730
|
+
const statusLabel = harnessDir === null ? STATUS_FILE : join8(harnessDir, STATUS_FILE);
|
|
5436
5731
|
ctx.logger(DISPATCH_LOGGER).warn(`workflow gate (${toolName}) (fail-open): cannot read ${statusLabel} — P-b lease attribution degraded; call allowed`);
|
|
5437
5732
|
}
|
|
5438
5733
|
const verdict = workflowPolicy(config, adapter.workflowAskCache, {
|
|
@@ -5637,7 +5932,7 @@ function callPairingKey(exec) {
|
|
|
5637
5932
|
function resolveAgentFlowWriteTarget(harnessDir, hint) {
|
|
5638
5933
|
const selection = resolveActiveWorkflow(harnessDir, hint);
|
|
5639
5934
|
if (selection.kind === "active")
|
|
5640
|
-
return { dir:
|
|
5935
|
+
return { dir: join14(harnessDir, selection.dir), selection };
|
|
5641
5936
|
const message = selection.kind === "error" ? selection.message : "no active lifecycle (unexpected terminal selection — the active-set resolver has no history view)";
|
|
5642
5937
|
if (!noActiveWarned) {
|
|
5643
5938
|
noActiveWarned = true;
|
|
@@ -5657,7 +5952,7 @@ function sleepSync(ms) {
|
|
|
5657
5952
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
5658
5953
|
}
|
|
5659
5954
|
function withWorkflowDirLock(workflowDir, fn, opts = {}) {
|
|
5660
|
-
const lockDir =
|
|
5955
|
+
const lockDir = join14(workflowDir, WORKFLOW_LEDGER_LOCKDIR);
|
|
5661
5956
|
if (heldWorkflowLocks.has(lockDir)) {
|
|
5662
5957
|
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
5958
|
}
|
|
@@ -5683,7 +5978,7 @@ function withWorkflowDirLock(workflowDir, fn, opts = {}) {
|
|
|
5683
5978
|
heldWorkflowLocks.add(lockDir);
|
|
5684
5979
|
try {
|
|
5685
5980
|
try {
|
|
5686
|
-
writeFileSync2(
|
|
5981
|
+
writeFileSync2(join14(lockDir, WORKFLOW_LOCKER_HOLDER_PID), String(process.pid), "utf8");
|
|
5687
5982
|
} catch {}
|
|
5688
5983
|
return fn();
|
|
5689
5984
|
} finally {
|
|
@@ -5692,7 +5987,7 @@ function withWorkflowDirLock(workflowDir, fn, opts = {}) {
|
|
|
5692
5987
|
const current = statSync5(lockDir);
|
|
5693
5988
|
if (acquired !== null && current.dev === acquired.dev && current.ino === acquired.ino) {
|
|
5694
5989
|
try {
|
|
5695
|
-
unlinkSync2(
|
|
5990
|
+
unlinkSync2(join14(lockDir, WORKFLOW_LOCKER_HOLDER_PID));
|
|
5696
5991
|
} catch {}
|
|
5697
5992
|
rmdirSync(lockDir);
|
|
5698
5993
|
}
|
|
@@ -5700,13 +5995,13 @@ function withWorkflowDirLock(workflowDir, fn, opts = {}) {
|
|
|
5700
5995
|
}
|
|
5701
5996
|
}
|
|
5702
5997
|
function appendEvent(workflowDir, event) {
|
|
5703
|
-
const file =
|
|
5998
|
+
const file = join14(workflowDir, AGENT_FLOW_FILE);
|
|
5704
5999
|
withWorkflowDirLock(workflowDir, () => {
|
|
5705
6000
|
appendFileSync(file, `${JSON.stringify(event)}
|
|
5706
6001
|
`);
|
|
5707
6002
|
if (statSync5(file).size <= AGENT_FLOW_SIZE_GATE_BYTES)
|
|
5708
6003
|
return;
|
|
5709
|
-
const content =
|
|
6004
|
+
const content = readFileSync3(file, "utf8");
|
|
5710
6005
|
const lines = content.replace(/\n$/, "").split(`
|
|
5711
6006
|
`);
|
|
5712
6007
|
if (lines.length > AGENT_FLOW_MAX_EVENTS) {
|
|
@@ -6110,7 +6405,7 @@ function summaryOf(events) {
|
|
|
6110
6405
|
function ledgerContent(file, n) {
|
|
6111
6406
|
const size = statSync5(file).size;
|
|
6112
6407
|
if (size <= AGENT_FLOW_TAIL_READ_THRESHOLD_BYTES) {
|
|
6113
|
-
return
|
|
6408
|
+
return readFileSync3(file, "utf8");
|
|
6114
6409
|
}
|
|
6115
6410
|
let windowBytes = AGENT_FLOW_TAIL_READ_THRESHOLD_BYTES;
|
|
6116
6411
|
const fd = openSync(file, "r");
|
|
@@ -6142,8 +6437,8 @@ function ledgerContent(file, n) {
|
|
|
6142
6437
|
}
|
|
6143
6438
|
}
|
|
6144
6439
|
function readAgentFlow(workflowDir, limit) {
|
|
6145
|
-
const file =
|
|
6146
|
-
if (!
|
|
6440
|
+
const file = join14(workflowDir, AGENT_FLOW_FILE);
|
|
6441
|
+
if (!existsSync11(file)) {
|
|
6147
6442
|
return { events: [], summary: [] };
|
|
6148
6443
|
}
|
|
6149
6444
|
const n = limit === undefined ? AGENT_FLOW_DEFAULT_LIMIT : Math.max(0, Math.floor(limit));
|
|
@@ -6308,8 +6603,8 @@ function reserveCatalogCandidate(pairing, ref, exec) {
|
|
|
6308
6603
|
map.set(label, { ref, fromSeq: seq });
|
|
6309
6604
|
ref.catalog = { session, label };
|
|
6310
6605
|
}
|
|
6311
|
-
function matchCatalogEvent(session, label,
|
|
6312
|
-
const result =
|
|
6606
|
+
function matchCatalogEvent(session, label, join9, envelope) {
|
|
6607
|
+
const result = join9.result;
|
|
6313
6608
|
if (result === undefined)
|
|
6314
6609
|
return;
|
|
6315
6610
|
const rec = asRecord(envelope);
|
|
@@ -6323,7 +6618,7 @@ function matchCatalogEvent(session, label, join8, envelope) {
|
|
|
6323
6618
|
const seq = rec.seq;
|
|
6324
6619
|
if (typeof seq !== "number" || !Number.isInteger(seq))
|
|
6325
6620
|
return;
|
|
6326
|
-
if (seq <
|
|
6621
|
+
if (seq < join9.fromSeq || seq >= session.seq || seq >= WORKFLOW_LEDGER_MAX_SEQ)
|
|
6327
6622
|
return;
|
|
6328
6623
|
const childCreatedAt = data.childCreatedAt;
|
|
6329
6624
|
if (typeof childCreatedAt !== "number" || !Number.isInteger(childCreatedAt) || childCreatedAt < 0)
|
|
@@ -6339,9 +6634,9 @@ function matchCatalogEvent(session, label, join8, envelope) {
|
|
|
6339
6634
|
return;
|
|
6340
6635
|
return childId;
|
|
6341
6636
|
}
|
|
6342
|
-
function consumeCatalogCandidate(map, label,
|
|
6637
|
+
function consumeCatalogCandidate(map, label, join9, childId) {
|
|
6343
6638
|
map.set(label, null);
|
|
6344
|
-
const ref =
|
|
6639
|
+
const ref = join9.ref;
|
|
6345
6640
|
ref.childId = childId;
|
|
6346
6641
|
delete ref.catalog;
|
|
6347
6642
|
recordSubagentLink({ ref, childId, label });
|
|
@@ -6352,16 +6647,16 @@ function advanceCatalogCandidate(pairing, ref, result) {
|
|
|
6352
6647
|
if (catalog === undefined)
|
|
6353
6648
|
return;
|
|
6354
6649
|
const map = pairing.catalogBySession.get(catalog.session);
|
|
6355
|
-
const
|
|
6356
|
-
if (map === undefined ||
|
|
6650
|
+
const join9 = map?.get(catalog.label);
|
|
6651
|
+
if (map === undefined || join9 === undefined || join9 === null || join9.ref !== ref)
|
|
6357
6652
|
return;
|
|
6358
|
-
|
|
6653
|
+
join9.result = result;
|
|
6359
6654
|
const end = catalog.session.seq;
|
|
6360
6655
|
let skippedReads = 0;
|
|
6361
6656
|
let firstSkippedSeq;
|
|
6362
6657
|
let firstSkippedError;
|
|
6363
6658
|
try {
|
|
6364
|
-
for (let seq =
|
|
6659
|
+
for (let seq = join9.fromSeq;seq < end; seq += 1) {
|
|
6365
6660
|
let envelope;
|
|
6366
6661
|
try {
|
|
6367
6662
|
envelope = catalog.session.eventAt(seq);
|
|
@@ -6373,10 +6668,10 @@ function advanceCatalogCandidate(pairing, ref, result) {
|
|
|
6373
6668
|
}
|
|
6374
6669
|
continue;
|
|
6375
6670
|
}
|
|
6376
|
-
const childId = matchCatalogEvent(catalog.session, catalog.label,
|
|
6671
|
+
const childId = matchCatalogEvent(catalog.session, catalog.label, join9, envelope);
|
|
6377
6672
|
if (childId === undefined)
|
|
6378
6673
|
continue;
|
|
6379
|
-
consumeCatalogCandidate(map, catalog.label,
|
|
6674
|
+
consumeCatalogCandidate(map, catalog.label, join9, childId);
|
|
6380
6675
|
return;
|
|
6381
6676
|
}
|
|
6382
6677
|
} finally {
|
|
@@ -6451,13 +6746,13 @@ function registerSubagentCatalogListener(ctx, pairing) {
|
|
|
6451
6746
|
const map = pairing.catalogBySession.get(session);
|
|
6452
6747
|
if (map === undefined)
|
|
6453
6748
|
return;
|
|
6454
|
-
for (const [label,
|
|
6455
|
-
if (
|
|
6749
|
+
for (const [label, join9] of map) {
|
|
6750
|
+
if (join9 === null || join9.result === undefined)
|
|
6456
6751
|
continue;
|
|
6457
|
-
const childId = matchCatalogEvent(session, label,
|
|
6752
|
+
const childId = matchCatalogEvent(session, label, join9, envelope);
|
|
6458
6753
|
if (childId === undefined)
|
|
6459
6754
|
continue;
|
|
6460
|
-
consumeCatalogCandidate(map, label,
|
|
6755
|
+
consumeCatalogCandidate(map, label, join9, childId);
|
|
6461
6756
|
}
|
|
6462
6757
|
} catch (error) {
|
|
6463
6758
|
log("warn", `subagent catalog observation failed (contained — the session append proceeds): ${errorMessage(error)}`);
|
|
@@ -6466,8 +6761,8 @@ function registerSubagentCatalogListener(ctx, pairing) {
|
|
|
6466
6761
|
}
|
|
6467
6762
|
|
|
6468
6763
|
// src/engine-status-store.ts
|
|
6469
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
6470
|
-
import { dirname as
|
|
6764
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync3, rmSync, statSync as statSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6765
|
+
import { dirname as dirname10, join as join16 } from "node:path";
|
|
6471
6766
|
var ENGINE_STATUS_SNAPSHOT_VERSION = 1;
|
|
6472
6767
|
var ENGINE_STATUS_SNAPSHOT_ENTRY_VERSION = 1;
|
|
6473
6768
|
var ENGINE_STATUS_SNAPSHOT_MAX_PER_SESSION = 50;
|
|
@@ -6476,19 +6771,19 @@ var ENGINE_STATUS_SNAPSHOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
|
6476
6771
|
var ENGINE_STATUS_SNAPSHOT_LOCK_TIMEOUT_MS = 250;
|
|
6477
6772
|
var ENGINE_STATUS_SNAPSHOT_RELATIVE_PATH = "snapshots/engine-status.json";
|
|
6478
6773
|
function engineStatusSnapshotPath(harnessDir) {
|
|
6479
|
-
return
|
|
6774
|
+
return join16(harnessDir, ENGINE_STATUS_SNAPSHOT_RELATIVE_PATH);
|
|
6480
6775
|
}
|
|
6481
6776
|
function engineStatusUnavailable(reason) {
|
|
6482
6777
|
return { kind: "unavailable", reason };
|
|
6483
6778
|
}
|
|
6484
|
-
function
|
|
6779
|
+
function isPlainObject4(value) {
|
|
6485
6780
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6486
6781
|
}
|
|
6487
6782
|
function nonEmptyString(value) {
|
|
6488
6783
|
return typeof value === "string" && value !== "" ? value : undefined;
|
|
6489
6784
|
}
|
|
6490
6785
|
function asEntry(value) {
|
|
6491
|
-
if (!
|
|
6786
|
+
if (!isPlainObject4(value))
|
|
6492
6787
|
return;
|
|
6493
6788
|
if (value.rv !== ENGINE_STATUS_SNAPSHOT_ENTRY_VERSION)
|
|
6494
6789
|
return;
|
|
@@ -6498,12 +6793,12 @@ function asEntry(value) {
|
|
|
6498
6793
|
return;
|
|
6499
6794
|
if (typeof value.turn !== "number" || !Number.isFinite(value.turn))
|
|
6500
6795
|
return;
|
|
6501
|
-
if (!
|
|
6796
|
+
if (!isPlainObject4(value.payload))
|
|
6502
6797
|
return;
|
|
6503
6798
|
return { rv: value.rv, cwd, at, turn: value.turn, payload: value.payload };
|
|
6504
6799
|
}
|
|
6505
6800
|
function asBinding(value) {
|
|
6506
|
-
if (!
|
|
6801
|
+
if (!isPlainObject4(value))
|
|
6507
6802
|
return;
|
|
6508
6803
|
const cwd = nonEmptyString(value.cwd);
|
|
6509
6804
|
if (cwd === undefined)
|
|
@@ -6534,7 +6829,7 @@ function readEngineStatusSnapshot(harnessDir, sessionId) {
|
|
|
6534
6829
|
return engineStatusUnavailable("no-session-entry");
|
|
6535
6830
|
let raw;
|
|
6536
6831
|
try {
|
|
6537
|
-
raw =
|
|
6832
|
+
raw = readFileSync7(engineStatusSnapshotPath(harnessDir), "utf8");
|
|
6538
6833
|
} catch (error) {
|
|
6539
6834
|
const code = error.code;
|
|
6540
6835
|
return engineStatusUnavailable(code === "ENOENT" ? "absent" : "unreadable");
|
|
@@ -6545,11 +6840,11 @@ function readEngineStatusSnapshot(harnessDir, sessionId) {
|
|
|
6545
6840
|
} catch {
|
|
6546
6841
|
return engineStatusUnavailable("invalid-json");
|
|
6547
6842
|
}
|
|
6548
|
-
if (!
|
|
6843
|
+
if (!isPlainObject4(doc) || doc.sv !== ENGINE_STATUS_SNAPSHOT_VERSION) {
|
|
6549
6844
|
return engineStatusUnavailable("envelope-schema");
|
|
6550
6845
|
}
|
|
6551
6846
|
const entries = doc.entries;
|
|
6552
|
-
if (!
|
|
6847
|
+
if (!isPlainObject4(entries))
|
|
6553
6848
|
return engineStatusUnavailable("envelope-schema");
|
|
6554
6849
|
if (!Object.hasOwn(entries, sessionId))
|
|
6555
6850
|
return engineStatusUnavailable("no-session-entry");
|
|
@@ -6574,7 +6869,7 @@ function serializeEnvelope(entries, bindings) {
|
|
|
6574
6869
|
function loadForWrite(harnessDir) {
|
|
6575
6870
|
let raw;
|
|
6576
6871
|
try {
|
|
6577
|
-
raw =
|
|
6872
|
+
raw = readFileSync7(engineStatusSnapshotPath(harnessDir), "utf8");
|
|
6578
6873
|
} catch (error) {
|
|
6579
6874
|
if (error.code === "ENOENT") {
|
|
6580
6875
|
return {
|
|
@@ -6590,7 +6885,7 @@ function loadForWrite(harnessDir) {
|
|
|
6590
6885
|
} catch {
|
|
6591
6886
|
return { kind: "refused", reason: "store-invalid-json" };
|
|
6592
6887
|
}
|
|
6593
|
-
if (!
|
|
6888
|
+
if (!isPlainObject4(parsed) || parsed.sv !== ENGINE_STATUS_SNAPSHOT_VERSION || !isPlainObject4(parsed.entries)) {
|
|
6594
6889
|
return { kind: "refused", reason: "store-envelope-schema" };
|
|
6595
6890
|
}
|
|
6596
6891
|
const entries = sessionMap();
|
|
@@ -6609,7 +6904,7 @@ function loadForWrite(harnessDir) {
|
|
|
6609
6904
|
}
|
|
6610
6905
|
const bindings = bindingMap();
|
|
6611
6906
|
if (parsed.bindings !== undefined) {
|
|
6612
|
-
if (!
|
|
6907
|
+
if (!isPlainObject4(parsed.bindings))
|
|
6613
6908
|
return { kind: "refused", reason: "store-bindings-schema" };
|
|
6614
6909
|
for (const [key, value] of Object.entries(parsed.bindings)) {
|
|
6615
6910
|
const binding = asBinding(value);
|
|
@@ -6664,12 +6959,12 @@ function writeEngineStatusSnapshot(harnessDir, input) {
|
|
|
6664
6959
|
return { kind: "degraded", reason: "no-session-cwd" };
|
|
6665
6960
|
if (!Number.isFinite(input.turn))
|
|
6666
6961
|
return { kind: "degraded", reason: "invalid-turn" };
|
|
6667
|
-
if (!
|
|
6962
|
+
if (!isPlainObject4(input.payload))
|
|
6668
6963
|
return { kind: "degraded", reason: "payload-not-object" };
|
|
6669
6964
|
const at = (input.now ?? new Date).toISOString();
|
|
6670
6965
|
if (!Number.isFinite(Date.parse(at)))
|
|
6671
6966
|
return { kind: "degraded", reason: "invalid-timestamp" };
|
|
6672
|
-
const dir =
|
|
6967
|
+
const dir = dirname10(engineStatusSnapshotPath(harnessDir));
|
|
6673
6968
|
const file = engineStatusSnapshotPath(harnessDir);
|
|
6674
6969
|
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
|
|
6675
6970
|
try {
|
|
@@ -6732,7 +7027,7 @@ var bindingStoreMemos = new Map;
|
|
|
6732
7027
|
function parseBindingStore(file) {
|
|
6733
7028
|
let raw;
|
|
6734
7029
|
try {
|
|
6735
|
-
raw =
|
|
7030
|
+
raw = readFileSync7(file, "utf8");
|
|
6736
7031
|
} catch {
|
|
6737
7032
|
return { kind: "unavailable", reason: "store-unreadable" };
|
|
6738
7033
|
}
|
|
@@ -6742,13 +7037,13 @@ function parseBindingStore(file) {
|
|
|
6742
7037
|
} catch {
|
|
6743
7038
|
return { kind: "unavailable", reason: "store-invalid-json" };
|
|
6744
7039
|
}
|
|
6745
|
-
if (!
|
|
7040
|
+
if (!isPlainObject4(parsed) || parsed.sv !== ENGINE_STATUS_SNAPSHOT_VERSION || !isPlainObject4(parsed.entries)) {
|
|
6746
7041
|
return { kind: "unavailable", reason: "store-envelope-schema" };
|
|
6747
7042
|
}
|
|
6748
7043
|
const bindings = parsed.bindings;
|
|
6749
7044
|
if (bindings === undefined)
|
|
6750
7045
|
return { kind: "ok", bindings: undefined };
|
|
6751
|
-
if (!
|
|
7046
|
+
if (!isPlainObject4(bindings))
|
|
6752
7047
|
return { kind: "unavailable", reason: "store-bindings-schema" };
|
|
6753
7048
|
return { kind: "ok", bindings };
|
|
6754
7049
|
}
|
|
@@ -6806,7 +7101,7 @@ function updateWorkflowSessionBinding(harnessDir, sessionId, cwd, update) {
|
|
|
6806
7101
|
return { kind: "degraded", reason: "invalid-selected-workflow-id" };
|
|
6807
7102
|
}
|
|
6808
7103
|
const file = engineStatusSnapshotPath(harnessDir);
|
|
6809
|
-
const dir =
|
|
7104
|
+
const dir = dirname10(file);
|
|
6810
7105
|
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
|
|
6811
7106
|
try {
|
|
6812
7107
|
mkdirSync4(dir, { recursive: true });
|
|
@@ -6858,7 +7153,7 @@ function residualSeverityIndex(severity) {
|
|
|
6858
7153
|
function pluginVersion() {
|
|
6859
7154
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
6860
7155
|
try {
|
|
6861
|
-
const pkg = JSON.parse(
|
|
7156
|
+
const pkg = JSON.parse(readFileSync10(new URL(rel, import.meta.url), "utf8"));
|
|
6862
7157
|
if (typeof pkg.version === "string" && pkg.version !== "")
|
|
6863
7158
|
return pkg.version;
|
|
6864
7159
|
} catch {}
|
|
@@ -7024,8 +7319,8 @@ function hhmm(ts) {
|
|
|
7024
7319
|
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
7025
7320
|
}
|
|
7026
7321
|
function harnessStateSource(harnessDir, selection) {
|
|
7027
|
-
const statusPath =
|
|
7028
|
-
if (!
|
|
7322
|
+
const statusPath = join19(harnessDir, STATUS_FILE);
|
|
7323
|
+
if (!existsSync15(statusPath))
|
|
7029
7324
|
return null;
|
|
7030
7325
|
try {
|
|
7031
7326
|
const rollup = residualRollup(harnessDir);
|
|
@@ -7034,9 +7329,9 @@ function harnessStateSource(harnessDir, selection) {
|
|
|
7034
7329
|
if (selection.kind === "error") {
|
|
7035
7330
|
return selectionErrorState(selection, rollup, harnessDir);
|
|
7036
7331
|
}
|
|
7037
|
-
const snapshotPath =
|
|
7332
|
+
const snapshotPath = join19(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
7038
7333
|
let snapshot;
|
|
7039
|
-
if (!
|
|
7334
|
+
if (!existsSync15(snapshotPath)) {
|
|
7040
7335
|
return selectionErrorState({
|
|
7041
7336
|
kind: "error",
|
|
7042
7337
|
code: "workflow.selection.snapshot-unreadable",
|
|
@@ -7044,7 +7339,7 @@ function harnessStateSource(harnessDir, selection) {
|
|
|
7044
7339
|
}, rollup, harnessDir);
|
|
7045
7340
|
}
|
|
7046
7341
|
try {
|
|
7047
|
-
snapshot = readWorkflowSnapshot(
|
|
7342
|
+
snapshot = readWorkflowSnapshot(join19(harnessDir, selection.dir)).snapshot;
|
|
7048
7343
|
} catch {
|
|
7049
7344
|
return selectionErrorState({
|
|
7050
7345
|
kind: "error",
|
|
@@ -7100,7 +7395,7 @@ function harnessStateSource(harnessDir, selection) {
|
|
|
7100
7395
|
leases,
|
|
7101
7396
|
knowledge: knowledgeDigest(harnessDir),
|
|
7102
7397
|
direction: compass !== undefined ? compassDirection(compass.compassPath) : null,
|
|
7103
|
-
agentFlow: readAgentFlow(
|
|
7398
|
+
agentFlow: readAgentFlow(join19(harnessDir, selection.dir), 50)
|
|
7104
7399
|
};
|
|
7105
7400
|
} catch {
|
|
7106
7401
|
return null;
|
|
@@ -7130,7 +7425,7 @@ function selectionErrorState(selection, rollup, harnessDir) {
|
|
|
7130
7425
|
function projectRollupSource(harnessDir, residuals) {
|
|
7131
7426
|
const milestones = [];
|
|
7132
7427
|
const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
7133
|
-
if (
|
|
7428
|
+
if (existsSync15(projectsDir)) {
|
|
7134
7429
|
let entries;
|
|
7135
7430
|
try {
|
|
7136
7431
|
entries = readdirSync4(projectsDir, { withFileTypes: true });
|
|
@@ -7140,11 +7435,11 @@ function projectRollupSource(harnessDir, residuals) {
|
|
|
7140
7435
|
for (const entry of entries) {
|
|
7141
7436
|
if (!entry.isDirectory())
|
|
7142
7437
|
continue;
|
|
7143
|
-
const roadmapPath =
|
|
7144
|
-
if (!
|
|
7438
|
+
const roadmapPath = join19(projectsDir, entry.name, PROJECT_ROADMAP_FILE);
|
|
7439
|
+
if (!existsSync15(roadmapPath))
|
|
7145
7440
|
continue;
|
|
7146
7441
|
try {
|
|
7147
|
-
const doc = parseCompassFrontmatterText(
|
|
7442
|
+
const doc = parseCompassFrontmatterText(readFileSync10(roadmapPath, "utf8"), roadmapPath);
|
|
7148
7443
|
if (Array.isArray(doc.milestones)) {
|
|
7149
7444
|
for (const milestone of doc.milestones) {
|
|
7150
7445
|
if (typeof milestone === "string" && milestone.trim() !== "")
|
|
@@ -7206,7 +7501,7 @@ function residualRollup(harnessDir) {
|
|
|
7206
7501
|
}
|
|
7207
7502
|
function projectRegisters(harnessDir) {
|
|
7208
7503
|
const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
7209
|
-
if (!
|
|
7504
|
+
if (!existsSync15(projectsDir))
|
|
7210
7505
|
return [];
|
|
7211
7506
|
let entries;
|
|
7212
7507
|
try {
|
|
@@ -7218,8 +7513,8 @@ function projectRegisters(harnessDir) {
|
|
|
7218
7513
|
for (const entry of entries) {
|
|
7219
7514
|
if (!entry.isDirectory())
|
|
7220
7515
|
continue;
|
|
7221
|
-
const registerPath =
|
|
7222
|
-
if (!
|
|
7516
|
+
const registerPath = join19(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
7517
|
+
if (!existsSync15(registerPath))
|
|
7223
7518
|
continue;
|
|
7224
7519
|
try {
|
|
7225
7520
|
registers.push({ projectId: entry.name, doc: readJson(registerPath) });
|
|
@@ -7230,13 +7525,13 @@ function projectRegisters(harnessDir) {
|
|
|
7230
7525
|
return registers;
|
|
7231
7526
|
}
|
|
7232
7527
|
function knowledgeDigest(harnessDir) {
|
|
7233
|
-
const indexPath =
|
|
7234
|
-
if (!
|
|
7528
|
+
const indexPath = join19(harnessDir, "knowledge", "README.md");
|
|
7529
|
+
if (!existsSync15(indexPath))
|
|
7235
7530
|
return null;
|
|
7236
7531
|
try {
|
|
7237
7532
|
const categories = new Set;
|
|
7238
7533
|
let docCount = 0;
|
|
7239
|
-
for (const line of
|
|
7534
|
+
for (const line of readFileSync10(indexPath, "utf8").split(/\r?\n/)) {
|
|
7240
7535
|
const row = line.trim().match(/^\|(.+)\|$/);
|
|
7241
7536
|
if (row === null)
|
|
7242
7537
|
continue;
|
|
@@ -7259,7 +7554,7 @@ function knowledgeDigest(harnessDir) {
|
|
|
7259
7554
|
}
|
|
7260
7555
|
function compassDirection(compassPath) {
|
|
7261
7556
|
try {
|
|
7262
|
-
const content =
|
|
7557
|
+
const content = readFileSync10(compassPath, "utf8");
|
|
7263
7558
|
const section = content.match(/^## Direction lock[^\n]*\n+([\s\S]*?)(?=\n## |$)/m);
|
|
7264
7559
|
if (section === null)
|
|
7265
7560
|
return null;
|
|
@@ -7276,20 +7571,20 @@ function compassDirection(compassPath) {
|
|
|
7276
7571
|
}
|
|
7277
7572
|
function selectedCompass(harnessDir, workflowId, snapshot) {
|
|
7278
7573
|
const ref = snapshot.compass_ref;
|
|
7279
|
-
if (typeof ref !== "string" || ref.trim() === "" ||
|
|
7574
|
+
if (typeof ref !== "string" || ref.trim() === "" || isAbsolute9(ref))
|
|
7280
7575
|
return;
|
|
7281
|
-
const compassPath =
|
|
7576
|
+
const compassPath = join19(harnessDir, ref);
|
|
7282
7577
|
if (!pathInside(harnessDir, compassPath))
|
|
7283
7578
|
return;
|
|
7284
|
-
if (
|
|
7579
|
+
if (basename6(compassPath) !== "delivery-compass.md")
|
|
7285
7580
|
return;
|
|
7286
|
-
if (!
|
|
7581
|
+
if (!existsSync15(compassPath))
|
|
7287
7582
|
return;
|
|
7288
7583
|
let resolvedHarness;
|
|
7289
7584
|
let resolvedCompass;
|
|
7290
7585
|
try {
|
|
7291
|
-
resolvedHarness =
|
|
7292
|
-
resolvedCompass =
|
|
7586
|
+
resolvedHarness = realpathSync6(harnessDir);
|
|
7587
|
+
resolvedCompass = realpathSync6(compassPath);
|
|
7293
7588
|
} catch {
|
|
7294
7589
|
return;
|
|
7295
7590
|
}
|
|
@@ -7309,15 +7604,15 @@ function selectedCompass(harnessDir, workflowId, snapshot) {
|
|
|
7309
7604
|
}
|
|
7310
7605
|
function pathInside(root, candidate) {
|
|
7311
7606
|
const rel = relative7(root, candidate);
|
|
7312
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep3}`) && !
|
|
7607
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute9(rel);
|
|
7313
7608
|
}
|
|
7314
7609
|
function iterationGateSource(harnessDir, selection) {
|
|
7315
7610
|
if (harnessDir === null || selection === undefined || selection.kind === "error")
|
|
7316
7611
|
return;
|
|
7317
|
-
const statusPath =
|
|
7318
|
-
if (!
|
|
7612
|
+
const statusPath = join19(harnessDir, STATUS_FILE);
|
|
7613
|
+
if (!existsSync15(statusPath))
|
|
7319
7614
|
return;
|
|
7320
|
-
const snapshotPath =
|
|
7615
|
+
const snapshotPath = join19(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
7321
7616
|
try {
|
|
7322
7617
|
const snapshotDoc = readJson(snapshotPath);
|
|
7323
7618
|
const compass = selectedCompass(harnessDir, selection.workflowId, snapshotDoc);
|
|
@@ -7407,7 +7702,7 @@ function persistEngineStatusSnapshot(ctx, harnessDir, cwd, turn, catalogPayload,
|
|
|
7407
7702
|
}
|
|
7408
7703
|
|
|
7409
7704
|
// src/engine-status-endpoint.ts
|
|
7410
|
-
import { isAbsolute as
|
|
7705
|
+
import { isAbsolute as isAbsolute11 } from "node:path";
|
|
7411
7706
|
|
|
7412
7707
|
// ../../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
7708
|
import { Service as Service2 } from "@deepseek-ai/cordis";
|
|
@@ -7463,7 +7758,7 @@ function liveSeqOf(value) {
|
|
|
7463
7758
|
function isRefusedPath(cwd) {
|
|
7464
7759
|
if (cwd.includes("\x00"))
|
|
7465
7760
|
return true;
|
|
7466
|
-
if (!
|
|
7761
|
+
if (!isAbsolute11(cwd))
|
|
7467
7762
|
return true;
|
|
7468
7763
|
return cwd.split(/[\\/]+/).includes("..");
|
|
7469
7764
|
}
|
|
@@ -7700,11 +7995,11 @@ function installEngineStatusEndpoint(ctx, options) {
|
|
|
7700
7995
|
|
|
7701
7996
|
// src/gates/status.ts
|
|
7702
7997
|
import { existsSync as existsSync17, readdirSync as readdirSync7 } from "node:fs";
|
|
7703
|
-
import { basename as
|
|
7998
|
+
import { basename as basename8, join as join21, relative as relative9, resolve as resolve11 } from "node:path";
|
|
7704
7999
|
var LOGGER_NAME = "mstar/status-gate";
|
|
7705
8000
|
function harnessDocKindOfTarget(harnessDir, targetPath) {
|
|
7706
|
-
const resolved =
|
|
7707
|
-
const name =
|
|
8001
|
+
const resolved = resolve11(targetPath);
|
|
8002
|
+
const name = basename8(resolved);
|
|
7708
8003
|
if (name !== STATUS_FILE && name !== WORKFLOW_SNAPSHOT_FILE && name !== PROJECT_REGISTER_FILE)
|
|
7709
8004
|
return null;
|
|
7710
8005
|
const rel = relative9(harnessDir, resolved);
|
|
@@ -7716,8 +8011,8 @@ function harnessDocKindOfTarget(harnessDir, targetPath) {
|
|
|
7716
8011
|
workflowDir = resolveWorkflowDir(harnessDir, { harnessDir });
|
|
7717
8012
|
projectDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
7718
8013
|
} catch {
|
|
7719
|
-
workflowDir =
|
|
7720
|
-
projectDir =
|
|
8014
|
+
workflowDir = join21(harnessDir, "workflows");
|
|
8015
|
+
projectDir = join21(harnessDir, "projects");
|
|
7721
8016
|
}
|
|
7722
8017
|
if (name === WORKFLOW_SNAPSHOT_FILE && /^[^/]+\/snapshot\.json$/.test(relative9(workflowDir, resolved)))
|
|
7723
8018
|
return "snapshot";
|
|
@@ -7781,7 +8076,7 @@ function projectRegisterDocs(harnessDir) {
|
|
|
7781
8076
|
for (const entry of entries) {
|
|
7782
8077
|
if (!entry.isDirectory())
|
|
7783
8078
|
continue;
|
|
7784
|
-
const registerPath =
|
|
8079
|
+
const registerPath = join21(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
7785
8080
|
if (!existsSync17(registerPath))
|
|
7786
8081
|
continue;
|
|
7787
8082
|
try {
|
|
@@ -7855,8 +8150,8 @@ async function editIntentListener(ctx, resolver, config, adapter, target, actor,
|
|
|
7855
8150
|
}
|
|
7856
8151
|
|
|
7857
8152
|
// src/gates/skill-lint.ts
|
|
7858
|
-
import { existsSync as existsSync18, readFileSync as
|
|
7859
|
-
import { basename as
|
|
8153
|
+
import { existsSync as existsSync18, readFileSync as readFileSync13 } from "node:fs";
|
|
8154
|
+
import { basename as basename9, dirname as dirname12, resolve as resolve13, sep as sep6 } from "node:path";
|
|
7860
8155
|
var SKILL_LINT_LOGGER = "mstar/skill-lint";
|
|
7861
8156
|
|
|
7862
8157
|
class SkillLintVetoError extends Error {
|
|
@@ -7897,7 +8192,7 @@ function lintSkillDoc(doc, options = {}) {
|
|
|
7897
8192
|
return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
|
|
7898
8193
|
}
|
|
7899
8194
|
function lintSkillWrite(doc, options) {
|
|
7900
|
-
const skillId =
|
|
8195
|
+
const skillId = basename9(dirname12(resolve13(options.target)));
|
|
7901
8196
|
const result = lintSkillDoc(doc, { skillId });
|
|
7902
8197
|
if (options.hard && !result.ok) {
|
|
7903
8198
|
throw new SkillLintVetoError(options.target, result.violations);
|
|
@@ -7910,13 +8205,13 @@ function skillRootsOf(config) {
|
|
|
7910
8205
|
return roots.map((root) => root.trim()).filter((root) => root !== "");
|
|
7911
8206
|
}
|
|
7912
8207
|
function isSkillTarget(roots, target) {
|
|
7913
|
-
if (
|
|
8208
|
+
if (basename9(target.displayPath) !== "SKILL.md")
|
|
7914
8209
|
return false;
|
|
7915
|
-
const resolvedPath =
|
|
7916
|
-
return roots.some((root) => resolvedPath.startsWith(
|
|
8210
|
+
const resolvedPath = resolve13(target.displayPath);
|
|
8211
|
+
return roots.some((root) => resolvedPath.startsWith(resolve13(root) + sep6));
|
|
7917
8212
|
}
|
|
7918
8213
|
function skillNameOf(target) {
|
|
7919
|
-
return
|
|
8214
|
+
return basename9(dirname12(target.displayPath));
|
|
7920
8215
|
}
|
|
7921
8216
|
function skillCanonicalForm(target) {
|
|
7922
8217
|
return resolveSkillRoot("dsh", { skill: skillNameOf(target), rel: "SKILL.md" });
|
|
@@ -7928,12 +8223,12 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
|
|
|
7928
8223
|
return;
|
|
7929
8224
|
if (!isSkillTarget(roots, target))
|
|
7930
8225
|
return;
|
|
7931
|
-
const skillPath =
|
|
8226
|
+
const skillPath = resolve13(target.displayPath);
|
|
7932
8227
|
if (!existsSync18(skillPath))
|
|
7933
8228
|
return;
|
|
7934
8229
|
let doc;
|
|
7935
8230
|
try {
|
|
7936
|
-
doc =
|
|
8231
|
+
doc = readFileSync13(skillPath, "utf8");
|
|
7937
8232
|
} catch (error) {
|
|
7938
8233
|
ctx.logger(SKILL_LINT_LOGGER).error(`skill lint degraded to allow (cannot read ${skillPath}): ${error.message}`);
|
|
7939
8234
|
ctx.emit("mstar/skill-lint", {
|
|
@@ -7946,7 +8241,7 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
|
|
|
7946
8241
|
});
|
|
7947
8242
|
return;
|
|
7948
8243
|
}
|
|
7949
|
-
const result = lintSkillDoc(doc, { skillId:
|
|
8244
|
+
const result = lintSkillDoc(doc, { skillId: basename9(dirname12(skillPath)) });
|
|
7950
8245
|
if (result.ok)
|
|
7951
8246
|
return;
|
|
7952
8247
|
const hard = resolveSeamHard(harnessDir, config);
|
|
@@ -7981,14 +8276,16 @@ async function skillWriteIntentListener(ctx, resolver, config, target, actor, ne
|
|
|
7981
8276
|
|
|
7982
8277
|
// src/gates/seams.ts
|
|
7983
8278
|
import { existsSync as existsSync19, readFileSync as readFileSync15, readdirSync as readdirSync10 } from "node:fs";
|
|
7984
|
-
import { basename as
|
|
8279
|
+
import { basename as basename10, dirname as dirname13, join as join22, resolve as resolve15, sep as sep8 } from "node:path";
|
|
7985
8280
|
|
|
7986
8281
|
// ../engine/dist/audit.js
|
|
7987
|
-
import { AsyncLocalStorage as
|
|
8282
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
|
|
8283
|
+
import { AsyncLocalStorage as AsyncLocalStorage22 } from "node:async_hooks";
|
|
7988
8284
|
var DATE_PART2 = String.raw`\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])`;
|
|
7989
8285
|
var RFC3339_Z_RE2 = new RegExp(String.raw`^${DATE_PART2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$`);
|
|
7990
8286
|
var DATE_ONLY_RE2 = new RegExp(String.raw`^${DATE_PART2}$`);
|
|
7991
|
-
var heldLockDirs2 = new
|
|
8287
|
+
var heldLockDirs2 = new AsyncLocalStorage3;
|
|
8288
|
+
var writeAuthorizations2 = new AsyncLocalStorage22;
|
|
7992
8289
|
var MSTARC_HARNESS_DIR_KEY2 = "harness_dir";
|
|
7993
8290
|
var MSTARC_PLAN_DIR_KEY2 = "plan_dir";
|
|
7994
8291
|
var MSTARC_SDD_DIR_KEY2 = "sdd_dir";
|
|
@@ -8180,7 +8477,7 @@ var SEAM_LOGGERS = {
|
|
|
8180
8477
|
roles: "mstar/roles-lint"
|
|
8181
8478
|
};
|
|
8182
8479
|
function isIndexFile(path) {
|
|
8183
|
-
const base =
|
|
8480
|
+
const base = basename10(path);
|
|
8184
8481
|
return base === "README.md" || base === "index.md";
|
|
8185
8482
|
}
|
|
8186
8483
|
function isMarkdownDoc(path) {
|
|
@@ -8189,7 +8486,7 @@ function isMarkdownDoc(path) {
|
|
|
8189
8486
|
function isAuditPlanTarget(path) {
|
|
8190
8487
|
if (!isMarkdownDoc(path))
|
|
8191
8488
|
return false;
|
|
8192
|
-
const segments =
|
|
8489
|
+
const segments = resolve15(path).split(sep8);
|
|
8193
8490
|
for (let i = 0;i < segments.length; i++) {
|
|
8194
8491
|
if (segments[i] !== "plans")
|
|
8195
8492
|
continue;
|
|
@@ -8200,7 +8497,7 @@ function isAuditPlanTarget(path) {
|
|
|
8200
8497
|
return false;
|
|
8201
8498
|
}
|
|
8202
8499
|
function isRolesTarget(path) {
|
|
8203
|
-
const segments =
|
|
8500
|
+
const segments = resolve15(path).split(sep8);
|
|
8204
8501
|
const idx = segments.indexOf("mstar-roles");
|
|
8205
8502
|
if (idx === -1)
|
|
8206
8503
|
return false;
|
|
@@ -8210,17 +8507,17 @@ function isRolesTarget(path) {
|
|
|
8210
8507
|
return rest[0] === "SKILL.md" || rest[0] === "references" && rest[rest.length - 1].endsWith(".md");
|
|
8211
8508
|
}
|
|
8212
8509
|
function rolesDirOf(path) {
|
|
8213
|
-
const segments =
|
|
8510
|
+
const segments = resolve15(path).split(sep8);
|
|
8214
8511
|
const idx = segments.indexOf("mstar-roles");
|
|
8215
8512
|
if (idx === -1)
|
|
8216
|
-
return
|
|
8217
|
-
return idx === 0 ?
|
|
8513
|
+
return dirname13(path);
|
|
8514
|
+
return idx === 0 ? sep8 : join22(sep8, ...segments.slice(1, idx + 1));
|
|
8218
8515
|
}
|
|
8219
8516
|
function isSeamTarget(seam, harnessDir, target) {
|
|
8220
|
-
const path =
|
|
8517
|
+
const path = resolve15(target.displayPath);
|
|
8221
8518
|
switch (seam) {
|
|
8222
8519
|
case "design-md": {
|
|
8223
|
-
const base =
|
|
8520
|
+
const base = basename10(path);
|
|
8224
8521
|
return base === "DESIGN.md" || base === "DESIGN.dark.md";
|
|
8225
8522
|
}
|
|
8226
8523
|
case "audit":
|
|
@@ -8228,7 +8525,7 @@ function isSeamTarget(seam, harnessDir, target) {
|
|
|
8228
8525
|
case "compound": {
|
|
8229
8526
|
if (harnessDir === null)
|
|
8230
8527
|
return false;
|
|
8231
|
-
return path.startsWith(
|
|
8528
|
+
return path.startsWith(resolve15(join22(harnessDir, "knowledge")) + sep8) && isMarkdownDoc(path);
|
|
8232
8529
|
}
|
|
8233
8530
|
case "roles":
|
|
8234
8531
|
return isRolesTarget(path);
|
|
@@ -8245,8 +8542,8 @@ function auditSecretsViolations(findings, file) {
|
|
|
8245
8542
|
}
|
|
8246
8543
|
function validateDesignDoc(doc, path) {
|
|
8247
8544
|
const violations = [...validateDesignTokenFrontmatter(doc).violations];
|
|
8248
|
-
const isDark =
|
|
8249
|
-
const sibling =
|
|
8545
|
+
const isDark = basename10(path) === "DESIGN.dark.md";
|
|
8546
|
+
const sibling = join22(dirname13(path), isDark ? "DESIGN.md" : "DESIGN.dark.md");
|
|
8250
8547
|
if (existsSync19(sibling)) {
|
|
8251
8548
|
const light = isDark ? readFileSync15(sibling, "utf8") : doc;
|
|
8252
8549
|
const dark = isDark ? doc : readFileSync15(sibling, "utf8");
|
|
@@ -8263,17 +8560,17 @@ function validateAuditDoc(doc, path) {
|
|
|
8263
8560
|
function validateCompoundDoc(doc, _path, harnessDir) {
|
|
8264
8561
|
const violations = [...validateSchemaYaml(doc).violations];
|
|
8265
8562
|
if (harnessDir !== null) {
|
|
8266
|
-
violations.push(...referenceExists(
|
|
8563
|
+
violations.push(...referenceExists(dirname13(harnessDir), doc).violations);
|
|
8267
8564
|
}
|
|
8268
8565
|
return violations.length === 0 ? { ok: true, violations } : { ok: false, violations };
|
|
8269
8566
|
}
|
|
8270
|
-
function validateRolesState(rolesDir, skillsRoot =
|
|
8567
|
+
function validateRolesState(rolesDir, skillsRoot = dirname13(rolesDir)) {
|
|
8271
8568
|
const violations = [...validateRoleMapping(rolesDir).violations];
|
|
8272
8569
|
const skillTexts = {};
|
|
8273
8570
|
for (const entry of readdirSync10(skillsRoot, { withFileTypes: true })) {
|
|
8274
8571
|
if (!entry.isDirectory() || !entry.name.startsWith("mstar-"))
|
|
8275
8572
|
continue;
|
|
8276
|
-
const skillFile =
|
|
8573
|
+
const skillFile = join22(skillsRoot, entry.name, "SKILL.md");
|
|
8277
8574
|
if (!existsSync19(skillFile))
|
|
8278
8575
|
continue;
|
|
8279
8576
|
try {
|
|
@@ -8307,7 +8604,7 @@ function gateSeamIntent(ctx, harnessDir, config, seam, target) {
|
|
|
8307
8604
|
try {
|
|
8308
8605
|
if (!isSeamTarget(seam, harnessDir, target))
|
|
8309
8606
|
return;
|
|
8310
|
-
const path =
|
|
8607
|
+
const path = resolve15(target.displayPath);
|
|
8311
8608
|
if (!existsSync19(path))
|
|
8312
8609
|
return;
|
|
8313
8610
|
let doc;
|
|
@@ -8324,12 +8621,12 @@ function gateSeamIntent(ctx, harnessDir, config, seam, target) {
|
|
|
8324
8621
|
const hard = resolveSeamHard(harnessDir, config);
|
|
8325
8622
|
const verdict = applyEnforcement(result, { hard });
|
|
8326
8623
|
if (verdict.hardBlocked) {
|
|
8327
|
-
logger.error(`${
|
|
8624
|
+
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
8625
|
${verdict.violations.map(formatViolation).join(`
|
|
8329
8626
|
`)}`);
|
|
8330
8627
|
emitSeamAdvisory(ctx, seam, target.displayPath, verdict, hard, { repair: true });
|
|
8331
8628
|
} else {
|
|
8332
|
-
logger.warn(`${
|
|
8629
|
+
logger.warn(`${basename10(path)} write to ${target.displayPath} (advisory):
|
|
8333
8630
|
${verdict.violations.map(formatViolation).join(`
|
|
8334
8631
|
`)}`);
|
|
8335
8632
|
emitSeamAdvisory(ctx, seam, target.displayPath, verdict, hard);
|
|
@@ -8379,7 +8676,7 @@ function lintRolesWrite(doc, options) {
|
|
|
8379
8676
|
|
|
8380
8677
|
// src/gates/tools.ts
|
|
8381
8678
|
import { existsSync as existsSync20, readFileSync as readFileSync16 } from "node:fs";
|
|
8382
|
-
import { join as
|
|
8679
|
+
import { join as join23, resolve as resolve17 } from "node:path";
|
|
8383
8680
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
8384
8681
|
var ITERATION_VIOLATION_SCHEMA = {
|
|
8385
8682
|
type: "object",
|
|
@@ -8599,8 +8896,8 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8599
8896
|
presentCall: (args) => ({ card: "generic", title: "Validate DESIGN.md", kind: "other", rawInput: args.dir }),
|
|
8600
8897
|
isConcurrencySafe: () => false,
|
|
8601
8898
|
async execute(args) {
|
|
8602
|
-
const abs =
|
|
8603
|
-
const lightPath =
|
|
8899
|
+
const abs = resolve17(args.dir);
|
|
8900
|
+
const lightPath = join23(abs, "DESIGN.md");
|
|
8604
8901
|
if (!existsSync20(lightPath))
|
|
8605
8902
|
throw new Error(`design file not found: ${lightPath}`);
|
|
8606
8903
|
const light = readFileSync16(lightPath, "utf8");
|
|
@@ -8653,7 +8950,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8653
8950
|
presentCall: (args) => ({ card: "generic", title: "Validate audit plan", kind: "other", rawInput: args.plan_path }),
|
|
8654
8951
|
isConcurrencySafe: () => false,
|
|
8655
8952
|
async execute(args) {
|
|
8656
|
-
const abs =
|
|
8953
|
+
const abs = resolve17(args.plan_path);
|
|
8657
8954
|
if (!existsSync20(abs))
|
|
8658
8955
|
throw new Error(`plan file not found: ${abs}`);
|
|
8659
8956
|
const text = readFileSync16(abs, "utf8");
|
|
@@ -8693,17 +8990,17 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8693
8990
|
presentCall: (args) => ({ card: "generic", title: "Validate knowledge doc", kind: "other", rawInput: args.doc_path }),
|
|
8694
8991
|
isConcurrencySafe: () => false,
|
|
8695
8992
|
async execute(args, exec) {
|
|
8696
|
-
const abs =
|
|
8993
|
+
const abs = resolve17(args.doc_path);
|
|
8697
8994
|
if (!existsSync20(abs))
|
|
8698
8995
|
throw new Error(`knowledge doc not found: ${abs}`);
|
|
8699
8996
|
const text = readFileSync16(abs, "utf8");
|
|
8700
8997
|
const base = validateCompoundDoc(text, abs, args.repo_root !== undefined ? null : resolver.forAgent(exec.agent));
|
|
8701
8998
|
const violations = [...base.violations];
|
|
8702
8999
|
if (args.repo_root !== undefined) {
|
|
8703
|
-
violations.push(...referenceExists(
|
|
9000
|
+
violations.push(...referenceExists(resolve17(args.repo_root), text).violations);
|
|
8704
9001
|
}
|
|
8705
9002
|
if (args.knowledge_dir !== undefined) {
|
|
8706
|
-
const knowledgeDir =
|
|
9003
|
+
const knowledgeDir = resolve17(args.knowledge_dir);
|
|
8707
9004
|
violations.push(...assertIndexRows(knowledgeDir).violations);
|
|
8708
9005
|
violations.push(...scopeGuard(abs, [knowledgeDir]).violations);
|
|
8709
9006
|
}
|
|
@@ -8738,10 +9035,10 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8738
9035
|
presentCall: (args) => ({ card: "generic", title: "Validate roles mapping", kind: "other", rawInput: args.roles_dir }),
|
|
8739
9036
|
isConcurrencySafe: () => false,
|
|
8740
9037
|
async execute(args) {
|
|
8741
|
-
const rolesDir =
|
|
8742
|
-
if (!existsSync20(
|
|
9038
|
+
const rolesDir = resolve17(args.roles_dir);
|
|
9039
|
+
if (!existsSync20(join23(rolesDir, "SKILL.md")))
|
|
8743
9040
|
throw new Error(`roles dir not found: ${rolesDir}`);
|
|
8744
|
-
const result = validateRolesState(rolesDir, args.skills_root !== undefined ?
|
|
9041
|
+
const result = validateRolesState(rolesDir, args.skills_root !== undefined ? resolve17(args.skills_root) : undefined);
|
|
8745
9042
|
return { ok: result.ok, violations: result.violations.map(iterationViolationView) };
|
|
8746
9043
|
}
|
|
8747
9044
|
}));
|
|
@@ -8750,7 +9047,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
8750
9047
|
|
|
8751
9048
|
// src/gates/adapter.ts
|
|
8752
9049
|
import { existsSync as existsSync21 } from "node:fs";
|
|
8753
|
-
import { join as
|
|
9050
|
+
import { join as join24 } from "node:path";
|
|
8754
9051
|
import { Service as Service3 } from "@deepseek-ai/cordis";
|
|
8755
9052
|
var HOST_LOGGER = "mstar/host-adapter";
|
|
8756
9053
|
function mergeLeasesMatch(left, right) {
|
|
@@ -8865,7 +9162,7 @@ class DshHostAdapter extends Service3 {
|
|
|
8865
9162
|
if (harnessDir !== null) {
|
|
8866
9163
|
const selection = resolveActiveWorkflow(harnessDir);
|
|
8867
9164
|
if (selection.kind === "active") {
|
|
8868
|
-
const snapshotPath =
|
|
9165
|
+
const snapshotPath = join24(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
|
|
8869
9166
|
try {
|
|
8870
9167
|
const snapshot = readJson(snapshotPath);
|
|
8871
9168
|
const stored = snapshot?.integration_merge_lease;
|
|
@@ -8892,7 +9189,7 @@ class DshHostAdapter extends Service3 {
|
|
|
8892
9189
|
|
|
8893
9190
|
// src/gates/agent-personas.ts
|
|
8894
9191
|
import { readFileSync as readFileSync17, readdirSync as readdirSync12, statSync as statSync8 } from "node:fs";
|
|
8895
|
-
import { join as
|
|
9192
|
+
import { join as join25, resolve as resolve18, sep as sep9 } from "node:path";
|
|
8896
9193
|
var ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
|
|
8897
9194
|
var defaultCache = new Map;
|
|
8898
9195
|
function personaFor(roleId, lookup, warn) {
|
|
@@ -8920,7 +9217,7 @@ function subagentRoleIds(agentsDir) {
|
|
|
8920
9217
|
const roleId = name.slice(0, -3);
|
|
8921
9218
|
let content;
|
|
8922
9219
|
try {
|
|
8923
|
-
content = readFileSync17(
|
|
9220
|
+
content = readFileSync17(join25(agentsDir, name), "utf8");
|
|
8924
9221
|
} catch {
|
|
8925
9222
|
continue;
|
|
8926
9223
|
}
|
|
@@ -8933,9 +9230,9 @@ function subagentRoleIds(agentsDir) {
|
|
|
8933
9230
|
function defaultFromMirror(agentsDir, roleId, warn) {
|
|
8934
9231
|
if (!ROLE_ID_PATTERN.test(roleId))
|
|
8935
9232
|
return;
|
|
8936
|
-
const root =
|
|
8937
|
-
const file =
|
|
8938
|
-
if (!file.startsWith(root +
|
|
9233
|
+
const root = resolve18(agentsDir);
|
|
9234
|
+
const file = resolve18(agentsDir, `${roleId}.md`);
|
|
9235
|
+
if (!file.startsWith(root + sep9))
|
|
8939
9236
|
return;
|
|
8940
9237
|
let stat;
|
|
8941
9238
|
try {
|
|
@@ -9182,7 +9479,7 @@ function log2(level, message) {
|
|
|
9182
9479
|
|
|
9183
9480
|
// src/gates/workflow-ledger.ts
|
|
9184
9481
|
import { readFileSync as readFileSync18, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
9185
|
-
import { join as
|
|
9482
|
+
import { join as join26 } from "node:path";
|
|
9186
9483
|
var WORKFLOW_LEDGER_LOGGER = "mstar/workflow-ledger";
|
|
9187
9484
|
var TOOL_WORKFLOW_RUN_START = "tool-workflow/run-start";
|
|
9188
9485
|
var TOOL_WORKFLOW_AGENT_START = "tool-workflow/agent-start";
|
|
@@ -9242,7 +9539,7 @@ function loadWatermark(workflowDir, fresh = false) {
|
|
|
9242
9539
|
return cached;
|
|
9243
9540
|
const watermark = new Map;
|
|
9244
9541
|
try {
|
|
9245
|
-
const raw = readFileSync18(
|
|
9542
|
+
const raw = readFileSync18(join26(workflowDir, WORKFLOW_LEDGER_WATERMARK_FILE), "utf8");
|
|
9246
9543
|
const record = asRecord(JSON.parse(raw));
|
|
9247
9544
|
const cursors = asRecord(record?.cursors);
|
|
9248
9545
|
if (cursors !== undefined) {
|
|
@@ -9265,7 +9562,7 @@ function loadWatermark(workflowDir, fresh = false) {
|
|
|
9265
9562
|
}
|
|
9266
9563
|
function saveWatermark(workflowDir, watermark) {
|
|
9267
9564
|
try {
|
|
9268
|
-
const file =
|
|
9565
|
+
const file = join26(workflowDir, WORKFLOW_LEDGER_WATERMARK_FILE);
|
|
9269
9566
|
const tmp = `${file}.tmp`;
|
|
9270
9567
|
writeFileSync7(tmp, JSON.stringify({ v: 1, cursors: Object.fromEntries(watermark) }));
|
|
9271
9568
|
renameSync4(tmp, file);
|
|
@@ -9585,7 +9882,7 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
|
|
|
9585
9882
|
|
|
9586
9883
|
// src/gates/goal-bridge.ts
|
|
9587
9884
|
import { existsSync as existsSync22, readdirSync as readdirSync13 } from "node:fs";
|
|
9588
|
-
import { join as
|
|
9885
|
+
import { join as join27 } from "node:path";
|
|
9589
9886
|
var GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
|
|
9590
9887
|
var GOAL_CHANGE_EVENT_TYPE = "goal/change";
|
|
9591
9888
|
var GOAL_CHANGE_VERSION = 1;
|
|
@@ -9610,12 +9907,12 @@ function projectRegisterPointer(harnessDir) {
|
|
|
9610
9907
|
for (const entry of readdirSync13(projectsDir, { withFileTypes: true })) {
|
|
9611
9908
|
if (!entry.isDirectory())
|
|
9612
9909
|
continue;
|
|
9613
|
-
const registerPath =
|
|
9910
|
+
const registerPath = join27(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
9614
9911
|
if (existsSync22(registerPath))
|
|
9615
9912
|
return registerPath;
|
|
9616
9913
|
}
|
|
9617
9914
|
} catch {}
|
|
9618
|
-
return
|
|
9915
|
+
return join27(projectsDir, _DEFAULT_PROJECT, PROJECT_REGISTER_FILE);
|
|
9619
9916
|
}
|
|
9620
9917
|
function blockedAdvisoryOf(envelope) {
|
|
9621
9918
|
const record = asRecord(envelope);
|
|
@@ -9667,7 +9964,7 @@ function registerGoalBridge(ctx, resolver) {
|
|
|
9667
9964
|
|
|
9668
9965
|
// src/gates/plan-mode-bridge.ts
|
|
9669
9966
|
import { existsSync as existsSync23 } from "node:fs";
|
|
9670
|
-
import { isAbsolute as
|
|
9967
|
+
import { isAbsolute as isAbsolute12, join as join28, relative as relative10, sep as sep10 } from "node:path";
|
|
9671
9968
|
|
|
9672
9969
|
// src/gates/steering.ts
|
|
9673
9970
|
function isRootLikeAgent(agent) {
|
|
@@ -9711,7 +10008,7 @@ function errorMessage5(error) {
|
|
|
9711
10008
|
return error instanceof Error ? error.message : String(error);
|
|
9712
10009
|
}
|
|
9713
10010
|
function selectedLifecycle(harnessDir, hint) {
|
|
9714
|
-
if (!existsSync23(
|
|
10011
|
+
if (!existsSync23(join28(harnessDir, STATUS_FILE)))
|
|
9715
10012
|
return { kind: "none" };
|
|
9716
10013
|
const selection = resolveReadWorkflow(harnessDir, hint);
|
|
9717
10014
|
if (selection.kind === "error") {
|
|
@@ -9719,7 +10016,7 @@ function selectedLifecycle(harnessDir, hint) {
|
|
|
9719
10016
|
}
|
|
9720
10017
|
let doc;
|
|
9721
10018
|
try {
|
|
9722
|
-
doc = readJson(
|
|
10019
|
+
doc = readJson(join28(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE));
|
|
9723
10020
|
} catch {
|
|
9724
10021
|
return { kind: "none" };
|
|
9725
10022
|
}
|
|
@@ -9729,11 +10026,11 @@ function selectedLifecycle(harnessDir, hint) {
|
|
|
9729
10026
|
}
|
|
9730
10027
|
function selectedCompassSteers(harnessDir, lifecycle) {
|
|
9731
10028
|
const ref = lifecycle.doc.compass_ref;
|
|
9732
|
-
if (typeof ref !== "string" || ref.trim() === "" ||
|
|
10029
|
+
if (typeof ref !== "string" || ref.trim() === "" || isAbsolute12(ref))
|
|
9733
10030
|
return false;
|
|
9734
|
-
const compassPath =
|
|
10031
|
+
const compassPath = join28(harnessDir, ref);
|
|
9735
10032
|
const inside = relative10(harnessDir, compassPath);
|
|
9736
|
-
if (inside === "" || inside === ".." || inside.startsWith(`..${
|
|
10033
|
+
if (inside === "" || inside === ".." || inside.startsWith(`..${sep10}`) || isAbsolute12(inside))
|
|
9737
10034
|
return false;
|
|
9738
10035
|
if (!existsSync23(compassPath))
|
|
9739
10036
|
return false;
|
|
@@ -10304,7 +10601,7 @@ function registerMstarCommands(ctx) {
|
|
|
10304
10601
|
for (const file of readdirSync14(dir).sort()) {
|
|
10305
10602
|
if (!file.endsWith(".md"))
|
|
10306
10603
|
continue;
|
|
10307
|
-
const parsed = parseCommandMarkdown(readFileSync19(
|
|
10604
|
+
const parsed = parseCommandMarkdown(readFileSync19(join29(dir, file), "utf8"));
|
|
10308
10605
|
if (parsed === undefined)
|
|
10309
10606
|
continue;
|
|
10310
10607
|
commandsCtx.commands.register({
|