@mstar-harness/engine 3.9.2 → 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.d.ts CHANGED
@@ -9,7 +9,8 @@
9
9
  * the execution/merge lease state machines + same-host status write lock,
10
10
  * `dispatch` implements the Assignment field contract, default-branch
11
11
  * gate, QC seat mapping and tri-identity/anti-recursion prechecks, `lint`
12
- * implements marker/TDD-triple/plan-quality/frontmatter/STRATEGY checks,
12
+ * implements marker/TDD-triple/plan-quality/frontmatter/STRATEGY checks and
13
+ * ephemeral/provenance citation discovery,
13
14
  * `design-md` validates DESIGN.md token frontmatter + light/dark parity +
14
15
  * completeness levels, `audit` validates audit Status blocks, redacts
15
16
  * secrets and scaffolds audit-<date>/ plan dirs, and `compound` validates
@@ -70,8 +71,8 @@ export type { AuditCategory, AuditConfidence, AuditEffort, AuditFinding, AuditPr
70
71
  export { AUDIT_CATEGORIES, AUDIT_CONFIDENCES, AUDIT_EFFORTS, AUDIT_PRIORITIES, AUDIT_RISKS, promoteAuditPlans, scaffoldAuditPlan, scanSecrets, supplyChainChecks, validateAuditStatusBlocks, } from "./audit.js";
71
72
  export { KNOWLEDGE_BUG_PROBLEM_TYPES, KNOWLEDGE_CATEGORY_MAP, KNOWLEDGE_KNOWLEDGE_PROBLEM_TYPES, KNOWLEDGE_PROBLEM_TYPES, KNOWLEDGE_REQUIRED_FIELDS, KNOWLEDGE_RESOLUTION_TYPES, KNOWLEDGE_SEVERITIES, assertIndexRows, compoundRefreshScope, referenceExists, scopeGuard, validateSchemaYaml, } from "./compound.js";
72
73
  export type { ReferenceCheckResult } from "./compound.js";
73
- export type { EphemeralCitation, PlanQualityFinding, PlanQualityResult, SimplifyMarker, TemporaryMarker, TemporaryMarkerResult, } from "./lint.js";
74
- export { assertSddTddTriple, findEphemeralCitations, findSimplifyMarkers, findTemporaryMarkers, lintSkillFrontmatter, lintStrategySections, planQualityBar, } from "./lint.js";
74
+ export type { EphemeralCitation, PlanQualityFinding, PlanQualityResult, ProvenanceCitation, SimplifyMarker, TemporaryMarker, TemporaryMarkerResult, } from "./lint.js";
75
+ export { assertSddTddTriple, findEphemeralCitations, findProvenanceCitations, findSimplifyMarkers, findTemporaryMarkers, lintSkillFrontmatter, lintStrategySections, planQualityBar, } from "./lint.js";
75
76
  export type { DevTrackParam, QcReviewerParam, RoleFamily, RoleMappingEntry, RoleMappingOptions, } from "./roles.js";
76
77
  export { DEV_TRACK_PARAMS, QC_REVIEWER_PARAMS, ROLE_MAPPING, SHARED_FAMILIES, lintLoadOrder, validateRoleMapping, } from "./roles.js";
77
78
  export type { DetectResult, HostAdapter, HostId, SkillRootPaths, ToolSignal } from "./host.js";
@@ -86,3 +87,5 @@ export type { ArtifactDoc, ArtifactKind, ArtifactRef, ArtifactStore } from "./st
86
87
  export { assertFsStorePath, createFsStore, getArtifactStore, loadStoreModule, resolveArtifactPath, setArtifactStore } from "./store.js";
87
88
  export { collectActiveLifecycleBranches, scanActiveLifecycleBranches, type ActiveLifecycleScan } from "./lifecycle-branches.js";
88
89
  export { WorkflowSnapshotValidationError } from "./workflow.js";
90
+ export { CoordinationError, bindPlanSession, mutatePlanCoordination, readCoordinatedArtifact, readPlanCoordination, readSessionEnvelope, replaceCoordinatedArtifact, resolvePlanScope, resolveProcessHarnessDir, } from "./coordination.js";
91
+ export type { BindPlanSessionInput, CoordinatedReplacement, CoordinationRequest, CoordinationResult, CoordinationRole, CoordinationSession, HandoffEvidence, PlanCoordinationOperation, PlanCoordinationView, PlanScopeInput, PrepareCoordinationRequest, ProgressCoordinationRequest, ResidualAddCoordinationRequest, ResidualCloseCoordinationRequest, ResidualInput, ResolvedPlanScope, VersionedArtifact, } from "./coordination.js";
@@ -157,7 +157,6 @@ export declare function parseCompassFrontmatter(filePath: string): Record<string
157
157
  /**
158
158
  * Parse flat-subset YAML frontmatter from raw file content — the single
159
159
  * shared parser core behind `parseCompassFrontmatter` (path wrapper) and
160
- * the roadmap validator (plan `20260819-workflow-engine-core.md` Task 4
161
- * extract/reuse the same parsing, no fork).
160
+ * the roadmap validator (extract/reuse the same parsing, no fork).
162
161
  */
163
162
  export declare function parseCompassFrontmatterText(content: string, filePath: string): Record<string, unknown>;
package/dist/lease.d.ts CHANGED
@@ -42,7 +42,7 @@ export type ClaimLeaseFields = {
42
42
  export type LeaseTransition = {
43
43
  ok: boolean;
44
44
  row: PlanRow;
45
- outcome?: "claimed" | "resumed" | "released";
45
+ outcome?: "claimed" | "resumed" | "released" | "transferred";
46
46
  violations: ValidationResult[];
47
47
  };
48
48
  /**
@@ -103,6 +103,18 @@ export declare function claimLease(row: PlanRow, holder: string, fields: ClaimLe
103
103
  * protocol); the caller enforces that ordering around the locked update.
104
104
  */
105
105
  export declare function releaseLease(row: PlanRow, holder: string): LeaseTransition;
106
+ /**
107
+ * Holder transfer for the scoped coordination `accept` transition (spec §D):
108
+ * move an ACTIVE `execution_lease` to another holder verbatim — same
109
+ * `claimed_at`, `worktree_path`, `working_branch`, extra fields preserved;
110
+ * only `holder` changes. Deliberately NOT `claimLease`: there is no
111
+ * Todo/Blocked → InProgress transition and no steal path here. The locked
112
+ * coordination writer has already authenticated the receiving holder as the
113
+ * workflow's coordinator, so `from` must still match the stored holder — a
114
+ * mismatched `from` is `lease.transfer.other-holder` and leaves the row
115
+ * untouched.
116
+ */
117
+ export declare function transferLease(row: PlanRow, from: string, to: string): LeaseTransition;
106
118
  /**
107
119
  * Same-holder resume check (status-and-residuals.md § Claim-before-`InProgress`
108
120
  * #2): `true` iff the stored lease `holder` equals the session `holder`.
package/dist/lint.d.ts CHANGED
@@ -126,8 +126,8 @@ export type EphemeralCitation = {
126
126
  * concrete instance → reported (`task-2-report`, `task-1.diff`).
127
127
  * Placeholders (`task-N-brief`, `task-N-report`, `<plan-id>`,
128
128
  * `{SDD_DIR}/task-N-report.md`) never match.
129
- * - `.mstar/sdd/<segment>` / `.agents/sdd/<segment>` with a concrete first
130
- * segment (`20260815-x`) → reported; `<plan-id>` / `{SDD_DIR}` segments
129
+ * - `.mstar/sdd/<segment>` / `.agents/sdd/<segment>` with a concrete dated
130
+ * first segment → reported; `<plan-id>` / `{SDD_DIR}` segments
131
131
  * are template forms → never match.
132
132
  *
133
133
  * Discovery only — a finder returning an array, same shape as
@@ -137,6 +137,54 @@ export type EphemeralCitation = {
137
137
  * 1-based line order, source order within a line.
138
138
  */
139
139
  export declare function findEphemeralCitations(skillText: string): EphemeralCitation[];
140
+ /**
141
+ * A provenance citation found in text: a dated plan/iteration id token, or a
142
+ * local-harness deeplink whose specificity comes from a dated instance
143
+ * segment. Both disclose local artifact provenance, which tracked code and
144
+ * docs must not carry (repo AGENTS.md "Git and local artifacts": tracked
145
+ * content must not disclose real plan/iteration ids or local deep paths;
146
+ * synthetic example forms and generic layout lines are exempt).
147
+ */
148
+ export type ProvenanceCitation = {
149
+ /** 1-based line number of the citation. */
150
+ line: number;
151
+ /** The matched citation token (dated plan id or deeplink path). */
152
+ match: string;
153
+ /** `plan-id`: a dated-slug plan/iteration id token; `harness-path`: a
154
+ * local-harness deeplink containing a dated token. */
155
+ kind: "plan-id" | "harness-path";
156
+ };
157
+ /**
158
+ * Find provenance citations in text (repo AGENTS.md "Git and local
159
+ * artifacts": tracked code and docs must not depend on local harness
160
+ * artifacts or disclose their provenance with real plan/iteration ids or
161
+ * local deep paths).
162
+ *
163
+ * Discrimination (HARD — zero false positives on the skills corpus):
164
+ * - `plan-id`: a dated-slug token (`20991231-<slug>`; the dated token
165
+ * inside `iter-20990101-<slug>` counts). Synthetic example
166
+ * slugs (`20991231-example-plan`, `20260717-example`) are never reported;
167
+ * a token followed by a `.digits` version segment (`20260908-v3.9.0`) is
168
+ * a version, not a plan id; placeholder shapes (`task-N-*`, `<plan-id>`,
169
+ * `{…}`) never match the digit-anchored form.
170
+ * - `harness-path`: a `.mstar/…` / `.agents/…` deeplink containing a
171
+ * qualifying dated token — the dated instance segment carries the
172
+ * specificity, so generic layout lines (`.mstar/plans/`,
173
+ * `.mstar/status.json`, `.mstar/knowledge/<category>/`) are never
174
+ * reported.
175
+ * - Attribution (single decision, shared with `findEphemeralCitations`):
176
+ * sdd deeplinks (first segment `sdd`, the SDD_DEEPLINK_RE face) belong to
177
+ * the ephemeral check exclusively; a dated token inside any harness path
178
+ * is reported at most once — as `harness-path` for non-sdd paths, never
179
+ * by this finder for sdd paths. Undated concrete path segments are out of
180
+ * scope (shape ambiguity — prose/review territory, like merge SHAs).
181
+ *
182
+ * Discovery only — a finder returning an array, same shape as
183
+ * `findEphemeralCitations`; callers wrap findings into `ViolationResult`s.
184
+ * Citations are reported line by line in 1-based line order, source order
185
+ * within a line.
186
+ */
187
+ export declare function findProvenanceCitations(text: string): ProvenanceCitation[];
140
188
  /**
141
189
  * Validate task report evidence: an explicit scoped-check declaration for
142
190
  * document/policy checks, otherwise the executable-change TDD triple.
package/dist/migrate.d.ts CHANGED
@@ -84,6 +84,14 @@ export type MigratePlan = {
84
84
  */
85
85
  projectDir: string;
86
86
  dryRun: boolean;
87
+ /**
88
+ * Byte version (`sha256:…`, or `"absent"`) of the source `status.json` this
89
+ * plan was derived from (spec §C3 source-version CAS). The executor
90
+ * re-reads the source under the root lock before its first write and
91
+ * refuses a root whose bytes moved after planning — a still-v1 document
92
+ * changed by another writer is never silently replaced.
93
+ */
94
+ sourceVersion: string;
87
95
  /** Root status.json already at `version: 2` -> nothing to plan/apply. */
88
96
  alreadyMigrated: boolean;
89
97
  /** Human message (no-op reason when `alreadyMigrated`). */
package/dist/path.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type ValidationResult } from "./core.js";
1
+ import type { ValidationResult } from "./core.js";
2
2
  /**
3
3
  * Options for `resolveHarnessDir`.
4
4
  */
@@ -11,8 +11,8 @@ export type ResolveHarnessDirOptions = {
11
11
  */
12
12
  harnessDir?: string;
13
13
  /**
14
- * Workspace-root stop boundary (roadmap §7c / plan
15
- * 20260810-harness-root-boundary). The upward probe keeps walking only
14
+ * Workspace-root stop boundary (roadmap §7c). The upward probe keeps
15
+ * walking only
16
16
  * while `dir` is at or below this root — a harness dir above it is never
17
17
  * returned (the `~/.mstar` global-collision defect is the special case).
18
18
  * Resolved against `startDir` when relative. When omitted, the default
@@ -164,11 +164,20 @@ export declare function resolveScaffoldDirs(root: string): {
164
164
  * `.mstarc`-declared `project_dir`) with a valid `roadmap.md` + empty
165
165
  * `residuals.json` (plan-conventions § 初始化 Plan 目录;
166
166
  * mstar-project-governance § `_default` 回退). Idempotent: an existing
167
- * non-empty `status.json`, `roadmap.md`, or `residuals.json` is never
168
- * clobbered; re-running on an initialized tree only creates missing
169
- * pieces. Returns the absolute resolved harness dir.
170
- */
171
- export declare function scaffoldHarness(root: string): string;
167
+ * `roadmap.md` is never clobbered, and re-running on an initialized tree only
168
+ * creates missing pieces. Returns the absolute resolved harness dir.
169
+ *
170
+ * The two coordination documents (`status.json`, `_default/residuals.json`)
171
+ * are written create-only through the active `ArtifactStore` inside the
172
+ * private protected-write context, serialized on the target's
173
+ * `withStatusWriteLock` (spec §C4): a concurrent writer's bytes are never
174
+ * replaced, and an existing empty/malformed document fails validation instead
175
+ * of being silently reinitialized. Callers whose target root differs from the
176
+ * active store's root MUST `setArtifactStore(createFsStore(<harnessRoot>))`
177
+ * first (same contract as the other routed writers). No scoped session or
178
+ * coordination record is created here.
179
+ */
180
+ export declare function scaffoldHarness(root: string): Promise<string>;
172
181
  /**
173
182
  * Harness kind for the gitignore fence — the canonical snippet is per
174
183
  * harness layout (plan-conventions § Git 跟踪策略): `.mstar/` (default) and
package/dist/project.d.ts CHANGED
@@ -199,8 +199,8 @@ export declare function findingsCleanupGate(register: ProjectRegisterDoc, planId
199
199
  */
200
200
  export declare function techDebtRollup(projectDir: string): TechDebtRollup;
201
201
  /**
202
- * List theme-scoped research files under `<projectDir>/references/` (plan
203
- * 20260820-project-research-corpus Task 1 — compass ruling 1): top-level
202
+ * List theme-scoped research files under `<projectDir>/references/`
203
+ * (compass ruling 1): top-level
204
204
  * files plus files exactly one subdirectory deep; deeper nesting ignored;
205
205
  * directories never listed; regular files only (`Dirent.isFile()`). Returns
206
206
  * paths relative to the references root with `/` separators, sorted by code
package/dist/status.d.ts CHANGED
@@ -63,6 +63,18 @@ export type PlanRow = {
63
63
  execution_lease?: unknown;
64
64
  [key: string]: unknown;
65
65
  };
66
+ /**
67
+ * Every plan id a `plans[]` row can be addressed by (status-and-residuals.md
68
+ * § Compatibility: read accepts `id` or the legacy `plan_id`; write prefers
69
+ * `id`). Both non-empty keys are reported, in canonical-first order: a row
70
+ * whose two keys disagree is ambiguous (`status.plan-row.dual-id`), and an
71
+ * ownership guard keyed on one of them would let the other escape protection.
72
+ * Empty strings never count; a non-object or a row carrying neither key has
73
+ * no address.
74
+ */
75
+ export declare function rowPlanIds(row: unknown): string[];
76
+ /** The canonical single plan id of a row — `id`, else legacy `plan_id`. */
77
+ export declare function rowPlanId(row: unknown): string | undefined;
66
78
  /**
67
79
  * Normalize a residual `severity` value for reading/rolling up
68
80
  * (status-and-residuals.md § severity 5 + rollup `norm_sev`):
@@ -156,6 +168,11 @@ export declare const validateStatus: typeof validateStatusV2;
156
168
  * an invalid entry would fail `validateStatusV2` anyway, but the explicit
157
169
  * gate keeps the pre-lock fail-fast contract of `registerWorkflow`).
158
170
  *
171
+ * Replacing an existing entry additionally takes that entry's snapshot lock
172
+ * (spec §C3 root → snapshot order, `withRegisteredSnapshotLock`) and holds it
173
+ * across the root write; callers must therefore hold the root lock only, not
174
+ * a snapshot lock.
175
+ *
159
176
  * Async-only (architect-locked 2026-08-27): the durable write goes through
160
177
  * `getArtifactStore().put({ kind: "status", key: "root", ... })` inside the
161
178
  * caller's lock — the store is the persist backend, never a second lock.
@@ -185,7 +202,10 @@ export declare function registerWorkflow(root: string, entry: WorkflowEntry): Pr
185
202
  * `withStatusWriteLock`, bumping root `updated_at` only when an entry was
186
203
  * actually removed. The final document is validated (removal-at-terminal
187
204
  * invariant included) before the write — a v1 root is refused with the
188
- * `mstar migrate` hint.
205
+ * `mstar migrate` hint. Removing a registered entry additionally takes that
206
+ * entry's snapshot lock (spec §C3 root → snapshot order) and holds it across
207
+ * the root write, so the coordination check cannot be raced by a concurrent
208
+ * bind.
189
209
  *
190
210
  * Fails loud when the active FsStore would resolve its
191
211
  * `status.json` to a path other than the caller's root — the no-op branches
@@ -1,4 +1,5 @@
1
1
  import type { GateResult, ValidationResult } from "./core.js";
2
+ import { type SnapshotCoordination } from "./coordination-write.js";
2
3
  import { type IntegrationMergeLease } from "./lease.js";
3
4
  import { type PlanRow } from "./status.js";
4
5
  /** Snapshot file name inside `workflows/<id>/` ( — writer contract). */
@@ -63,7 +64,17 @@ export type WorkflowSnapshot = {
63
64
  integration_worktree_path?: string;
64
65
  legacy_metadata?: Record<string, unknown>;
65
66
  compass_ref?: string;
67
+ /**
68
+ * Snapshot-level coordination block ( — scoped plan-PM coordination).
69
+ * Present only on a coordinated lifecycle: it carries the workflow's
70
+ * coordinator binding (session id + canonical envelope path). The block
71
+ * is validated strictly (`validateSnapshotCoordination`) and may only be
72
+ * changed by the locked coordination writer.
73
+ */
74
+ coordination?: SnapshotCoordination;
66
75
  };
76
+ /** Stable JSON for change detection (sorted keys, recursive). */
77
+ export declare function stableJson(value: unknown): string;
67
78
  /**
68
79
  * Validate a v3 workflow snapshot document ( — final schema):
69
80
  * enum/type/id checks, `schema_version: 1`, required timestamps, `plans[]`
@@ -114,8 +125,34 @@ export declare class WorkflowSnapshotValidationError extends Error {
114
125
  }
115
126
  export declare function readWorkflowSnapshot(dir: string): WorkflowSnapshotRead;
116
127
  /**
117
- * Write a workflow snapshot as a whole-rewrite of `dir/snapshot.json` under
118
- * `withStatusWriteLock(snapshotPath)` ( the `.status-write.lockdir`
128
+ * Writer contract (spec §C4). `expectedVersion` is the CAS token — the exact
129
+ * on-disk artifact version (`sha256:<64 hex>`), or `"absent"` for
130
+ * create-only. `createOnly` is the locked `absent` shorthand used by the
131
+ * scaffold/migrate/audit writers: an existing document (even an empty or
132
+ * malformed one) is never silently replaced.
133
+ *
134
+ * Omitting `expectedVersion` means create-only, NOT "replace whatever is
135
+ * there": there is no missing-version compatibility fallback, so an existing
136
+ * snapshot still refuses with `coordination.expected-version-required`.
137
+ *
138
+ * The delta allowed here is `phase` + `updated_at` and nothing else — a
139
+ * coordinator persists its phase projection through this writer without ever
140
+ * gaining a backdoor to row owners, leases, lifecycle scalars or branch
141
+ * anchors. Lifecycle terminal changes belong to `closeWorkflow` (spec §C4).
142
+ */
143
+ export type WriteWorkflowSnapshotOptions = {
144
+ expectedVersion?: string;
145
+ createOnly?: boolean;
146
+ /**
147
+ * Canonical coordinator session envelope path (spec §C4). Required when the
148
+ * stored snapshot is coordinated: only the snapshot's own bound coordinator
149
+ * may pass, and the `coordination` block itself is never part of the delta.
150
+ */
151
+ sessionPath?: string;
152
+ };
153
+ /**
154
+ * Write a workflow snapshot as a field-scoped update of `dir/snapshot.json`
155
+ * under `withStatusWriteLock(snapshotPath)` ( — the `.status-write.lockdir`
119
156
  * lands inside `workflows/<id>/`, dirname of the snapshot; no harness-root
120
157
  * pollution). The snapshot is validated first — an invalid snapshot throws
121
158
  * and nothing is written. `dir` is created recursively. The durable write
@@ -128,14 +165,26 @@ export declare function readWorkflowSnapshot(dir: string): WorkflowSnapshotRead;
128
165
  * whose target root differs from the active store's root MUST
129
166
  * `setArtifactStore(createFsStore(root))` first.
130
167
  */
131
- export declare function writeWorkflowSnapshot(snapshot: WorkflowSnapshot, dir: string): Promise<void>;
168
+ export declare function writeWorkflowSnapshot(snapshot: WorkflowSnapshot, dir: string, opts?: WriteWorkflowSnapshotOptions): Promise<void>;
132
169
  /** Terminal enum predicate only; callers validate document shape separately. */
133
170
  export declare function isTerminalSnapshot(doc: WorkflowSnapshot): boolean;
134
171
  export type CloseWorkflowOptions = {
135
172
  endedAt: string;
173
+ /**
174
+ * Canonical coordinator session envelope path (spec §C4). Required when the
175
+ * stored snapshot is coordinated: the close writes the snapshot, so only the
176
+ * snapshot's own bound coordinator may pass. A missing/mismatched envelope
177
+ * refuses the close with `coordination.session-mismatch` before anything is
178
+ * written. Non-coordinated snapshots ignore it; an already-terminal snapshot
179
+ * is returned unchanged (no write, no authorization needed).
180
+ */
181
+ sessionPath?: string;
136
182
  };
137
183
  /**
138
184
  * Complete the latest snapshot under its write lock. Never releases leases.
139
185
  * A valid terminal snapshot is returned unchanged, including failed/stopped.
186
+ * A coordinated snapshot is closed only by its own bound coordinator
187
+ * (spec §C4) — the same envelope seam as `writeWorkflowSnapshot` — so a plan
188
+ * actor or a bare CLI call can never complete a lifecycle it does not own.
140
189
  */
141
190
  export declare function closeWorkflow(workflowId: string, dir: string, opts: CloseWorkflowOptions): Promise<WorkflowSnapshot>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/engine",
3
- "version": "3.9.2",
3
+ "version": "3.9.4",
4
4
  "description": "Morning Star Harness Workflow Engine — deterministic workflow enforcement library (path, status, lease, dispatch, sdd, iteration, lint gates).",
5
5
  "license": "MIT",
6
6
  "repository": {