@mstar-harness/dsh 2.4.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +10 -8
- package/README.zh.md +7 -5
- package/bundle/README.md +4 -4
- package/dist/client/panel/graph/project-graph.d.ts +29 -3
- package/dist/client/panel/locale.d.ts +1 -1
- package/dist/client/panel/zones/ProjectRollup.d.ts +24 -0
- package/dist/client.js +270 -34
- package/dist/gates/_shared.d.ts +4 -4
- package/dist/gates/adapter.d.ts +38 -12
- package/dist/gates/agent-flow.d.ts +117 -12
- package/dist/gates/dispatch.d.ts +16 -10
- package/dist/gates/goal-bridge.d.ts +1 -1
- package/dist/gates/status.d.ts +64 -14
- package/dist/gates/system-prompt.d.ts +6 -2
- package/dist/gates/workflow-ledger.d.ts +88 -17
- package/dist/gates/workflow-policy.d.ts +2 -1
- package/dist/gates/workflow-selection.d.ts +31 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1573 -690
- package/dist/service.d.ts +14 -9
- package/dist/types.d.ts +64 -2
- package/harness-agents/code-reviewer.md +0 -8
- package/harness-skills/mstar-branch-worktree/SKILL.md +14 -14
- package/harness-skills/mstar-branch-worktree/references/parallel-writable-pre-dispatch.md +2 -2
- package/harness-skills/mstar-compound/SKILL.md +2 -2
- package/harness-skills/mstar-compound/references/compound-workflow.md +3 -3
- package/harness-skills/mstar-compound-refresh/SKILL.md +4 -4
- package/harness-skills/mstar-compound-refresh/references/project-knowledge-bootstrap.md +1 -1
- package/harness-skills/mstar-dispatch-gates/SKILL.md +2 -2
- package/harness-skills/mstar-engine-legacy/SKILL.md +53 -0
- package/harness-skills/mstar-engine-legacy/references/anti-recursion-checklists.md +58 -0
- package/harness-skills/mstar-engine-legacy/references/engine-check-boilerplate.md +26 -0
- package/harness-skills/mstar-engine-legacy/references/lease-protocol.md +92 -0
- package/harness-skills/mstar-engine-legacy/references/qc-seat-n-restatements.md +57 -0
- package/harness-skills/mstar-engine-legacy/references/status-field-history.md +186 -0
- package/harness-skills/mstar-harness-core/SKILL.md +6 -3
- package/harness-skills/mstar-host/references/_shared/plan-mode-bridge-core.md +8 -8
- package/harness-skills/mstar-host/references/codex.md +1 -1
- package/harness-skills/mstar-host/references/cursor-plan-mode-bridge.md +19 -8
- package/harness-skills/mstar-host/references/cursor.md +5 -5
- package/harness-skills/mstar-host/references/dsh.md +10 -7
- package/harness-skills/mstar-host/references/kimi-plan-mode-bridge.md +3 -3
- package/harness-skills/mstar-host/references/kimi.md +1 -1
- package/harness-skills/mstar-host/references/omp-plan-mode-bridge.md +2 -2
- package/harness-skills/mstar-host/references/omp.md +2 -2
- package/harness-skills/mstar-host/references/zcode-plan-mode-bridge.md +3 -3
- package/harness-skills/mstar-host/references/zcode.md +1 -1
- package/harness-skills/mstar-iteration/SKILL.md +38 -37
- package/harness-skills/mstar-iteration/references/autonomous-direction-lock.md +2 -2
- package/harness-skills/mstar-iteration/references/iteration-compass-template.md +3 -3
- package/harness-skills/mstar-iteration/references/phase-2-worktree-lease.md +50 -38
- package/harness-skills/mstar-iteration/references/phase-3-iteration-close.md +7 -7
- package/harness-skills/mstar-iteration/references/phase-4-5-pr-delivery.md +2 -2
- package/harness-skills/mstar-phase-gates/SKILL.md +3 -3
- package/harness-skills/mstar-plan-artifacts/SKILL.md +21 -23
- package/harness-skills/mstar-plan-artifacts/references/knowledge-and-designs.md +16 -6
- package/harness-skills/mstar-plan-artifacts/references/plan-files-and-reports.md +10 -10
- package/harness-skills/mstar-plan-artifacts/references/status-and-residuals.md +190 -309
- package/harness-skills/mstar-plan-artifacts/templates/README.md +1 -3
- package/harness-skills/mstar-plan-artifacts/templates/status.empty.json +2 -4
- package/harness-skills/mstar-plan-conventions/SKILL.md +53 -13
- package/harness-skills/mstar-plan-conventions/references/artifact-storage-paths.md +7 -3
- package/harness-skills/mstar-plan-conventions/references/harness-bootstrap-and-agents-layering.md +9 -10
- package/harness-skills/mstar-project-governance/SKILL.md +119 -0
- package/harness-skills/mstar-review-qc/SKILL.md +3 -3
- package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +1 -1
- package/harness-skills/mstar-roles/references/project-manager/plan-management.md +6 -8
- package/harness-skills/mstar-roles/references/project-manager/qc-and-residuals.md +6 -6
- package/harness-skills/mstar-roles/references/project-manager.md +7 -7
- package/harness-skills/mstar-roles/references/qc-specialist/deep-review-lenses.md +1 -1
- package/harness-skills/mstar-roles/references/qc-specialist/report-template.md +1 -1
- package/harness-skills/mstar-roles/references/qc-specialist/reviewer-workflow.md +1 -1
- package/harness-skills/mstar-roles/references/qc-specialist-shared.md +2 -2
- package/harness-skills/mstar-sdd/SKILL.md +2 -2
- package/harness-skills/mstar-sdd/references/file-handoffs.md +1 -1
- package/package.json +14 -14
- package/harness-skills/mstar-plan-artifacts/references/done-compaction.md +0 -88
- package/harness-skills/mstar-plan-artifacts/templates/notes.empty.json +0 -5
- package/harness-skills/mstar-plan-artifacts/templates/plans-done.empty.json +0 -3
|
@@ -214,8 +214,15 @@ export type AgentFlowEvent = {
|
|
|
214
214
|
* settle carrying the SAME identity fields as its dispatch event.
|
|
215
215
|
*/
|
|
216
216
|
export interface AgentFlowDispatchRef {
|
|
217
|
-
/** The resolved `{HARNESS_DIR}` the dispatch
|
|
217
|
+
/** The resolved `{HARNESS_DIR}` the dispatch was attributed to. */
|
|
218
218
|
harnessDir: string;
|
|
219
|
+
/**
|
|
220
|
+
* The ACTIVE workflow dir the dispatch event was appended to
|
|
221
|
+
* (`<harnessDir>/workflows/<id>` — v3 layout). The settle records into
|
|
222
|
+
* the SAME file (settle pairing reads/writes the same ledger), even if
|
|
223
|
+
* the active set changed between dispatch and settle.
|
|
224
|
+
*/
|
|
225
|
+
workflowDir: string;
|
|
219
226
|
/** The dispatching session's stable id ('' when the exec carried none). */
|
|
220
227
|
agent?: string;
|
|
221
228
|
/** Assignment `Execute as` ('' when missing — the dispatch event's grammar). */
|
|
@@ -293,6 +300,74 @@ export declare function setAgentFlowLogger(sink: AgentFlowLogSink | undefined):
|
|
|
293
300
|
* @param prompt - the full Assignment text.
|
|
294
301
|
*/
|
|
295
302
|
export declare function taskIdOf(prompt: string): string | undefined;
|
|
303
|
+
/**
|
|
304
|
+
* Resolve the agent-flow WRITE target dir for one harness dir: the ACTIVE
|
|
305
|
+
* workflow's dir (root v2 `workflows[]` first entry — the shared active-set
|
|
306
|
+
* resolver; the terminal-mtime fallback is catalog-read-only and MUST NOT
|
|
307
|
+
* enter the writer's module graph). The active set is defined by root
|
|
308
|
+
* `workflows[]` MEMBERSHIP — non-terminal lifecycles (`running` AND
|
|
309
|
+
* `paused`; terminal lifecycles are removed at terminal) — so a paused
|
|
310
|
+
* lifecycle stays a valid append target (explicit decision, plan
|
|
311
|
+
* `20260819-workflow-dsh-viz` Task 2).
|
|
312
|
+
*
|
|
313
|
+
* No active entry → `null`: the record is SKIPPED with a one-time warn
|
|
314
|
+
* (per sink binding) — never a silent write into the root v1 file, never a
|
|
315
|
+
* write into a terminal snapshot dir (compass v3.0.0 § Catalog selection
|
|
316
|
+
* rule — the writer appends only to an active lifecycle).
|
|
317
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
318
|
+
* @returns the absolute workflow dir (`<harnessDir>/workflows/<id>`), or
|
|
319
|
+
* `null` when no active lifecycle resolves.
|
|
320
|
+
*/
|
|
321
|
+
export declare function resolveAgentFlowWriteDir(harnessDir: string): string | null;
|
|
322
|
+
/**
|
|
323
|
+
* Lock-directory name for the per-workflow write lock (qc3 W-1 fix-wave):
|
|
324
|
+
* guards the ledger append + size-gated truncating read-modify-write AND
|
|
325
|
+
* the workflow-ledger cursor sidecar read-modify-write against concurrent
|
|
326
|
+
* dsh processes sharing one active lifecycle (compass ruling 3). The
|
|
327
|
+
* lockdir lands INSIDE the workflow dir — dirname of the ledger — so it
|
|
328
|
+
* never pollutes the harness root (same placement rule as the engine's
|
|
329
|
+
* `withStatusWriteLock` lockdir).
|
|
330
|
+
*/
|
|
331
|
+
export declare const WORKFLOW_LEDGER_LOCKDIR = ".ledger-write.lockdir";
|
|
332
|
+
/**
|
|
333
|
+
* Run `fn` under the per-workflow inter-process write lock (qc3 W-1
|
|
334
|
+
* fix-wave): atomic `mkdir` on `<workflowDir>/.ledger-write.lockdir/`
|
|
335
|
+
* acquires (success acquires; existing dir → another writer holds the
|
|
336
|
+
* lock); while another writer holds it, wait up to
|
|
337
|
+
* {@link WORKFLOW_LOCKER_TIMEOUT_MS} and then THROW (Blocked — the
|
|
338
|
+
* lockdir is never removed for another holder). Ownership guard
|
|
339
|
+
* (double-unlock safety): the lockdir's `(dev, ino)` is captured at
|
|
340
|
+
* acquisition; `finally` re-stats the path and removes the directory ONLY
|
|
341
|
+
* when the identity is unchanged. Sync reentrancy detection: a nested
|
|
342
|
+
* acquisition on the same lockdir within this process throws immediately
|
|
343
|
+
* (sync code cannot interleave — a held dir is a bug, not contention).
|
|
344
|
+
* Crash diagnosis: a `holder.pid` file (acquiring process id) is written
|
|
345
|
+
* inside the lockdir on acquisition and removed on release; a hard crash
|
|
346
|
+
* leaks the lockdir and the timeout error names the recovery step.
|
|
347
|
+
*
|
|
348
|
+
* SAME PATTERN as the engine's `withStatusWriteLock` (packages/engine/src/
|
|
349
|
+
* lease.ts) — atomic-mkdir lockdir, the SSOT-documented alternative to
|
|
350
|
+
* `flock` on Bun (no `node:fs` flock); unlike flock, a hard crash leaks
|
|
351
|
+
* the lockdir (documented residual, recovery = remove when no writer is
|
|
352
|
+
* alive).
|
|
353
|
+
*
|
|
354
|
+
* Steady state: ONE writer per workflow dir (one dsh process per
|
|
355
|
+
* lifecycle). The lock serializes the multi-session shared-lifecycle case
|
|
356
|
+
* (compass ruling 3) and is held ONLY around the bounded critical section
|
|
357
|
+
* (append+truncate, or the cursor read-modify-write) — never across scans.
|
|
358
|
+
* @param workflowDir - the workflow dir whose ledger/cursor files the
|
|
359
|
+
* critical section touches.
|
|
360
|
+
* @param fn - the critical section (sync; the lock is sync by design — the
|
|
361
|
+
* ledger record paths are called from sync gate hooks).
|
|
362
|
+
* @param opts - `timeoutMs` / `pollMs` overrides (defaults 30 s / 10 ms) —
|
|
363
|
+
* the same testable options as the engine's `withStatusWriteLock`
|
|
364
|
+
* (`lease.ts`); production callers never set them.
|
|
365
|
+
* @returns the critical section's return value.
|
|
366
|
+
*/
|
|
367
|
+
export declare function withWorkflowDirLock<T>(workflowDir: string, fn: () => T, opts?: {
|
|
368
|
+
timeoutMs?: number;
|
|
369
|
+
pollMs?: number;
|
|
370
|
+
}): T;
|
|
296
371
|
/**
|
|
297
372
|
* Record one dispatch event (spec §2.1.3). Fully try/catch-contained — NEVER
|
|
298
373
|
* throws into the gate; a failing record logs only (`mstar/agent-flow`).
|
|
@@ -327,14 +402,21 @@ export declare function recordDispatch(input: {
|
|
|
327
402
|
/**
|
|
328
403
|
* Record one settle event (spec §2.1.3). Fully try/catch-contained; a failing
|
|
329
404
|
* record logs only. Callers resolve the harness dir from the PAIRED dispatch
|
|
330
|
-
* (the pairing store's dispatchRef — never a payload probe).
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
405
|
+
* (the pairing store's dispatchRef — never a payload probe). The settle
|
|
406
|
+
* appends to the ACTIVE workflow dir (v3 layout); a PAIRED settle carries
|
|
407
|
+
* the dispatchRef's `workflowDir` so it lands in the SAME file as its
|
|
408
|
+
* dispatch even if the active set changed in between. An unpaired direct
|
|
409
|
+
* call resolves the active workflow from `harnessDir`; no active lifecycle
|
|
410
|
+
* → skipped with a one-time warn (never a root v1 write).
|
|
411
|
+
* @param input - harness dir + optional paired workflow dir + agent id +
|
|
412
|
+
* outcome + optional duration + the PAIRED dispatch's identity
|
|
413
|
+
* (`role`/`planId`/`taskId` — same field names + semantics as the dispatch
|
|
414
|
+
* event; written for every paired settle, so the client can exactly pair
|
|
415
|
+
* the settle back to its dispatch).
|
|
335
416
|
*/
|
|
336
417
|
export declare function recordSettle(input: {
|
|
337
418
|
harnessDir: string;
|
|
419
|
+
workflowDir?: string;
|
|
338
420
|
agent?: string;
|
|
339
421
|
outcome: SettleOutcome;
|
|
340
422
|
durationMs?: number;
|
|
@@ -353,14 +435,24 @@ export declare function recordSettle(input: {
|
|
|
353
435
|
* from the durable `tool-workflow/*` session event (`ts` takes the envelope's
|
|
354
436
|
* `time`). Malformed input is a caller bug — the strict narrowing applies on
|
|
355
437
|
* READ (`eventFromUnknown`), never here.
|
|
356
|
-
*
|
|
438
|
+
*
|
|
439
|
+
* v3 write path: the event appends to the ACTIVE workflow dir (root v2
|
|
440
|
+
* `workflows[]` first entry). The consumer passes the already-resolved
|
|
441
|
+
* `workflowDir` (it needs it for the durable watermark anyway — one
|
|
442
|
+
* resolution per row); a direct caller without it resolves from
|
|
443
|
+
* `harnessDir`. No active lifecycle → `false` (skipped with a one-time warn
|
|
444
|
+
* — never a root v1 write, never a terminal snapshot write).
|
|
445
|
+
* @param input - harness dir + optional pre-resolved active workflow dir +
|
|
446
|
+
* the fully-shaped v1 workflow event.
|
|
357
447
|
* @returns `true` when the row was appended (the caller may durably advance
|
|
358
|
-
* its watermark); `false` on a contained append failure
|
|
359
|
-
* leave the cursor behind so the row
|
|
360
|
-
* (qc3 R-401: advance-then-record made a
|
|
448
|
+
* its watermark); `false` on a contained append failure OR when no active
|
|
449
|
+
* lifecycle resolves — the caller must leave the cursor behind so the row
|
|
450
|
+
* is re-attempted at the next scan (qc3 R-401: advance-then-record made a
|
|
451
|
+
* failed append permanent loss).
|
|
361
452
|
*/
|
|
362
453
|
export declare function recordWorkflowEvent(input: {
|
|
363
454
|
harnessDir: string;
|
|
455
|
+
workflowDir?: string;
|
|
364
456
|
event: AgentFlowWorkflowEvent;
|
|
365
457
|
}): boolean;
|
|
366
458
|
/**
|
|
@@ -417,11 +509,24 @@ export declare function recordWorkflowVerdict(input: WorkflowVerdictInput): void
|
|
|
417
509
|
* state instead of an evidence-missing degrade; only an UNREADABLE file
|
|
418
510
|
* returns null (advisory degrade — the catalog renders no agent-flow line).
|
|
419
511
|
* Malformed lines are skipped, never fatal.
|
|
420
|
-
*
|
|
512
|
+
*
|
|
513
|
+
* v3 layout: the ledger lives in the WORKFLOW dir (`<harnessDir>/workflows/
|
|
514
|
+
* <id>`), never the harness root — the dir parameter IS the workflow dir
|
|
515
|
+
* (the catalog passes `join(harnessDir, selection.dir)`; the writer appends
|
|
516
|
+
* there via `resolveAgentFlowWriteDir`).
|
|
517
|
+
*
|
|
518
|
+
* Large-file reads (plan `20260820-dsh-ledger-tail-read`): ledgers above
|
|
519
|
+
* `AGENT_FLOW_TAIL_READ_THRESHOLD_BYTES` are read as a bounded latest-first
|
|
520
|
+
* tail — seek from EOF, aligned to a line boundary, doubling backward until
|
|
521
|
+
* the window holds `limit` complete lines — so the catalog pays O(window) at
|
|
522
|
+
* the byte layer instead of parsing every historical line. Both paths feed
|
|
523
|
+
* the SAME parse funnel below (`ledgerContent` yields a `content` string),
|
|
524
|
+
* so tail/full parity is structural.
|
|
525
|
+
* @param workflowDir - the workflow dir whose `agent-flow.jsonl` is read.
|
|
421
526
|
* @param limit - explicit window bound: `undefined` → `AGENT_FLOW_DEFAULT_LIMIT`;
|
|
422
527
|
* otherwise `Math.max(0, Math.floor(limit))` — `0` requests the EMPTY window.
|
|
423
528
|
*/
|
|
424
|
-
export declare function readAgentFlow(
|
|
529
|
+
export declare function readAgentFlow(workflowDir: string, limit?: number): AgentFlowView | null;
|
|
425
530
|
/**
|
|
426
531
|
* The `tools/post-execute` settle pairing (plan `20260811-panel-f4-timeliness`
|
|
427
532
|
* Task 1 — replaces the old defensive payload probing): the VERIFIED
|
package/dist/gates/dispatch.d.ts
CHANGED
|
@@ -61,7 +61,8 @@ export declare function isAssignmentShaped(assignmentText: string): boolean;
|
|
|
61
61
|
* Resolve the hard-enforcement flag for one dispatch: explicit Config override
|
|
62
62
|
* wins, else the Assignment's OWN `Enforcement: hard` header flag (opencode
|
|
63
63
|
* parity — header region only, a body-quoted example never hardens), else the
|
|
64
|
-
*
|
|
64
|
+
* repo `.mstarc` `[config] enforcement`, else the iteration compass
|
|
65
|
+
* frontmatter, else warn-only.
|
|
65
66
|
*/
|
|
66
67
|
export declare function resolveDispatchHard(harnessDir: string | null, config: Config, assignmentText: string): boolean;
|
|
67
68
|
/** A header value that means "no value" (placeholder conventions). Type guard so callers narrow to `string`. Shared with the agent-flow ledger (qc1 F-003 — one grammar, no copy-paste drift). */
|
|
@@ -93,8 +94,11 @@ export declare function sessionIdOf(exec: ToolExecution): string | undefined;
|
|
|
93
94
|
* `dispatch.execution-mode.*` codes stay out of the parity field set) OR
|
|
94
95
|
* whose plan row is `InProgress`.
|
|
95
96
|
*
|
|
96
|
-
* Contract (status-and-residuals.md § Pre-dispatch re-verify
|
|
97
|
-
*
|
|
97
|
+
* Contract (status-and-residuals.md § Pre-dispatch re-verify; v3
|
|
98
|
+
* relocation — plan `20260819-workflow-dsh-viz` Task 3): before any
|
|
99
|
+
* writable implement dispatch, reread the ACTIVE workflow snapshot
|
|
100
|
+
* (`workflows/<id>/snapshot.json` — the v1 root `plans[]` home is gone; the
|
|
101
|
+
* root v2 `status.json` supplies the active `workflows[]`) and confirm
|
|
98
102
|
* the session still passes verify-held-lease — `holder`, `worktree_path` and
|
|
99
103
|
* `working_branch` must match the Assignment; mismatch or absent lease →
|
|
100
104
|
* STOP. Engine `verifyPlanExecutionLease` + `validateExecutionLease` carry
|
|
@@ -104,13 +108,15 @@ export declare function sessionIdOf(exec: ToolExecution): string | undefined;
|
|
|
104
108
|
*
|
|
105
109
|
* Degrade-allow cases (no false positives): no harness dir, unresolvable plan
|
|
106
110
|
* id, and non-SDD assignments whose plan row is absent or not InProgress.
|
|
107
|
-
* Unverifiable lease states (
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
111
|
+
* Unverifiable lease states (unreadable/missing root status.json, a
|
|
112
|
+
* selection failure — v1/migration-required root, no active workflow — an
|
|
113
|
+
* unreadable/missing snapshot, plan row not registered) are violations ONLY
|
|
114
|
+
* for sdd dispatches (the lease state cannot be confirmed — the status gate
|
|
115
|
+
* already guards the next write); unreadable docs never harden a soft
|
|
116
|
+
* workflow. A missing status.json is NOT a silent fail-open for sdd: the
|
|
117
|
+
* claim-before-InProgress red line needs the plan's execution_lease, and a
|
|
118
|
+
* missing root/snapshot cannot confirm it — `lease.dispatch.unverifiable`
|
|
119
|
+
* fires (advisory in warn, deny under hard).
|
|
114
120
|
*/
|
|
115
121
|
export declare function leaseGateViolations(harnessDir: string | null, exec: ToolExecution, writable: boolean | undefined, prompt: string): ValidationResult[];
|
|
116
122
|
/**
|
|
@@ -133,7 +133,7 @@ export declare function rootAgentOf(agent: unknown, agents: AgentsView): unknown
|
|
|
133
133
|
* compare when the mirror is in place — no churn) — plus a THIRD, advisory
|
|
134
134
|
* listener on the `session/event` firehose (Task 3): a `goal/change`
|
|
135
135
|
* envelope whose goal is blocked logs ONE warn (code + objective summary +
|
|
136
|
-
*
|
|
136
|
+
* project-register residual pointer) with ZERO harness writes
|
|
137
137
|
* (the one-way mirror; see {@link warnBlockedGoal}). The goals service is an
|
|
138
138
|
* OPTIONAL seam (`ctx.get('goals')` structural read): absent → ONE debug log
|
|
139
139
|
* + the mirror stays inert, never a boot failure — the blocked advisory is
|
package/dist/gates/status.d.ts
CHANGED
|
@@ -33,21 +33,66 @@ export interface StatusGateAdvisory {
|
|
|
33
33
|
degraded?: boolean;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
* (
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
36
|
+
* Canonical `{HARNESS_DIR}` coordination-document kinds the status gate
|
|
37
|
+
* covers (opencode-parity vocabulary — the SAME three kinds the P2-fixed
|
|
38
|
+
* opencode `harnessDocKindOfTarget` classifies): the v2 root `status.json`,
|
|
39
|
+
* `workflows/<id>/snapshot.json`, and `projects/<id>/residuals.json`. Each
|
|
40
|
+
* kind maps to its matching engine validator in
|
|
41
|
+
* {@link validateStatusValue}.
|
|
42
42
|
*/
|
|
43
|
-
export
|
|
43
|
+
export type HarnessDocKind = 'status' | 'snapshot' | 'register';
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
45
|
+
* Classify one fs target as a canonical `{HARNESS_DIR}` coordination
|
|
46
|
+
* document: basename ∈ {status.json, snapshot.json, residuals.json} AND the
|
|
47
|
+
* harness-relative path matches the canonical home (root `status.json`,
|
|
48
|
+
* `{WORKFLOW_DIR}/<id>/snapshot.json`, `{PROJECT_DIR}/<id>/residuals.json`
|
|
49
|
+
* — one path component each, mirroring the P2-fixed opencode
|
|
50
|
+
* `harnessDocKindOfTarget`).
|
|
51
|
+
*
|
|
52
|
+
* Phase-5 F1: the snapshot/register rel is computed against the RESOLVED
|
|
53
|
+
* layout dirs (`resolveWorkflowDir` / `resolveProjectDir` — a `.mstarc`
|
|
54
|
+
* `[config] workflow_dir` / `project_dir` declaration wins, defaults
|
|
55
|
+
* `workflows` / `projects` under the harness dir), so a custom layout
|
|
56
|
+
* classifies at the same location the runtime writes; resolver failure
|
|
57
|
+
* (never expected with the explicit harness dir) falls back to the default
|
|
58
|
+
* names.
|
|
59
|
+
*
|
|
60
|
+
* dsh divergence from the opencode probe (documented): opencode resolves
|
|
61
|
+
* the harness root by marker probe (W-REV-1) with a declared-root fallback;
|
|
62
|
+
* the dsh gates ALREADY resolve `{HARNESS_DIR}` per calling session
|
|
63
|
+
* workspace through the shared `HarnessResolver` BEFORE the intent slot
|
|
64
|
+
* runs, so classification here is against that resolved root — no second
|
|
65
|
+
* probe, no W-REV-3 rebuild. Returns the kind when gated, `null` otherwise
|
|
66
|
+
* (a non-coordination target, or a path outside the resolved harness dir —
|
|
67
|
+
* the gate is inert for them).
|
|
68
|
+
*/
|
|
69
|
+
export declare function harnessDocKindOfTarget(harnessDir: string, targetPath: string): HarnessDocKind | null;
|
|
70
|
+
/**
|
|
71
|
+
* Validate a PARSED harness coordination document through the
|
|
72
|
+
* kind-matched engine validator (v2 root / workflow snapshot / project
|
|
73
|
+
* register — the P2-fixed "one validator per kind" shape) plus the
|
|
74
|
+
* snapshot-only `findingsCleanupGate` extension per plan row that
|
|
75
|
+
* CONFIGURES a mode (the v1 per-plan-row cleanup gate relocated: plan rows
|
|
76
|
+
* live on the snapshot, residuals on the project registers). Shared by
|
|
77
|
+
* {@link validateStatusDoc} (the on-disk single-read path) and the host
|
|
78
|
+
* adapter's `beforeStatusWrite` (the incoming document) — the fs-intent
|
|
79
|
+
* gate, the adapter hook and the repair escape all surface the SAME
|
|
80
|
+
* violation codes.
|
|
81
|
+
* @param kind - the target's {@link HarnessDocKind} (matching engine validator).
|
|
82
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}`; required for the
|
|
83
|
+
* snapshot kind's cleanup extension (the registers it reads live under it),
|
|
84
|
+
* otherwise unused.
|
|
85
|
+
*/
|
|
86
|
+
export declare function validateStatusValue(doc: unknown, kind: HarnessDocKind, harnessDir?: string | null): GateResult;
|
|
87
|
+
/**
|
|
88
|
+
* Run the status gate over the CURRENT on-disk document at a canonical
|
|
89
|
+
* harness coordination target. The fs intent waterfall carries only
|
|
90
|
+
* `(target, actor)` — never the incoming content — so the vetoable check
|
|
91
|
+
* is the pre-write state (the opencode hook's fallback for the same
|
|
92
|
+
* reason). The kind-matched validator runs via {@link validateStatusValue}
|
|
93
|
+
* (the snapshot kind's `findingsCleanupGate` extension included) — schema
|
|
94
|
+
* violations short-circuit it (the doc must parse for the cleanup gate to
|
|
95
|
+
* be meaningful).
|
|
51
96
|
*
|
|
52
97
|
* Single-read contract: the file is parsed exactly once and the
|
|
53
98
|
* parsed doc is passed to {@link validateStatusValue} — the previous
|
|
@@ -57,8 +102,13 @@ export declare function validateStatusValue(doc: unknown): GateResult;
|
|
|
57
102
|
* shape; never throws. Missing files are guarded by the callers
|
|
58
103
|
* (`gateStatusIntent`, {@link DshHostAdapter.statusGate}) — first create has
|
|
59
104
|
* no document to validate and passes before this function runs.
|
|
105
|
+
* @param path - the canonical coordination-document path (root status.json
|
|
106
|
+
* / workflow snapshot / project register — the caller classifies it).
|
|
107
|
+
* @param kind - the target's {@link HarnessDocKind}.
|
|
108
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}` (the snapshot kind's
|
|
109
|
+
* cleanup extension needs it to locate the project registers).
|
|
60
110
|
*/
|
|
61
|
-
export declare function validateStatusDoc(
|
|
111
|
+
export declare function validateStatusDoc(path: string, kind: HarnessDocKind, harnessDir?: string | null): GateResult;
|
|
62
112
|
/**
|
|
63
113
|
* `fs/write-intent` listener. Registered with `prepend` so this decider runs
|
|
64
114
|
* BEFORE dsh-fs-policy regardless of mount order: the slot is first-wins by
|
|
@@ -38,9 +38,13 @@
|
|
|
38
38
|
* - The context provider reuses the catalog's unified machine-summary
|
|
39
39
|
* source (`buildCatalogSources` — the SAME builder the engine-status
|
|
40
40
|
* pre-step catalog row uses) and projects a BOUNDED subset: watermark +
|
|
41
|
-
* iteration gate + compact state line. Full
|
|
41
|
+
* iteration gate + compact state line. Full catalog content
|
|
42
42
|
* (residual detail, agent-flow events, knowledge digest, branch/policy
|
|
43
|
-
* anchors) stays out.
|
|
43
|
+
* anchors) stays out. v3 (plan `20260819-workflow-dsh-viz` Task 3): the
|
|
44
|
+
* digest reads ONLY the catalog row (`state` — itself aggregated from the
|
|
45
|
+
* SELECTED workflow snapshot + project registers) — no direct
|
|
46
|
+
* status.json / snapshot file reads to change. The build is TTL-memoized
|
|
47
|
+
* PER RESOLVED HARNESS DIR
|
|
44
48
|
* (`DEFAULT_CATALOG_TTL_MS`) so the per-assembly hot path does not
|
|
45
49
|
* re-read status.json / the compass / the ledger on every prompt
|
|
46
50
|
* assembly (the catalog's documented staleness tradeoff).
|
|
@@ -23,19 +23,34 @@
|
|
|
23
23
|
*
|
|
24
24
|
* DEDUPE (qc2 W-1 / qc3 F-301 fix-wave): ONE DURABLE per-session watermark —
|
|
25
25
|
* the next expected envelope `seq` (session-log position) — persisted to
|
|
26
|
-
* `{HARNESS_DIR}/workflow-ledger-cursors.json` (a small
|
|
27
|
-
* to `agent-flow.jsonl`, written
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* the workflow
|
|
26
|
+
* `{HARNESS_DIR}/workflows/<id>/workflow-ledger-cursors.json` (a small
|
|
27
|
+
* bounded sidecar next to the workflow-dir `agent-flow.jsonl`, written
|
|
28
|
+
* atomically temp-file + rename through the same containment discipline; the
|
|
29
|
+
* root cursor file is NOT read after migration — no read fallback). The
|
|
30
|
+
* watermark is consulted AND advanced by every scan (cold /
|
|
31
|
+
* created-backfill / live): envelopes with `seq` below it were already
|
|
32
|
+
* recorded — across cold+live overlap AND across plugin re-applies (a
|
|
33
|
+
* re-registration no longer re-records the same live sessions). The
|
|
34
|
+
* watermark advances only AFTER the ledger row appended successfully (qc3
|
|
35
|
+
* R-401 — a failing append leaves the cursor behind, so the row is
|
|
36
|
+
* re-attempted at the next scan, never permanently lost). The in-memory
|
|
37
|
+
* Map is the durable file's mirror (module-level cache keyed by WORKFLOW
|
|
38
|
+
* DIR, bounded by the session cap AND by a workflow-dir count cap — qc3
|
|
39
|
+
* S-4 — so a long-lived process across many workflow ids never grows the
|
|
40
|
+
* cache unbounded); a failed durable write keeps the in-memory mirror
|
|
41
|
+
* advanced (in-process dedupe — qc3 S-6) with one warn — a ledger row is
|
|
42
|
+
* never lost and the workflow run is never affected.
|
|
43
|
+
*
|
|
44
|
+
* INTER-PROCESS SERIALIZATION (qc3 W-1 fix-wave): the watermark
|
|
45
|
+
* read-modify-write (load fresh → mutate → whole-map save) runs inside the
|
|
46
|
+
* per-workflow write lock shared with the ledger append
|
|
47
|
+
* (`withWorkflowDirLock` from agent-flow.ts — the same lockdir pattern as
|
|
48
|
+
* the engine's `withStatusWriteLock`). Steady state is ONE writer per
|
|
49
|
+
* workflow dir; under multi-session shared lifecycle (compass ruling 3)
|
|
50
|
+
* the lock makes the cursor save read-modify-write atomic across
|
|
51
|
+
* processes — a whole-map save can no longer silently clobber another
|
|
52
|
+
* process's just-advanced cursor (the duplicate-row regression mode). The
|
|
53
|
+
* lock is held only around the bounded cursor update, never across scans.
|
|
39
54
|
*
|
|
40
55
|
* Mapping (Task 2 schema): `tool-workflow/run-start` → `workflow-run`
|
|
41
56
|
* (`agent` = the carrying parent session id), `tool-workflow/agent-start` →
|
|
@@ -92,12 +107,15 @@ import type { WorkflowAskCache } from './workflow-policy.ts';
|
|
|
92
107
|
/** Logger label for the workflow-ledger consumer (dsh logger naming: `<scope>/<subject>`). */
|
|
93
108
|
export declare const WORKFLOW_LEDGER_LOGGER = "mstar/workflow-ledger";
|
|
94
109
|
/**
|
|
95
|
-
* The durable watermark file name under
|
|
110
|
+
* The durable watermark file name under the WORKFLOW dir (qc2 W-1 / qc3
|
|
96
111
|
* F-301 fix-wave): `{ "v": 1, "cursors": { "<sessionId>": <nextSeq> } }` —
|
|
97
112
|
* the next expected envelope seq per session id. Written atomically
|
|
98
113
|
* (temp-file + rename) after every recorded workflow row; read lazily per
|
|
99
|
-
*
|
|
100
|
-
*
|
|
114
|
+
* workflow dir (module-level cache). v3 layout: the sidecar lives in the
|
|
115
|
+
* ACTIVE workflow dir (`workflows/<id>/workflow-ledger-cursors.json`) next
|
|
116
|
+
* to the ledger — the root cursor file is NOT read after migration (no read
|
|
117
|
+
* fallback). Absent on first run (silent); a present-but-corrupt file
|
|
118
|
+
* degrades to in-memory-only with one warn.
|
|
101
119
|
*/
|
|
102
120
|
export declare const WORKFLOW_LEDGER_WATERMARK_FILE = "workflow-ledger-cursors.json";
|
|
103
121
|
/**
|
|
@@ -107,6 +125,15 @@ export declare const WORKFLOW_LEDGER_WATERMARK_FILE = "workflow-ledger-cursors.j
|
|
|
107
125
|
* evicted session re-records its rows; bounded by the cap).
|
|
108
126
|
*/
|
|
109
127
|
export declare const WORKFLOW_LEDGER_WATERMARK_MAX_SESSIONS = 256;
|
|
128
|
+
/**
|
|
129
|
+
* Workflow-dir count cap for the module-level watermark cache (qc3 S-4
|
|
130
|
+
* fix-wave): a long-lived process can touch many workflow ids over its
|
|
131
|
+
* lifetime (each iteration lifecycle creates a new workflow dir) — the
|
|
132
|
+
* cache evicts the OLDEST cached dir when it exceeds this cap. The file is
|
|
133
|
+
* the durable store; the cache is only a mirror, so an evicted dir is
|
|
134
|
+
* re-read (and re-cached) on its next visit — no correctness impact.
|
|
135
|
+
*/
|
|
136
|
+
export declare const WORKFLOW_LEDGER_WATERMARK_MAX_DIRS = 64;
|
|
110
137
|
/** Consumer log levels the module sink understands. */
|
|
111
138
|
export type WorkflowLedgerLogLevel = 'debug' | 'warn';
|
|
112
139
|
/** Module-level consumer log sink — bound by `apply` to `ctx.logger(WORKFLOW_LEDGER_LOGGER)` (agent-flow ledger precedent). */
|
|
@@ -117,6 +144,49 @@ export type WorkflowLedgerLogSink = (level: WorkflowLedgerLogLevel, message: str
|
|
|
117
144
|
* can restore it (test pattern: agent-flow `setAgentFlowLogger`).
|
|
118
145
|
*/
|
|
119
146
|
export declare function setWorkflowLedgerLogger(sink: WorkflowLedgerLogSink): WorkflowLedgerLogSink;
|
|
147
|
+
/** The number of workflow dirs currently mirrored in the module-level watermark cache (qc3 S-4 observability). */
|
|
148
|
+
export declare function workflowLedgerWatermarkCacheSize(): number;
|
|
149
|
+
/**
|
|
150
|
+
* Advance one session's watermark entry to `nextSeq` and persist. Runs the
|
|
151
|
+
* whole read-modify-write under the per-workflow inter-process lock
|
|
152
|
+
* (qc3 W-1 fix-wave — same lock as the ledger append): the fresh load
|
|
153
|
+
* starts from the latest durable cursors, so two processes sharing one
|
|
154
|
+
* lifecycle never clobber each other's advances; the session entry is
|
|
155
|
+
* monotonic (`Math.max` — a stale concurrent advance never regresses an
|
|
156
|
+
* already-higher cursor). When a NEW session would push the map past the
|
|
157
|
+
* cap, evict first: prefer an entry whose session is no longer live
|
|
158
|
+
* (`isEvictable` — a live session's cursor must never be dropped, or the
|
|
159
|
+
* next re-apply would re-record its rows); fall back to the oldest entry
|
|
160
|
+
* when every entry is live. The residual of eviction (a later restore of an
|
|
161
|
+
* evicted session re-records) is bounded by the cap and documented in the
|
|
162
|
+
* README.
|
|
163
|
+
*
|
|
164
|
+
* DEGRADED PATH (qc3 S-6 fix-wave): a lock timeout (another writer stuck
|
|
165
|
+
* for the full timeout — a crashed/stuck peer), a reentrancy error, or a
|
|
166
|
+
* throwing critical section leaves the durable file untouched, but the
|
|
167
|
+
* in-memory mirror is STILL advanced (the same monotonic `Math.max`;
|
|
168
|
+
* eviction skipped — the cap bounds the durable sidecar only), so
|
|
169
|
+
* re-applies in THIS process stay deduped. The durable file lags: the next
|
|
170
|
+
* successful locked advance re-reads it fresh and catches up; a restart
|
|
171
|
+
* reloads the stale file and re-records the outage rows ONCE (the accepted
|
|
172
|
+
* bounded-duplicate mode — the ledger row above was already appended). One
|
|
173
|
+
* warn names the actual state (durable cursor NOT advanced).
|
|
174
|
+
*
|
|
175
|
+
* Exported as the degraded-path test seam: {@link withWorkflowDirLock}'s
|
|
176
|
+
* `lockOpts` (`timeoutMs`) lets a test shorten the otherwise-30 s wait —
|
|
177
|
+
* the consumer itself cannot reach the catch (the ledger append takes the
|
|
178
|
+
* SAME lock and would fail first), so the post-append window is driven
|
|
179
|
+
* directly.
|
|
180
|
+
*
|
|
181
|
+
* @param isEvictable - `true` for a candidate session that is safe to evict
|
|
182
|
+
* (not live in the sessions store).
|
|
183
|
+
* @param lockOpts - pass-through to {@link withWorkflowDirLock}
|
|
184
|
+
* (`timeoutMs` / `pollMs` overrides; production callers never set them).
|
|
185
|
+
*/
|
|
186
|
+
export declare function advanceWatermark(workflowDir: string, sid: string, nextSeq: number, isEvictable: (candidate: string) => boolean, lockOpts?: {
|
|
187
|
+
timeoutMs?: number;
|
|
188
|
+
pollMs?: number;
|
|
189
|
+
}): void;
|
|
120
190
|
/**
|
|
121
191
|
* Register the workflow-ledger consumer: (1) a `session/created` backfill
|
|
122
192
|
* listener (registered FIRST — qc3 S-305 — so no apply-time window exists
|
|
@@ -126,7 +196,8 @@ export declare function setWorkflowLedgerLogger(sink: WorkflowLedgerLogSink): Wo
|
|
|
126
196
|
* events never hit the firehose, `firstLiveSeq`); (3) a live
|
|
127
197
|
* `ctx.events.on('session/event', …)` listener filtering the four types.
|
|
128
198
|
* One DURABLE watermark per session id (session-log `seq` position,
|
|
129
|
-
* persisted to `{HARNESS_DIR}/workflow-ledger-cursors.json`
|
|
199
|
+
* persisted to `{HARNESS_DIR}/workflows/<id>/workflow-ledger-cursors.json` —
|
|
200
|
+
* the ACTIVE workflow dir, never the root) — re-applies
|
|
130
201
|
* never duplicate; no other cache. The watermark advances only AFTER a
|
|
131
202
|
* successful ledger append (qc3 R-401 — a failing append leaves the cursor
|
|
132
203
|
* behind so the row is re-attempted at the next scan, never lost). Every
|
|
@@ -189,7 +189,8 @@ export type WorkflowPolicyVerdict = {
|
|
|
189
189
|
* before returning; `markAsked` never throws. NEVER throws.
|
|
190
190
|
*
|
|
191
191
|
* P-b: `input.uncoveredPlanId` (the dispatch gate computes it from the
|
|
192
|
-
*
|
|
192
|
+
* ACTIVE workflow snapshot — `workflows/<id>/snapshot.json`, the v3
|
|
193
|
+
* status.json consumer; see dispatch.ts `writableFanOutUncovered`)
|
|
193
194
|
* → hard denies (reason cites the plan id), warn/ask emit the advisory
|
|
194
195
|
* verdict; `off` always allows. Preempts P-a/P-c: an orphan InProgress plan
|
|
195
196
|
* is a workspace red line independent of the workflow name.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { WorkflowSelectionView } from '../types.ts';
|
|
2
|
+
/** The active-set resolver result: the first active lifecycle or a clear error. */
|
|
3
|
+
export type ActiveWorkflowSelection = WorkflowSelectionView;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the ACTIVE lifecycle set (root v2 `status.json` `workflows[]` —
|
|
6
|
+
* the list holds non-terminal lifecycles only, removal-at-terminal). This
|
|
7
|
+
* is the ONLY resolver the agent-flow writer / ledger may use: no active
|
|
8
|
+
* entry → a clear error, never a terminal snapshot and never the root v1
|
|
9
|
+
* file.
|
|
10
|
+
*
|
|
11
|
+
* Active-set definition (explicit decision, plan `20260819-workflow-dsh-viz`
|
|
12
|
+
* Task 2): membership in `workflows[]` — the engine lifecycle enum's
|
|
13
|
+
* non-terminal states are `running` AND `paused` (terminal lifecycles are
|
|
14
|
+
* removed from the list at terminal). A PAUSED lifecycle therefore stays in
|
|
15
|
+
* the active set and the agent-flow writer / ledger append to its workflow
|
|
16
|
+
* dir (a paused lifecycle is still the operator's current lifecycle — its
|
|
17
|
+
* ledger must keep recording; only a TERMINAL lifecycle is never a write
|
|
18
|
+
* target).
|
|
19
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveActiveWorkflow(harnessDir: string): ActiveWorkflowSelection;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the workflow the catalog/panel READ path aggregates (compass
|
|
24
|
+
* v3.0.0 § Catalog selection rule): active `workflows[]` first (multiple
|
|
25
|
+
* active → first + a structured warning), else the latest terminal
|
|
26
|
+
* snapshot by mtime (history view), else a clear error. Never reads the
|
|
27
|
+
* root v1 `plans[]` / root `agent-flow.jsonl` as primary or as a quiet
|
|
28
|
+
* fallback.
|
|
29
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveReadWorkflow(harnessDir: string): WorkflowSelectionView;
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { DshHostAdapter } from './gates/adapter.ts';
|
|
|
16
16
|
import type { DispatchGateAdvisory } from './gates/dispatch.ts';
|
|
17
17
|
export { DshMstar } from './service.ts';
|
|
18
18
|
export type { DshMstarOptions } from './service.ts';
|
|
19
|
-
export type { MstarEngineStatusSource, MstarHarnessState, MstarIterationGateView, AgentFlowEventView, AgentFlowSummaryRow, AgentFlowView, } from './types.ts';
|
|
19
|
+
export type { MstarEngineStatusSource, MstarHarnessState, MstarIterationGateView, AgentFlowEventView, AgentFlowSummaryRow, AgentFlowView, WorkflowSelectionView, } from './types.ts';
|
|
20
20
|
export { AGENT_FLOW_FILE, AGENT_FLOW_MAX_EVENTS, SETTLE_SEAM, readAgentFlow, recordDispatch, recordSettle, recordWorkflowVerdict, } from './gates/agent-flow.ts';
|
|
21
21
|
export type { AgentFlowEvent, DispatchVerdict, SettleOutcome, WorkflowGateMode, WorkflowVerdict, WorkflowVerdictInput, } from './gates/agent-flow.ts';
|
|
22
22
|
export { Config, HarnessResolver, skillLocalConfig } from './gates/_shared.ts';
|