@mstar-harness/engine 2.4.1 → 3.0.1

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/host.d.ts CHANGED
@@ -9,10 +9,10 @@
9
9
  * cursor → opencode → omp → kimi → zcode → codex ("Order matters").
10
10
  * - `mstar-host` SKILL.md § Resolve loaded skill root — per-host skill-root
11
11
  * resolution.
12
- * - `.harness/references/skill-programmatic-roadmap.md` §8.4 — the
12
+ * - roadmap §8.4 — the
13
13
  * `HostAdapter` shared contract (all hooks optional; no concrete adapters
14
14
  * in the engine; pi deferred).
15
- * - `.harness/references/dsh-adapter-roadmap.md` §4 D5 (this iteration) —
15
+ * - dsh-adapter roadmap §4 D5 (this iteration) —
16
16
  * the dsh detection row + skill-root form; mirror text lands in the
17
17
  * mstar-host skill when this module is upstreamed.
18
18
  *
package/dist/index.d.ts CHANGED
@@ -23,11 +23,15 @@
23
23
  export type { GateResult, Severity, ValidationResult } from "./core.js";
24
24
  export { SEVERITY_ORDER, applyEnforcement, readHarnessVersion, readJson, resolveProjectRoot, writeJson } from "./core.js";
25
25
  export type { HarnessKind, ResolveHarnessDirOptions, ResolveSpecsDirOptions } from "./path.js";
26
- export { assertPlanWritingPath, emitGitignoreSnippet, resolveHarnessDir, resolveIterationDir, resolvePlanDir, resolveSddDir, resolveSpecsDir, scaffoldHarness, validateGitignore, } from "./path.js";
27
- export type { ArchiveResult, FindingsCleanupMode, PlanRow, ResidualEntry, StatusDoc, TechDebtCheck, TechDebtRollup, TechDebtSummary, } from "./status.js";
28
- export { archiveResiduals, findingsCleanupGate, normalizeSeverity, resolveCompassEnforcement, techDebtRollup, validatePlanRow, validateResidual, validateStatus, } from "./status.js";
26
+ export type { MstarcConfig } from "./mstarc.js";
27
+ export { MSTARC_FILE, MSTARC_HARNESS_DIR_KEY, MSTARC_PROJECT_DIR_KEY, MSTARC_SECTION, MSTARC_WORKFLOW_DIR_KEY, findMstarc, parseMstarc, } from "./mstarc.js";
28
+ export { assertPlanWritingPath, emitGitignoreSnippet, resolveHarnessDir, resolveIterationDir, resolveKnowledgeDir, resolvePlanDir, resolveProjectDir, resolveSddDir, resolveSpecsDir, resolveWorkflowDir, scaffoldHarness, validateGitignore, } from "./path.js";
29
+ export type { PlanRow, ResidualEntry, StatusDoc, StatusV2Doc, WorkflowEntry, } from "./status.js";
30
+ export { normalizeSeverity, registerWorkflow, resolveCompassEnforcement, resolveMstarcEnforcement, resolveRepoEnforcement, unregisterWorkflow, validatePlanRow, validateResidual, validateStatus, validateStatusV2, validateWorkflowEntry, } from "./status.js";
29
31
  export type { ClaimLeaseFields, ExecutionLease, ExecutionLeaseLocations, IntegrationMergeLease, LeaseTransition, LeaseVerifyResult, } from "./lease.js";
30
32
  export { canSteal, claimLease, planExecutionLeaseLocations, releaseLease, sameHolderResume, validateExecutionLease, validateIntegrationMergeLease, verifyPlanExecutionLease, withStatusWriteLock, } from "./lease.js";
33
+ export type { WorkflowBranchAnchors, WorkflowExecutionPolicy, WorkflowLifecycleStatus, WorkflowLifecycleType, WorkflowSnapshot, } from "./workflow.js";
34
+ export { WORKFLOW_LIFECYCLE_STATUSES, WORKFLOW_LIFECYCLE_TYPES, WORKFLOW_SNAPSHOT_FILE, WORKFLOW_TERMINAL_STATUSES, validateWorkflowSnapshot, writeWorkflowSnapshot, } from "./workflow.js";
31
35
  export type { AssignmentBranchForms, AssignmentFields, ComposeDispatchGateOptions, ComposeDispatchGateResult, DefaultBranchOptions, EnforcementFlag, EnforcementSource, ExecutionModeToNOptions, ExecutionModeToNResult, ValidateAssignmentFieldsOptions, } from "./dispatch.js";
32
36
  export { antiRecursionPrecheck, assertDefaultBranchProtected, assertTriIdentity, assignmentHeaderRegion, composeDispatchGate, executionModeToN, isReadOnlyAssignmentRole, parseAssignmentBranchForms, parseAssignmentFields, parseBranchPolicyDirectOnBranch, parseEnforcementFlag, validateAssignmentFields, } from "./dispatch.js";
33
37
  export type { BranchProbeOptions, L1PreDispatchInput, L2PreDispatchInput, QcAlignmentAssignment, QcSnapshotAssignment, WorktreeTrack, } from "./worktree.js";
@@ -35,7 +39,11 @@ export { assertBranchAlignment, assertControlVsFeaturePath, assertQcAlignment, l
35
39
  export type { ImplementerSessionLedger, ReviewPackageOptions, SddWorkspaceOptions, StickyRulesInput, StickyRulesResult, TaskBriefOptions, } from "./sdd.js";
36
40
  export { SddScriptError, assertBaseSha, implementerSessionStickyRules, readProgressLedger, reviewPackage, sddWorkspace, taskBrief, taskReportExists, } from "./sdd.js";
37
41
  export type { CompassDoc, PhaseGateOptions, PhaseGateResult, PhaseTransition, } from "./iteration.js";
38
- export { assertIndexRowObligations, evaluatePhaseGate, parseCompassFrontmatter, pushCadenceProbe, validateCompassFrontmatter, } from "./iteration.js";
42
+ export { assertIndexRowObligations, evaluatePhaseGate, parseCompassFrontmatter, parseCompassFrontmatterText, pushCadenceProbe, validateCompassFrontmatter, } from "./iteration.js";
43
+ export type { FindingsCleanupMode, ProjectRegisterDoc, ProjectRegisterEntry, RoadmapFrontmatter, RoadmapStatus, RoadmapValidation, TechDebtCheck, TechDebtRollup, TechDebtSummary, } from "./project.js";
44
+ export { PROJECT_REGISTER_FILE, PROJECT_ROADMAP_FILE, ROADMAP_STATUSES, _DEFAULT_PROJECT, findingsCleanupGate, techDebtRollup, validateProjectRegister, validateRoadmap, } from "./project.js";
45
+ export type { MigrateNotesFile, MigrateOptions, MigratePlan, MigrateRegister, MigrateResult, MigrateRoadmap, MigrateRootV2, MigrateSnapshot, MigrateStep, } from "./migrate.js";
46
+ export { ARCHIVED_STATUS_V1_FILE, MIGRATE_STATUS_FILE, NOTES_LEDGER_FILE, applyMigratePlan, migrateHarnessTree, } from "./migrate.js";
39
47
  export type { CompletenessItem, CompletenessLevel, CompletenessPlaceholder, CompletenessResult, DesignFrontmatter, } from "./design-md.js";
40
48
  export { assertLightDarkParity, completenessLevel, parseDesignFrontmatter, validateDesignTokenFrontmatter, } from "./design-md.js";
41
49
  export type { AuditCategory, AuditEffort, AuditFinding, AuditPriority, AuditRisk, RedactResult, ScaffoldAuditPlanOptions, ScaffoldAuditPlanResult, SecretFinding, } from "./audit.js";
@@ -1,5 +1,21 @@
1
1
  import type { GateResult, ValidationResult } from "./core.js";
2
- import { type StatusDoc } from "./status.js";
2
+ /**
3
+ * Loose shape of a parsed workflow snapshot (`workflows/<id>/snapshot.json`).
4
+ * All fields are `unknown` because documents come from JSON at runtime;
5
+ * validators narrow them. `plans[]` rows are the legacy PlanRow shape
6
+ * verbatim (plan Task 2) — `findPlanRow` accepts `id` or `plan_id`.
7
+ *
8
+ * Deliberate decoupling (qc wave-1 S-f): this is a loose LOCAL re-declaration,
9
+ * NOT an import of `WorkflowSnapshot` from workflow.ts. This module only
10
+ * reads `plans[].status`; importing the full schema would add a module edge
11
+ * to workflow.ts (which imports status.ts, which workflow.ts cycles back
12
+ * through — the call-time-safe loop family stays static-edge-free this
13
+ * way). Keep this shape in sync manually when the snapshot schema changes.
14
+ */
15
+ export type SnapshotDoc = {
16
+ plans?: unknown;
17
+ [key: string]: unknown;
18
+ };
3
19
  /**
4
20
  * Loose shape of a parsed delivery-compass.md frontmatter. All fields are
5
21
  * `unknown` because documents come from YAML at runtime; validators narrow
@@ -63,14 +79,20 @@ export type PhaseGateResult = {
63
79
  export declare function validateCompassFrontmatter(doc: unknown): GateResult;
64
80
  /**
65
81
  * Evaluate the Phase transition gates (mstar-iteration Phase transition
66
- * gates table): all compass-registered plans `Done` (per statusDoc plans[]
67
- * status) → Phase 3 required, with the checkable subsets of the §3.1 entry
68
- * and §3.5 exit checklists as missing-item violations.
82
+ * gates table): all compass-registered plans `Done` (per the workflow
83
+ * snapshot plans[] status) → Phase 3 required, with the checkable subsets
84
+ * of the §3.1 entry and §3.5 exit checklists as missing-item violations.
85
+ *
86
+ * v3 relocation: the first doc is the workflow snapshot
87
+ * (`workflows/<id>/snapshot.json`); the compass stays the second input.
88
+ * The §3.1 residual item relocated to the project-layer
89
+ * `findingsCleanupGate(register, planId)` — the snapshot carries no
90
+ * residuals (residual close is a project-register state change).
69
91
  *
70
92
  * Pure function — git probes (`currentBranch`, `specIntegrationBranch`,
71
93
  * `prBaseBranch`) come from the caller via `opts`.
72
94
  */
73
- export declare function evaluatePhaseGate(statusDoc: StatusDoc, compassDoc: CompassDoc, opts?: PhaseGateOptions): PhaseGateResult;
95
+ export declare function evaluatePhaseGate(snapshotDoc: SnapshotDoc, compassDoc: CompassDoc, opts?: PhaseGateOptions): PhaseGateResult;
74
96
  /**
75
97
  * §5.1a push-cadence probe (HARD): never push the PR head while required CI
76
98
  * is still queued/in_progress or an AI/bot review wave is running. Pure
@@ -94,9 +116,18 @@ export declare function assertIndexRowObligations(iterationsDir: string): GateRe
94
116
  * iteration-compass-template.md` Fields guide); `validateCompassFrontmatter`
95
117
  * validates the parsed doc. The engine deliberately has no YAML dependency,
96
118
  * so this hand-rolled flat-subset parser lives here — the single shared
97
- * parser used by the CLI and the omp `mstar_iteration_gate` tool (no fork).
119
+ * parser used by the CLI, the omp `mstar_iteration_gate` tool, and the
120
+ * roadmap validator (no fork). Path wrapper over
121
+ * `parseCompassFrontmatterText`.
98
122
  *
99
123
  * Throws with the file path on structural errors (no fence / unterminated
100
124
  * fence / unsupported line) so callers can fail with a precise message.
101
125
  */
102
126
  export declare function parseCompassFrontmatter(filePath: string): Record<string, unknown>;
127
+ /**
128
+ * Parse flat-subset YAML frontmatter from raw file content — the single
129
+ * shared parser core behind `parseCompassFrontmatter` (path wrapper) and
130
+ * the roadmap validator (plan `20260819-workflow-engine-core.md` Task 4 —
131
+ * extract/reuse the same parsing, no fork).
132
+ */
133
+ export declare function parseCompassFrontmatterText(content: string, filePath: string): Record<string, unknown>;
package/dist/lease.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import type { GateResult, ValidationResult } from "./core.js";
2
2
  import type { PlanRow } from "./status.js";
3
3
  /**
4
- * `plans[].execution_lease` (v1) see spec header. Extra fields (e.g. the
5
- * real control data's `base_sha`) are allowed and preserved.
4
+ * `plans[].execution_lease` on a workflow snapshot's plan row (v3 data
5
+ * home the snapshot's `plans[]` rows are the legacy PlanRow shape
6
+ * verbatim) — see spec header. Extra fields (e.g. the real control data's
7
+ * `base_sha`) are allowed and preserved.
6
8
  */
7
9
  export type ExecutionLease = {
8
10
  holder: string;
@@ -13,7 +15,8 @@ export type ExecutionLease = {
13
15
  [key: string]: unknown;
14
16
  };
15
17
  /**
16
- * Root `metadata.integration_merge_lease` (v1)see spec header. Absent =
18
+ * Snapshot top-level `integration_merge_lease` (v3relocated from the v1
19
+ * root `metadata.integration_merge_lease`) — see spec header. Absent =
17
20
  * unclaimed; writers delete the key on release (never `null`/tombstone).
18
21
  */
19
22
  export type IntegrationMergeLease = {
@@ -52,9 +55,9 @@ export type LeaseTransition = {
52
55
  */
53
56
  export declare function validateExecutionLease(lease: unknown): GateResult;
54
57
  /**
55
- * Validate one root `metadata.integration_merge_lease` object
56
- * (status-and-residuals.md § Root `metadata.integration_merge_lease` (v1)):
57
- * required `holder` / `claimed_at` / `plan_id` / `source_branch` /
58
+ * Validate one snapshot top-level `integration_merge_lease` object
59
+ * (status-and-residuals.md § Snapshot top-level `integration_merge_lease`
60
+ * (v3)): required `holder` / `claimed_at` / `plan_id` / `source_branch` /
58
61
  * `target_branch`; optional `session_label`. Absent = unclaimed; `null` and
59
62
  * tombstone objects are invalid (writers delete the key on release).
60
63
  * Integration merges into `spec_integration_branch` are serial — one holder
@@ -83,7 +86,8 @@ export declare function validateIntegrationMergeLease(lease: unknown): GateResul
83
86
  * (the written lease must itself pass the validator).
84
87
  *
85
88
  * Pure: returns the resulting row; the caller performs the locked
86
- * read-check-replace-verify around `status.json` and persists it.
89
+ * read-check-replace-verify around `workflows/<id>/snapshot.json` (v3 data
90
+ * home) and persists the whole snapshot.
87
91
  */
88
92
  export declare function claimLease(row: PlanRow, holder: string, fields: ClaimLeaseFields): LeaseTransition;
89
93
  /**
@@ -118,46 +122,43 @@ export declare function sameHolderResume(lease: unknown, holder: string): boolea
118
122
  export declare function canSteal(lease: unknown, holder: string, opts?: {
119
123
  userOverride?: boolean;
120
124
  }): boolean;
121
- /** Execution lease locations found on one plan row (SSOT + legacy read-compat). */
125
+ /** Execution lease location found on one plan row (SSOT). */
122
126
  export type ExecutionLeaseLocations = {
123
127
  /** SSOT location: `plans[].execution_lease`. */
124
128
  row: unknown;
125
- /** Legacy/hand-written read-compat location: `plans[].metadata.execution_lease`. */
126
- metadata: unknown;
127
129
  };
128
130
  export declare function planExecutionLeaseLocations(row: Record<string, unknown>): ExecutionLeaseLocations;
129
131
  export type LeaseVerifyResult = {
130
132
  ok: boolean;
131
133
  violations: ValidationResult[];
132
- /** The lease chosen for validation (row-level wins) — absent when neither location has one. */
134
+ /** The lease chosen for validation (row-level) — absent when the row has none. */
133
135
  lease?: unknown;
134
136
  };
135
137
  /**
136
- * Verify a plan's `execution_lease` across its two possible locations
138
+ * Verify a plan's `execution_lease` at its SSOT location
137
139
  * (status-and-residuals.md § `plans[].execution_lease`; ADR
138
140
  * 2026-07-22-iteration-worktree-plan-lease.md A3 — the plan row is the
139
141
  * claim/hold/release SSOT):
140
- * - Row-level `plans[].execution_lease` only, valid → OK.
141
- * - Metadata-only (`plans[].metadata.execution_lease`) → high-severity
142
- * `lease.verify.non-ssot-location`: the metadata location is a
143
- * legacy/hand-written read-compat fallback, NOT equivalent to SSOT
144
- * success. Always a FAIL (non-zero exit) with the lease shape still
145
- * validated and reported.
146
- * - Both locations present → `lease.verify.dual-write`: the row-level lease
147
- * wins and is validated; the metadata copy must be deleted.
142
+ * - Row-level `plans[].execution_lease` present, valid → OK.
148
143
  * - Neither present → `lease.verify.missing` (non-InProgress) /
149
144
  * `lease.verify.orphan` (InProgress).
150
145
  *
146
+ * The v1-era legacy read-compat location (`plans[].metadata.execution_lease`)
147
+ * is deleted in the v3 cutover: the workflow snapshot is machine-written
148
+ * (whole-rewrite), so the hand-written fallback is a v1 dead path — no
149
+ * dual-track.
150
+ *
151
151
  * Kept in the engine so every host hook / CLI entry / Slice-2+ consumer
152
152
  * imports ONE gate (CLI `mstar lease verify` is a thin wrapper).
153
153
  */
154
154
  export declare function verifyPlanExecutionLease(row: Record<string, unknown>, planId: string): LeaseVerifyResult;
155
155
  /**
156
- * Same-host exclusive write lock around `status.json` coordination writes
157
- * (status-and-residuals.md § "Same-host exclusive write lock (control
158
- * status.json)"; phase-2-worktree-lease.md § "Same-host exclusive write
159
- * lock"). Lease mutations and plan-status transitions that touch leases MUST
160
- * run inside this lock for the full read-check-replace-verify sequence.
156
+ * Same-host exclusive write lock around coordination writes — the root
157
+ * `status.json` AND `workflows/<id>/snapshot.json` (status-and-residuals.md
158
+ * § "Same-host exclusive write lock (control status.json)";
159
+ * phase-2-worktree-lease.md § "Same-host exclusive write lock"). Lease
160
+ * mutations and plan-status transitions that touch leases MUST run inside
161
+ * this lock for the full read-check-replace-verify sequence.
161
162
  *
162
163
  * Acquires by atomic `mkdir` on `<status dir>/.status-write.lockdir/`
163
164
  * (success acquires; existing dir → another writer holds the lock). While
package/dist/lint.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Engine lint module — deterministic lint checks ported from skill prose.
3
3
  *
4
- * Roadmap: `.harness/references/skill-programmatic-roadmap.md` §8.2 `lint`
5
- * row + §4.5 Lint layer (`simplify:` / `temporary` marker presence; SDD TDD
4
+ * Roadmap §8.2 `lint` row + §4.5 Lint layer (`simplify:` / `temporary` marker presence; SDD TDD
6
5
  * triple in completion reports; plan-quality-bar checks; skill frontmatter
7
6
  * contract; STRATEGY.md required sections). Skill text stays the semantic
8
7
  * SSOT (roadmap D5) — this module implements the deterministic subset and
@@ -0,0 +1,134 @@
1
+ import { type ProjectRegisterDoc } from "./project.js";
2
+ import { type StatusV2Doc } from "./status.js";
3
+ import { type WorkflowLifecycleStatus, type WorkflowLifecycleType, type WorkflowSnapshot } from "./workflow.js";
4
+ /** Root status file name inside `{HARNESS_DIR}` (v1 input / v2 output). */
5
+ export declare const MIGRATE_STATUS_FILE = "status.json";
6
+ /** Legacy v1 root copy written by migration (never deleted without it). */
7
+ export declare const ARCHIVED_STATUS_V1_FILE = "archived/status.v1.json";
8
+ /** Notes ledger file name inside `workflows/<id>/` (schema v1 event kinds). */
9
+ export declare const NOTES_LEDGER_FILE = "notes.jsonl";
10
+ /** One planned apply step (kind + source -> destination labels for dry-run). */
11
+ export type MigrateStep = {
12
+ kind: "archive-status-v1" | "write-snapshot" | "write-notes" | "write-register" | "write-roadmap" | "replace-root-v2";
13
+ source: string;
14
+ destination: string;
15
+ };
16
+ /** One planned workflow snapshot (id = plan id or iteration id). */
17
+ export type MigrateSnapshot = {
18
+ id: string;
19
+ type: WorkflowLifecycleType;
20
+ status: WorkflowLifecycleStatus;
21
+ /**
22
+ * Canonical (default-layout) harness-relative snapshot path, e.g.
23
+ * `workflows/<id>/snapshot.json`. The actual write target derives from
24
+ * `MigratePlan.workflowDir` (Phase-5 F1 — a `.mstarc` custom
25
+ * `workflow_dir` is honored by the executor); this field keeps the
26
+ * default-layout rel name for display/provenance.
27
+ */
28
+ file: string;
29
+ /** Provenance label (compass file / status.json row). */
30
+ source: string;
31
+ data: WorkflowSnapshot;
32
+ };
33
+ /** One planned notes ledger (`workflows/<id>/notes.jsonl`). */
34
+ export type MigrateNotesFile = {
35
+ /** Canonical (default-layout) rel path; actual target = `plan.workflowDir` + the suffix. */
36
+ file: string;
37
+ source: string;
38
+ /** Serialized JSON lines (each ends with `\n` when joined). */
39
+ lines: string[];
40
+ };
41
+ /** One planned project register document (`projects/<id>/residuals.json`). */
42
+ export type MigrateRegister = {
43
+ /** Canonical (default-layout) rel path; actual target = `plan.projectDir` + the suffix. */
44
+ file: string;
45
+ source: string;
46
+ data: ProjectRegisterDoc;
47
+ };
48
+ /** One planned roadmap seed (`projects/<id>/roadmap.md`). */
49
+ export type MigrateRoadmap = {
50
+ /** Canonical (default-layout) rel path; actual target = `plan.projectDir` + the suffix. */
51
+ file: string;
52
+ source: string;
53
+ content: string;
54
+ };
55
+ /** The root v2 replacement (commit point; empty `workflows[]` until re-registered). */
56
+ export type MigrateRootV2 = {
57
+ file: string;
58
+ data: StatusV2Doc;
59
+ };
60
+ /** Planner options (plan Task 6 — `--dry-run` returns steps, zero writes). */
61
+ export type MigrateOptions = {
62
+ dryRun?: boolean;
63
+ /** Project id for the register/roadmap home (default `_default`). */
64
+ projectId?: string;
65
+ };
66
+ /** Full migration plan: every write is described; the executor applies it. */
67
+ export type MigratePlan = {
68
+ /** Resolved harness dir. */
69
+ root: string;
70
+ /**
71
+ * Resolved `{WORKFLOW_DIR}` (Phase-5 F1): the `.mstarc` `[config]
72
+ * workflow_dir` declaration wins, else `{HARNESS_DIR}/workflows`. The
73
+ * snapshot/notes `file` fields below keep the canonical default-layout
74
+ * rel names for display/provenance; the executor derives the actual
75
+ * write targets from this dir so a custom layout lands where the v3
76
+ * runtime reads.
77
+ */
78
+ workflowDir: string;
79
+ /**
80
+ * Resolved `{PROJECT_DIR}` (Phase-5 F1): the `.mstarc` `[config]
81
+ * project_dir` declaration wins, else `{HARNESS_DIR}/projects`. Same
82
+ * canonical-`file`-vs-actual-target split as `workflowDir` for the
83
+ * register/roadmap writes.
84
+ */
85
+ projectDir: string;
86
+ dryRun: boolean;
87
+ /** Root status.json already at `version: 2` -> nothing to plan/apply. */
88
+ alreadyMigrated: boolean;
89
+ /** Human message (no-op reason when `alreadyMigrated`). */
90
+ message: string;
91
+ snapshots: MigrateSnapshot[];
92
+ notesFiles: MigrateNotesFile[];
93
+ register: MigrateRegister | null;
94
+ roadmap: MigrateRoadmap | null;
95
+ rootV2: MigrateRootV2;
96
+ archive: {
97
+ file: string;
98
+ source: string;
99
+ };
100
+ /** Informational notes surfaced in dry-run output (never silent drops). */
101
+ migrationNotes: string[];
102
+ /** Ordered apply steps (additive-first; root v2 replacement last). */
103
+ steps: MigrateStep[];
104
+ };
105
+ /** Executor result. */
106
+ export type MigrateResult = {
107
+ applied: boolean;
108
+ message: string;
109
+ };
110
+ /**
111
+ * Pure migration planner (plan Task 6): reads the v1 tree under `root` and
112
+ * returns the full v2 migration plan — snapshots, notes ledgers, project
113
+ * register, roadmap seeds, the archived v1 copy and the root v2
114
+ * replacement — with an ordered step list (source -> destination). ZERO
115
+ * writes; the caller applies via `applyMigratePlan`.
116
+ *
117
+ * A v2 root (`status.json` `version === 2`) yields an `alreadyMigrated`
118
+ * plan with no steps (idempotence); `opts.dryRun` marks the plan so apply
119
+ * is a no-op too.
120
+ */
121
+ export declare function migrateHarnessTree(root: string, opts?: MigrateOptions): MigratePlan;
122
+ /**
123
+ * Execute a migration plan (plan Task 6). Additive-first ordering: the v1
124
+ * root is archived, workflow snapshots/notes, the project register and the
125
+ * roadmap are written BEFORE the root v2 replacement — the LAST step, the
126
+ * commit point. A failure before it leaves the v1 tree intact (re-run
127
+ * applies the same deterministic plan). Re-running on a v2 root, or with a
128
+ * `dryRun` plan, is a no-op. Every destination stays inside the harness
129
+ * dir; every snapshot is validated fail-closed inside `writeWorkflowSnapshot`
130
+ * — the writer is the authoritative validator, so the apply loop does not
131
+ * pre-validate (qc wave-1 S-h: a gate here would run the same O(rows) pass
132
+ * twice per snapshot).
133
+ */
134
+ export declare function applyMigratePlan(plan: MigratePlan): Promise<MigrateResult>;
@@ -0,0 +1,72 @@
1
+ /** Canonical config file name. */
2
+ export declare const MSTARC_FILE = ".mstarc";
3
+ /** INI section holding the harness config. */
4
+ export declare const MSTARC_SECTION = "config";
5
+ /** `[config]` key declaring the harness root. */
6
+ export declare const MSTARC_HARNESS_DIR_KEY = "harness_dir";
7
+ /** `[config]` key declaring `{PLAN_DIR}`. */
8
+ export declare const MSTARC_PLAN_DIR_KEY = "plan_dir";
9
+ /** `[config]` key declaring the `{SDD_DIR}` per-plan base. */
10
+ export declare const MSTARC_SDD_DIR_KEY = "sdd_dir";
11
+ /** `[config]` key declaring `{ITERATION_DIR}`. */
12
+ export declare const MSTARC_ITERATION_DIR_KEY = "iteration_dir";
13
+ /** `[config]` key declaring `{KNOWLEDGE_DIR}`. */
14
+ export declare const MSTARC_KNOWLEDGE_DIR_KEY = "knowledge_dir";
15
+ /** `[config]` key declaring `{SPECS_DIR}`. */
16
+ export declare const MSTARC_SPECS_DIR_KEY = "specs_dir";
17
+ /** `[config]` key declaring `{WORKFLOW_DIR}` (v3 workflow lifecycle layout). */
18
+ export declare const MSTARC_WORKFLOW_DIR_KEY = "workflow_dir";
19
+ /** `[config]` key declaring `{PROJECT_DIR}` (v3 project roadmap/register). */
20
+ export declare const MSTARC_PROJECT_DIR_KEY = "project_dir";
21
+ /** `[config]` key declaring the repo hard-gate policy (`hard` / `soft`). */
22
+ export declare const MSTARC_ENFORCEMENT_KEY = "enforcement";
23
+ /** Parsed `.mstarc` harness config (unknown sections/keys are ignored). */
24
+ export type MstarcConfig = {
25
+ /** Declared harness root, relative to the `.mstarc` directory or absolute. */
26
+ harnessDir?: string;
27
+ /** Declared `{PLAN_DIR}`. */
28
+ planDir?: string;
29
+ /** Declared `{SDD_DIR}` base (the per-plan dir joins `<plan-id>`). */
30
+ sddDir?: string;
31
+ /** Declared `{ITERATION_DIR}`. */
32
+ iterationDir?: string;
33
+ /** Declared `{KNOWLEDGE_DIR}`. */
34
+ knowledgeDir?: string;
35
+ /** Declared `{SPECS_DIR}` (authoritative — skips the candidate chain). */
36
+ specsDir?: string;
37
+ /** Declared `{WORKFLOW_DIR}` (v3 workflow snapshots live under it). */
38
+ workflowDir?: string;
39
+ /** Declared `{PROJECT_DIR}` (v3 project roadmap + register live under it). */
40
+ projectDir?: string;
41
+ /** Declared hard-gate policy — `hard` or `soft` (anything else ignored). */
42
+ enforcement?: "hard" | "soft";
43
+ };
44
+ /**
45
+ * Parse `.mstarc` text — minimal INI subset: `#`/`;` comments, `[section]`
46
+ * headers, `key=value` pairs (trimmed). Only the `[config]` section is
47
+ * read; unknown keys are ignored (forward compatibility). The last
48
+ * occurrence of a key wins; an empty value is treated as unset.
49
+ */
50
+ export declare function parseMstarc(text: string): MstarcConfig;
51
+ /**
52
+ * Find the nearest `.mstarc` walking up from `startDir`, never above
53
+ * `boundary` (find-first-stop, mirroring the `{HARNESS_DIR}` probe stop
54
+ * rule — plan-conventions § {HARNESS_DIR} 解析顺序: a config above the
55
+ * workspace root is never adopted, same class as the `~/.mstar`
56
+ * global-collision defect). Returns the absolute file path, or `null`.
57
+ */
58
+ export declare function findMstarc(startDir: string, boundary: string): string | null;
59
+ /** A discovered `.mstarc` plus its parsed config. */
60
+ export type LoadedMstarc = {
61
+ /** Absolute path of the `.mstarc` file. */
62
+ file: string;
63
+ /** Directory containing the `.mstarc` file — base for relative values. */
64
+ dir: string;
65
+ /** Parsed `[config]` contents. */
66
+ config: MstarcConfig;
67
+ };
68
+ /**
69
+ * Find + parse the nearest `.mstarc` at or below `boundary` from
70
+ * `startDir`. Returns `null` when no config exists in scope.
71
+ */
72
+ export declare function loadMstarc(startDir: string, boundary: string): LoadedMstarc | null;
package/dist/path.d.ts CHANGED
@@ -27,12 +27,17 @@ export type ResolveHarnessDirOptions = {
27
27
  };
28
28
  /**
29
29
  * Resolve `{HARNESS_DIR}` per plan-conventions § {HARNESS_DIR} 解析顺序
30
- * (find-first-stop): `.mstar/` → `.agents/` → `.plans/`/`plans/`, walking up
31
- * from `startDir` but NEVER above the workspace root (`opts.workspaceRoot`,
32
- * default = git top-level of `startDir`). Harness candidates are
33
- * dir-existence (the empty-dir rule applies to `{SPECS_DIR}` only). An
34
- * explicit override via `opts.harnessDir` or `MSTAR_HARNESS_DIR` wins over
35
- * probing and short-circuits before any boundary logic.
30
+ * (find-first-stop): `.mstarc` `[config] harness_dir` → `.mstar/` →
31
+ * `.agents/` → `.plans/`/`plans/`, walking up from `startDir` but NEVER
32
+ * above the workspace root (`opts.workspaceRoot`, default = git top-level
33
+ * of `startDir`). The `.mstarc` layer: the nearest config file at or below
34
+ * the boundary declares `harness_dir`, resolved against the config file's
35
+ * own directory — no dir-existence requirement (callers may scaffold) and
36
+ * no boundary check on the result (explicit layers keep authority; only
37
+ * the config discovery walk is bounded). Harness candidates from probing
38
+ * are dir-existence checks (the empty-dir rule applies to `{SPECS_DIR}`
39
+ * only). An explicit override via `opts.harnessDir` or `MSTAR_HARNESS_DIR`
40
+ * wins over both and short-circuits before any boundary logic.
36
41
  *
37
42
  * Returns the absolute harness dir, or `null` when no candidate exists
38
43
  * within the workspace boundary.
@@ -50,21 +55,26 @@ export type ResolveSpecsDirOptions = {
50
55
  create?: boolean;
51
56
  };
52
57
  /**
53
- * Resolve `{SPECS_DIR}` per plan-conventions § {SPECS_DIR} 解析: first
54
- * non-empty candidate wins `{HARNESS_DIR}/specs/` `docs/specs/`
55
- * repo-root `specs/` (repo root = parent of the harness dir), then the
56
- * legacy read-only `designs/` candidates (`{HARNESS_DIR}/designs/`
57
- * repo-root `designs/`, § {SPECS_DIR} 解析 Legacy 兼容读 only, never
58
- * created by init). A candidate that exists but holds no files is treated
59
- * as absent (empty-dir rule, recursive). When all candidates are absent,
60
- * `{HARNESS_DIR}/specs/` is created and returned (unless `create: false`).
58
+ * Resolve `{SPECS_DIR}` per plan-conventions § {SPECS_DIR} 解析: a
59
+ * `.mstarc` `[config] specs_dir` declaration is authoritative returned
60
+ * directly (resolved against the config file's directory; created when
61
+ * `create` is not false; no candidate chain, no empty-dir rule). Otherwise
62
+ * the first non-empty candidate wins`{HARNESS_DIR}/specs/`
63
+ * `docs/specs/` repo-root `specs/` (repo root = parent of the harness
64
+ * dir), then the legacy read-only `designs/` candidates
65
+ * (`{HARNESS_DIR}/designs/` repo-root `designs/`, § {SPECS_DIR} 解析
66
+ * Legacy — 兼容读 only, never created by init). A candidate that exists
67
+ * but holds no files is treated as absent (empty-dir rule, recursive).
68
+ * When all candidates are absent, `{HARNESS_DIR}/specs/` is created and
69
+ * returned (unless `create: false`).
61
70
  */
62
71
  export declare function resolveSpecsDir(harnessDir: string, opts?: ResolveSpecsDirOptions): string;
63
72
  /**
64
73
  * Compose `{PLAN_DIR}` from the harness dir (plan-conventions § 路径符号).
65
- * Legacy layout: when the harness root is a plans dir itself (`.plans/` or
66
- * `plans/`, resolution rung 3), `{HARNESS_DIR}={PLAN_DIR}` the same
67
- * directory is returned.
74
+ * A `.mstarc` `[config] plan_dir` declaration wins (resolved against the
75
+ * config file's directory). Legacy layout: when the harness root is a plans
76
+ * dir itself (`.plans/` or `plans/`, resolution rung 3),
77
+ * `{HARNESS_DIR}={PLAN_DIR}` — the same directory is returned.
68
78
  */
69
79
  export declare function resolvePlanDir(harnessDir: string): string;
70
80
  /**
@@ -77,16 +87,46 @@ export declare function resolvePlanDir(harnessDir: string): string;
77
87
  export declare function assertSafePathComponent(value: string, what: string): void;
78
88
  /**
79
89
  * Compose `{SDD_DIR}` = `{HARNESS_DIR}/sdd/<plan-id>/` (plan-conventions
80
- * § 路径符号). The per-plan directory is created by the sdd workspace flow,
81
- * not here. `planId` must be a single safe path component (traversal
82
- * guard) see `assertSafePathComponent`.
90
+ * § 路径符号). A `.mstarc` `[config] sdd_dir` declaration replaces the
91
+ * `sdd` base (the `<plan-id>` segment is still appended). The per-plan
92
+ * directory is created by the sdd workspace flow, not here. `planId` must
93
+ * be a single safe path component (traversal guard) — see
94
+ * `assertSafePathComponent`.
83
95
  */
84
96
  export declare function resolveSddDir(harnessDir: string, planId: string): string;
85
97
  /**
86
98
  * Compose `{ITERATION_DIR}` = `{HARNESS_DIR}/iterations/` (plan-conventions
87
- * § 路径符号).
99
+ * § 路径符号). A `.mstarc` `[config] iteration_dir` declaration wins
100
+ * (resolved against the config file's directory).
88
101
  */
89
102
  export declare function resolveIterationDir(harnessDir: string): string;
103
+ /**
104
+ * Compose `{KNOWLEDGE_DIR}` = `{HARNESS_DIR}/knowledge/` (plan-conventions
105
+ * § 路径符号). A `.mstarc` `[config] knowledge_dir` declaration wins
106
+ * (resolved against the config file's directory).
107
+ */
108
+ export declare function resolveKnowledgeDir(harnessDir: string): string;
109
+ /**
110
+ * Resolve `{WORKFLOW_DIR}` — default `{HARNESS_DIR}/workflows/`
111
+ * (v3 workflow lifecycle layout: snapshot.json + notes per workflow id).
112
+ * A `.mstarc` `[config] workflow_dir` declaration wins (resolved against
113
+ * the config file's directory). The dir need not exist — writers
114
+ * (`writeWorkflowSnapshot` / register paths) create it on demand.
115
+ *
116
+ * Deferred-by-design (qc1 S-3): the startDir-first signature is asymmetric
117
+ * with the harnessDir-first sibling resolvers — brief-mandated for the CLI
118
+ * consumer (it probes from the cwd). Revisit with a harness-dir-first
119
+ * variant when a third v3 subdir resolver appears.
120
+ */
121
+ export declare function resolveWorkflowDir(startDir?: string, opts?: ResolveHarnessDirOptions): string;
122
+ /**
123
+ * Resolve `{PROJECT_DIR}` — default `{HARNESS_DIR}/projects/` (v3 project
124
+ * layer: roadmap.md + residuals register per project id). A `.mstarc`
125
+ * `[config] project_dir` declaration wins (resolved against the config
126
+ * file's directory). Same deferred-by-design signature asymmetry as
127
+ * `resolveWorkflowDir` (qc1 S-3).
128
+ */
129
+ export declare function resolveProjectDir(startDir?: string, opts?: ResolveHarnessDirOptions): string;
90
130
  /**
91
131
  * Initialize the harness directory under `root`: create `.mstar/` with
92
132
  * `plans/`, `iterations/`, `knowledge/`, `specs/`, `sdd/` and write