@mstar-harness/engine 3.4.1 → 3.5.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/index.d.ts CHANGED
@@ -61,5 +61,7 @@ export type { DetectResult, HostAdapter, HostId, SkillRootPaths, ToolSignal } fr
61
61
  export { detectHost, resolveSkillRoot } from "./host.js";
62
62
  export type { FiveQuestionMode, FiveQuestionSection } from "./skill-authoring.js";
63
63
  export { FIVE_QUESTION_SECTIONS, RUNTIME_HEADING_ALIASES, lintFiveQuestion, lintFrontmatter, resolveAssetPath, stripFrontmatter, } from "./skill-authoring.js";
64
- export type { MergeClass, PrReportTarget, PrReviewSeatPromptOptions, PrReviewSizing, PrReviewTier, PrTierKeyword, PrSizeBand, PrTallyInput, PrTallyResult, PrVerdict, ResolvePrReviewTierInput, ReviewChangesetMode, ReviewInlineComment, ReviewPostPlan, ValidateFindingDocOptions, } from "./prreview.js";
65
- export { MERGE_CLASSES, PR_VERDICTS, REVIEW_EMOJI, computePrTally, pickReviewBranchName, planReviewPost, preflightChangeset, prReviewReportPath, prReviewSeatPrompt, prReviewSizing, resolvePrReviewTier, validateFindingDoc, validatePrReviewReport, } from "./prreview.js";
64
+ export type { MergeClass, MstarReviewFinding, MstarReviewV1, PrReportTarget, PrReviewSeatPromptOptions, PrReviewSizing, PrReviewTier, PrTierKeyword, PrSizeBand, PrTallyInput, PrTallyResult, PrVerdict, ResolvePrReviewTierInput, ReviewChangesetMode, ReviewInlineComment, ReviewPostPlan, ValidateFindingDocOptions, } from "./prreview.js";
65
+ export { MERGE_CLASSES, PR_VERDICTS, REVIEW_EMOJI, computePrTally, pickReviewBranchName, planReviewPost, preflightChangeset, prReviewReportPath, prReviewSeatPrompt, prReviewSizing, resolvePrReviewTier, synthesizeReview, validateFindingDoc, validateMstarReviewV1, validatePrReviewReport, } from "./prreview.js";
66
+ export type { ArtifactDoc, ArtifactKind, ArtifactRef, ArtifactStore } from "./store.js";
67
+ export { assertFsStorePath, createFsStore, getArtifactStore, loadStoreModule, resolveArtifactPath, setArtifactStore } from "./store.js";
package/dist/project.d.ts CHANGED
@@ -138,9 +138,13 @@ export type CloseProjectRegisterEntryOpts = {
138
138
  * duplicate-id detection), set each entry's `source_plan` to the used key
139
139
  * (provenance must match the entries key; the caller cannot know the bumped
140
140
  * key beforehand), append preserving every other key, validate the whole
141
- * register with `validateProjectRegister`, then `writeJson` (atomic
142
- * temp+rename never `open(w)`). Fail-loud: any validation failure throws
143
- * and the register is left untouched. Returns the key actually used.
141
+ * register with `validateProjectRegister`, then `ArtifactStore.put`
142
+ * (FsStore uses `writeJson` — atomic temp+rename; never `open(w)`).
143
+ * Fails loud (qc3 F-201) when the active FsStore would resolve a register
144
+ * path other than `<projectDir>/residuals.json` — callers whose target root
145
+ * differs from the active store's root MUST
146
+ * `setArtifactStore(createFsStore(root))` first. Fail-loud: any validation
147
+ * failure throws and the register is left untouched. Returns the key used.
144
148
  */
145
149
  export declare function appendProjectRegisterEntries(opts: AppendProjectRegisterEntriesOpts): Promise<{
146
150
  ok: true;
@@ -151,8 +155,11 @@ export declare function appendProjectRegisterEntries(opts: AppendProjectRegister
151
155
  * Task 1): under `withStatusWriteLock(registerPath, ...)`, find `entryId` in
152
156
  * `entries[planKey]` (absent → throw), set `lifecycle: resolved` +
153
157
  * `closed_at: <today YYYY-MM-DD>` + `closure_note`, validate the whole
154
- * register with `validateProjectRegister`, then `writeJson` (atomic
155
- * temp+rename). Fail-loud: an invalid register throws and nothing is written.
158
+ * register with `validateProjectRegister`, then `ArtifactStore.put`
159
+ * (FsStore uses `writeJson` atomic temp+rename).
160
+ * Fails loud (qc3 F-201) when the active FsStore would resolve a register
161
+ * path other than `<projectDir>/residuals.json`. Fail-loud: an invalid
162
+ * register throws and nothing is written.
156
163
  */
157
164
  export declare function closeProjectRegisterEntry(opts: CloseProjectRegisterEntryOpts): Promise<{
158
165
  ok: true;
@@ -56,6 +56,71 @@ export type PrTallyResult = {
56
56
  * never overrides the verdict (override invariant).
57
57
  */
58
58
  export declare function computePrTally(input: PrTallyInput): PrTallyResult;
59
+ /**
60
+ * One accepted PR-review finding in the `mstar.review/v1` envelope (SP3 §
61
+ * Schema). `mergeClass` is harness vocab (MERGE_CLASSES); `title`/`body`
62
+ * are non-empty strings; the rest are optional.
63
+ */
64
+ export type MstarReviewFinding = {
65
+ mergeClass: MergeClass;
66
+ category?: string;
67
+ file_path?: string | null;
68
+ line_start?: number | null;
69
+ line_end?: number | null;
70
+ title: string;
71
+ body: string;
72
+ fingerprint_hint?: string;
73
+ };
74
+ /**
75
+ * The `mstar.review/v1` envelope (SP3 § Schema) — a parseable review
76
+ * document with harness vocab, sibling to the Markdown pr-review report.
77
+ * `verdict` is PR_VERDICTS; `tally` (when present) must be a full
78
+ * `PrTallyResult` (shape-checked) whose `verdict` must equal the top-level
79
+ * `verdict` (consistency rule).
80
+ */
81
+ export type MstarReviewV1 = {
82
+ schema: "mstar.review/v1";
83
+ verdict: PrVerdict;
84
+ summary_md: string;
85
+ tally?: PrTallyResult;
86
+ findings: MstarReviewFinding[];
87
+ target?: {
88
+ owner?: string;
89
+ repo?: string;
90
+ pr?: number;
91
+ head_sha?: string;
92
+ };
93
+ };
94
+ /**
95
+ * Validate a `mstar.review/v1` envelope (SP3 § Schema). Fail-loud sibling
96
+ * of {@link validatePrReviewReport} (the Markdown report validator) —
97
+ * shares PR_VERDICTS / MERGE_CLASSES only, never reuses the Markdown
98
+ * parser. Inspector M1 vocab (`comment|request_changes|approve`,
99
+ * `critical|warning|suggestion|info`, or a stray `severity` key) is
100
+ * rejected with `review.inspector-vocab`; a provided `tally` is
101
+ * shape-checked against the `PrTallyResult` {@link computePrTally} produces
102
+ * (verdict vocab, integer `scorePct` in [0, 100], four non-negative-integer
103
+ * counts, string `chatHeader`) and a malformed one is rejected with
104
+ * `review.tally-malformed` (shape only — no arithmetic consistency); a
105
+ * `tally.verdict` disagreeing with the top-level `verdict` is rejected
106
+ * with `review.verdict-tally-mismatch` (consistency rule, architect-locked).
107
+ */
108
+ export declare function validateMstarReviewV1(doc: unknown): GateResult;
109
+ /**
110
+ * Fold already-vetted findings into a complete `mstar.review/v1` envelope
111
+ * (SP3 § synthesizeReview). Pure and synchronous — verdict/tally come ONLY
112
+ * from {@link computePrTally}; no I/O, no GitHub, no store, no seat
113
+ * dispatch. `findings` pass through untouched; `target` is carried when
114
+ * provided. When `summary_md` is omitted, {@link defaultReviewSummary}
115
+ * builds the locked deterministic template.
116
+ */
117
+ export declare function synthesizeReview(input: {
118
+ findings: MstarReviewV1["findings"];
119
+ summary_md?: string;
120
+ unverifiedCount?: number;
121
+ unmetAc?: PrTallyInput["unmetAc"];
122
+ target?: MstarReviewV1["target"];
123
+ }): MstarReviewV1;
59
124
  /**
60
125
  * Which reviewed artifact the report (or evidence file) belongs to.
61
126
  *
package/dist/status.d.ts CHANGED
@@ -154,8 +154,16 @@ export declare const validateStatus: typeof validateStatusV2;
154
154
  * acquiring the lock; this helper asserts it as a safety net (cheap —
155
155
  * an invalid entry would fail `validateStatusV2` anyway, but the explicit
156
156
  * gate keeps the pre-lock fail-fast contract of `registerWorkflow`).
157
+ *
158
+ * Async-only (architect-locked 2026-08-27): the durable write goes through
159
+ * `getArtifactStore().put({ kind: "status", key: "root", ... })` inside the
160
+ * caller's lock — the store is the persist backend, never a second lock.
161
+ * Fails loud (qc3 F-201) when the active FsStore would resolve its
162
+ * `status.json` to a path other than `statusPath` — callers whose root
163
+ * differs from the active store's root MUST
164
+ * `setArtifactStore(createFsStore(root))` first.
157
165
  */
158
- export declare function registerWorkflowEntryLocked(statusPath: string, entry: WorkflowEntry): StatusV2Doc;
166
+ export declare function registerWorkflowEntryLocked(statusPath: string, entry: WorkflowEntry): Promise<StatusV2Doc>;
159
167
  /**
160
168
  * Register one active workflow entry in the v2 root file (plan Task 3).
161
169
  * Idempotent upsert by entry `id` under the root-file `withStatusWriteLock`,
@@ -177,6 +185,10 @@ export declare function registerWorkflow(root: string, entry: WorkflowEntry): Pr
177
185
  * actually removed. The final document is validated (removal-at-terminal
178
186
  * invariant included) before the write — a v1 root is refused with the
179
187
  * `mstar migrate` hint.
188
+ *
189
+ * Fails loud (qc3 F-201) when the active FsStore would resolve its
190
+ * `status.json` to a path other than the caller's root — the no-op branches
191
+ * below never mask a store/path mismatch.
180
192
  */
181
193
  export declare function unregisterWorkflow(root: string, id: string): Promise<StatusV2Doc>;
182
194
  /**
@@ -0,0 +1,76 @@
1
+ /** JSON coordination-doc kinds the store persists (spec SP2). */
2
+ export type ArtifactKind = "status" | "snapshot" | "residuals" | "review" | "json";
3
+ /** Stable key inside the kind. Workflow id, project id, or review id;
4
+ * `kind: "status"` always uses key `"root"`. */
5
+ export type ArtifactRef = {
6
+ kind: ArtifactKind;
7
+ key: string;
8
+ };
9
+ /** A store document: the ref plus the payload and an optional schema id. */
10
+ export type ArtifactDoc<T = unknown> = ArtifactRef & {
11
+ payload: T;
12
+ /** Optional content-type / schema id (e.g. mstar.review/v1). */
13
+ schema?: string;
14
+ };
15
+ /** Type-only persist contract (HostAdapter pattern). `put` / `get` /
16
+ * `delete` are async-only — a network-backed store never needs a sync
17
+ * facade (architect-locked 2026-08-27: no `putSync` anywhere). `list?`
18
+ * is optional enumeration (D4): stores that cannot enumerate decline by
19
+ * omitting the member — callers probe `typeof store.list === "function"`
20
+ * (same pattern as `delete?`). */
21
+ export interface ArtifactStore {
22
+ put(doc: ArtifactDoc): Promise<void>;
23
+ get<T = unknown>(ref: ArtifactRef): Promise<T | undefined>;
24
+ delete?(ref: ArtifactRef): Promise<void>;
25
+ /** Enumerate refs of `kind`, sorted by key ascending (spec D4). Uniform
26
+ * rule: report what exists — missing backing dir/file → `[]`; every
27
+ * listed key round-trips through `get`. `json` is not enumerable. */
28
+ list?(kind: ArtifactKind): Promise<ArtifactRef[]>;
29
+ }
30
+ /** Map an artifact ref to its file path under `harnessRoot` (spec SP2
31
+ * FsStore path table — the single kind→path mapping, shared with SP3;
32
+ * SP3 never re-implements or extends it). Exported (qc1 S-002) so SP3
33
+ * imports the contract instead of re-deriving it textually. */
34
+ export declare function resolveArtifactPath(harnessRoot: string, ref: ArtifactRef): string;
35
+ /** Default local adapter: maps kinds to the existing `.mstar/` paths.
36
+ * `put` uses the sync `writeJson` (atomic temp+rename unchanged) and
37
+ * returns a resolved Promise; locks stay with callers (architect-locked
38
+ * 2026-08-27). `get` mirrors `readJson`: missing file → `undefined`,
39
+ * malformed JSON → throw with the path in the message. The returned store
40
+ * also exposes its resolved `root` so the routed writers can fail loud
41
+ * when a caller's explicit target path diverges from the store-resolved
42
+ * path (qc3 F-201); `root` is not part of the `ArtifactStore` contract.
43
+ * `list` enumerates per the D4 table: report what exists — missing
44
+ * backing dir/file → `[]`, `json` throws, keys sorted ascending. */
45
+ export declare function createFsStore(harnessRoot: string): ArtifactStore & {
46
+ root: string;
47
+ };
48
+ export declare function setArtifactStore(store: ArtifactStore | undefined): void;
49
+ /** The active store: the injected one when set, otherwise a lazily
50
+ * created `FsStore` from `resolveHarnessDir(process.cwd())`. A `null`
51
+ * resolution throws the same fail-loud "harness dir not found" style as
52
+ * `resolveHarnessSubdir` — never a silent cwd fallback. */
53
+ export declare function getArtifactStore(): ArtifactStore;
54
+ /**
55
+ * Fail-loud path-agreement guard for the routed writers (qc3 F-201): when
56
+ * `store` is an FsStore, resolve the path the store would compute for
57
+ * `ref` and require it to equal `expectedPath` (the caller's explicit
58
+ * target). A divergence means the caller's path lives outside the active
59
+ * store's root — the lockdir would serialize the parameter path while the
60
+ * put lands at the store root (silent decoupling / split-brain window).
61
+ * Custom (non-FS) stores own their mapping by design and are skipped.
62
+ * Cheap: pure path resolution, no I/O.
63
+ */
64
+ export declare function assertFsStorePath(store: ArtifactStore, ref: ArtifactRef, expectedPath: string): void;
65
+ /** Load a store module from a filesystem path (spec SP2 § Injection 2–3,
66
+ * SP2-AC6 / SP2-AC7). Resolves against cwd; rejects empty values and any
67
+ * URI scheme before `import()`; throws when the file is missing. Accepts a
68
+ * `createArtifactStore` named export, a default-exported factory, or a
69
+ * default-exported object; the result is structurally verified (`put` +
70
+ * `get` functions) before use.
71
+ *
72
+ * CJS interop: `import()` of a CommonJS module surfaces `module.exports`
73
+ * as `default` (plus statically detected named exports on the namespace);
74
+ * the `?? mod` fallback covers loaders that surface `module.exports`
75
+ * directly as the namespace. No loader protocol beyond `import()`. */
76
+ export declare function loadStoreModule(modulePath: string): Promise<ArtifactStore>;
@@ -0,0 +1 @@
1
+ export {};
@@ -73,6 +73,14 @@ export declare function validateWorkflowSnapshot(doc: unknown): GateResult;
73
73
  * `withStatusWriteLock(snapshotPath)` (plan Task 2 — the `.status-write.lockdir`
74
74
  * lands inside `workflows/<id>/`, dirname of the snapshot; no harness-root
75
75
  * pollution). The snapshot is validated first — an invalid snapshot throws
76
- * and nothing is written. `dir` is created recursively.
76
+ * and nothing is written. `dir` is created recursively. The durable write
77
+ * routes through the active `ArtifactStore` (spec SP2: snapshot →
78
+ * `{ kind: "snapshot", key: <workflow id> }`) inside the existing lock — the
79
+ * default FsStore resolves `{WORKFLOW_DIR}/<key>/snapshot.json`, identical
80
+ * to `join(dir, WORKFLOW_SNAPSHOT_FILE)` for canonical callers. The write
81
+ * fails loud when the active FsStore would resolve a different path than
82
+ * the caller's `join(dir, WORKFLOW_SNAPSHOT_FILE)` (qc3 F-201) — callers
83
+ * whose target root differs from the active store's root MUST
84
+ * `setArtifactStore(createFsStore(root))` first.
77
85
  */
78
86
  export declare function writeWorkflowSnapshot(snapshot: WorkflowSnapshot, dir: string): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/engine",
3
- "version": "3.4.1",
3
+ "version": "3.5.1",
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": {