@mstar-harness/dsh 2.2.0 → 2.3.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.
Files changed (62) hide show
  1. package/README.i18n.yaml +2 -3
  2. package/README.md +118 -3
  3. package/README.zh.md +118 -3
  4. package/bundle/README.md +11 -1
  5. package/dist/client/panel/graph/event-log.d.ts +18 -3
  6. package/dist/client/panel/graph/project-graph.d.ts +40 -14
  7. package/dist/client/panel/locale.d.ts +1 -1
  8. package/dist/client.js +131 -17
  9. package/dist/gates/_shared.d.ts +104 -5
  10. package/dist/gates/adapter.d.ts +34 -1
  11. package/dist/gates/agent-flow.d.ts +186 -4
  12. package/dist/gates/agent-personas.d.ts +44 -0
  13. package/dist/gates/dispatch.d.ts +64 -2
  14. package/dist/gates/fallbacks-advisory.d.ts +77 -0
  15. package/dist/gates/fallbacks-decoration.d.ts +108 -0
  16. package/dist/gates/fallbacks-probe.d.ts +63 -0
  17. package/dist/gates/fallbacks-seeds.d.ts +98 -0
  18. package/dist/gates/goal-bridge.d.ts +151 -0
  19. package/dist/gates/plan-mode-bridge.d.ts +70 -0
  20. package/dist/gates/skill-lint.d.ts +4 -3
  21. package/dist/gates/system-prompt.d.ts +121 -0
  22. package/dist/gates/workflow-ledger.d.ts +149 -0
  23. package/dist/gates/workflow-policy.d.ts +203 -0
  24. package/dist/index.d.ts +10 -4
  25. package/dist/index.js +1833 -60
  26. package/dist/types.d.ts +30 -5
  27. package/harness-agents/architect.md +35 -0
  28. package/harness-agents/code-reviewer.md +98 -0
  29. package/harness-agents/frontend-dev.md +33 -0
  30. package/harness-agents/fullstack-dev-2.md +33 -0
  31. package/harness-agents/fullstack-dev.md +33 -0
  32. package/harness-agents/ops-engineer.md +33 -0
  33. package/harness-agents/product-manager.md +35 -0
  34. package/harness-agents/project-manager.md +32 -0
  35. package/harness-agents/prompt-engineer.md +33 -0
  36. package/harness-agents/qa-engineer.md +35 -0
  37. package/harness-agents/qc-specialist-2.md +62 -0
  38. package/harness-agents/qc-specialist-3.md +62 -0
  39. package/harness-agents/qc-specialist.md +62 -0
  40. package/harness-agents/writing-specialist.md +35 -0
  41. package/harness-commands/codebase-audit.md +2 -2
  42. package/harness-skills/mstar-audit/SKILL.md +20 -0
  43. package/harness-skills/mstar-audit/references/audit-playbook.md +27 -0
  44. package/harness-skills/mstar-branch-worktree/SKILL.md +7 -0
  45. package/harness-skills/mstar-coding-behavior/SKILL.md +5 -0
  46. package/harness-skills/mstar-compound/references/compound-workflow.md +16 -0
  47. package/harness-skills/mstar-compound-refresh/SKILL.md +5 -0
  48. package/harness-skills/mstar-iteration/references/phase-4-5-pr-delivery.md +1 -0
  49. package/harness-skills/mstar-plan-artifacts/SKILL.md +1 -0
  50. package/harness-skills/mstar-plan-artifacts/references/status-and-residuals.md +8 -0
  51. package/harness-skills/mstar-review-qc/SKILL.md +6 -0
  52. package/harness-skills/mstar-roles/references/project-manager/qc-and-residuals.md +2 -1
  53. package/harness-skills/mstar-roles/references/qc-specialist/deep-review-lenses.md +16 -7
  54. package/harness-skills/mstar-roles/references/qc-specialist/report-template.md +17 -1
  55. package/harness-skills/mstar-roles/references/qc-specialist-shared.md +3 -1
  56. package/harness-skills/mstar-roles/references/writing-specialist.md +33 -0
  57. package/harness-skills/mstar-sdd/SKILL.md +5 -1
  58. package/harness-skills/mstar-sdd/references/file-handoffs.md +2 -0
  59. package/harness-skills/mstar-skill-authoring/SKILL.md +4 -2
  60. package/harness-skills/mstar-skill-authoring/references/regression-fixation.md +46 -0
  61. package/harness-skills/mstar-skill-authoring/references/skillsbench-authoring.md +37 -0
  62. package/package.json +7 -4
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Workflow/ralph gate policy — the P-a name allowlist + P-b lease
3
+ * attribution + P-c first-seen ask (plan `20260815-dsh-workflow-gate`
4
+ * Tasks 2–3, W-B3). The policy is the SINGLE
5
+ * decision point for the four-tier `workflowGate` mode semantics
6
+ * (`off | warn | ask | hard`, default `warn`): it turns one composed
7
+ * {@link WorkflowGateInput} (Task 1) into `allow | warn | ask | deny` + a
8
+ * reason. The dispatch-gate listener maps the verdict to the
9
+ * `tools/pre-execute` refusal vocabulary (`PreToolDecision`) — this module
10
+ * NEVER throws and NEVER builds an approval path: an `ask` verdict flows
11
+ * through dsh's own approval waterfall (fail-closed upstream, this gate
12
+ * invents no answerer).
13
+ *
14
+ * Policy matrix (mode × P-a/P-b/P-c):
15
+ *
16
+ * | input | mode | verdict |
17
+ * |---|---|---|
18
+ * | uncovered InProgress plan (P-b) | `off` | allow — the gate short-circuits `off` before the policy; kept here for a total policy |
19
+ * | uncovered InProgress plan (P-b) | `hard` | deny — reason cites the uncovered plan id, veto before any child starts |
20
+ * | uncovered InProgress plan (P-b) | `warn`/`ask` | warn — allowed + advisory verdict + one warn (the ask channel is for first-seen NAMES, never the workspace red line) |
21
+ * | ralph (no `meta.name`), covered/read-only | any | allow — P-a/P-c NEVER apply to ralph (no allowlist identity); P-b applies |
22
+ * | workflow, name ∈ `workflowNames` (non-empty list), covered | any | allow — the allowlist passes under every mode |
23
+ * | workflow, unknown (empty/absent list ⇒ EVERY name unknown), covered | `off` | allow — the gate short-circuits `off` before the policy; kept here for a total policy |
24
+ * | workflow, unknown, covered | `warn` | warn — advisory + one warn (Task 1 behavior, now centralized) |
25
+ * | workflow, unknown, covered | `hard` | deny — reason names the workflow name, veto before any child starts |
26
+ * | workflow, unknown, covered, first-seen (no cached decision) | `ask` | ask — `{kind:'ask'}` through the approval waterfall + the name is marked asked (W-1 — the run-start observation promotes only asked names to allow) |
27
+ * | workflow, unknown, covered, cached allow | `ask` | allow — cached decision, NO re-ask |
28
+ * | workflow, unknown, covered, cached deny | `ask` | deny — cached decision, NO re-ask |
29
+ *
30
+ * P-c cache lifecycle: the {@link WorkflowAskCache} is apply-scoped — created
31
+ * per plugin `apply` (owned by the host adapter, constructed with it) and
32
+ * closed over by the listener; there is NO module-level Map, so disposal
33
+ * (fiber teardown / HMR reload) makes the cache unreachable with the apply
34
+ * and a fresh apply starts with an empty cache. The cache records only
35
+ * RESOLVED decisions (`allow` | `deny`) keyed by workflow name.
36
+ *
37
+ * Cache-key normalization (Task 5 fold-in — the Task-4 Important congruence
38
+ * fix): every key — the gate's `metaName` (composed through
39
+ * {@link normalizeWorkflowName} in dispatch.ts `workflowGateInputOf`), the
40
+ * run-start observation's `runName` (workflow-ledger.ts), and the explicit
41
+ * `record()` / `markAsked()` APIs (which normalize internally, F-302) — is
42
+ * the ASCII-control-char-stripped, UNCAPPED name. One shared function, both
43
+ * seams; a control-char name (`au\u0000dit`) gates and observes under the
44
+ * same normalized key (`audit`), and the length is never capped on the
45
+ * identity axis (the ledger ROW display field is capped separately — the
46
+ * cache key stays the full name, matching the gate).
47
+ *
48
+ * Answer-recording seam (`WorkflowAskCache.record` + W-1 `markAsked`): the
49
+ * gate CANNOT observe the ask outcome directly — the tool registry's
50
+ * `serviceAsk` consumes the approval result internally (`deepseek-harness
51
+ * packages/core/tools/src/index.ts`, `prepareExecution`/`serviceAsk`) — so
52
+ * the ANSWER reaches the cache through the workflow-ledger consumer's
53
+ * run-start observation (plan `20260815-dsh-workflow-gate` Task 4 fold-in —
54
+ * the Task-2 Important handoff): an ALLOWED ask executes the call, the
55
+ * durable `tool-workflow/run-start` session event carries the run name, and
56
+ * the consumer records `allow` for it (`workflow-ledger.ts`). The policy
57
+ * marks every name that received an `ask` verdict in this apply
58
+ * (`markAsked`, at the single ask point), and the observation promotes ONLY
59
+ * marked names to `allow` — a run observed WITHOUT a prior ask (a P-b
60
+ * advisory under `ask` mode, a `warn`/`off`-mode run) is NOT an approval
61
+ * resolution and never pre-authorizes the name (qc2 W-1). A DENIED answer
62
+ * produces no run → no observation → the next same-name call re-asks
63
+ * (fail-closed — no grant evidence, never an invented allow). The explicit
64
+ * `record()` API stays the general seam — an answerer integration or the
65
+ * tests may drive it directly, and an unresolved first-seen re-asks per
66
+ * call until the observation (or an explicit record) lands.
67
+ *
68
+ * Module boundary: no barrel. This module imports dispatch.ts TYPE-ONLY
69
+ * (`WorkflowGateInput` — erased at runtime, no cycle); dispatch.ts imports
70
+ * the policy/cache values from here. The P-a vocabulary
71
+ * (`workflowNameUnknown`, `WORKFLOW_NAME_UNKNOWN_CODE`) centralized HERE from
72
+ * Task 1's dispatch.ts.
73
+ */
74
+ import type { Config } from './_shared.ts';
75
+ import type { WorkflowGateInput } from './dispatch.ts';
76
+ /**
77
+ * The workflow-gate advisory/deny violation code for a name outside the P-a
78
+ * allowlist (warn-mode advisory / hard-mode veto reason — Task 2/3 policies
79
+ * share this vocabulary).
80
+ */
81
+ export declare const WORKFLOW_NAME_UNKNOWN_CODE = "workflow.name.unknown";
82
+ /**
83
+ * The workflow-gate advisory/deny violation code for P-b lease attribution
84
+ * (Task 3): the calling workspace has an `InProgress` plan without
85
+ * `execution_lease` coverage (warn-mode advisory / hard-mode veto reason —
86
+ * the reason cites the uncovered plan id).
87
+ */
88
+ export declare const WORKFLOW_LEASE_UNCOVERED_CODE = "workflow.lease.uncovered";
89
+ /**
90
+ * Strip ASCII control characters (newlines / tabs / CR — the log-forging
91
+ * surface, qc2 S-1) from one workflow name. THE shared P-c cache-key
92
+ * normalization (plan `20260815-dsh-workflow-gate` Task 5 fold-in — the
93
+ * Task-4 Important congruence fix): the gate's `metaName` (composed in
94
+ * dispatch.ts `workflowGateInputOf`) and the run-start observation's
95
+ * `runName` (workflow-ledger.ts) MUST key the ask cache through the SAME
96
+ * function — a raw-vs-stripped mismatch (e.g. `au\u0000dit` gating under
97
+ * one key while the observation records another) would re-ask a resolved
98
+ * name forever. Pure — NEVER throws. The ledger's other display fields
99
+ * (`label` / `phase`) use the same strip; ID-sized fields (`runId` /
100
+ * `childId`) are NOT routed here — they keep their skip-if-oversized
101
+ * semantics.
102
+ */
103
+ export declare function normalizeWorkflowName(value: string): string;
104
+ /**
105
+ * P-a allowlist membership: a workflow name is UNKNOWN when `workflowNames`
106
+ * is absent or empty (⇒ every name unknown — documented, the gate is NOT
107
+ * "allow all" by omission) or the name is not listed. Ralph calls carry no
108
+ * `meta.name` — P-a never applies to them (callers guard on
109
+ * `input.metaName !== undefined` first).
110
+ *
111
+ * Comparison boundary (qc1-S2): entries are normalized through
112
+ * {@link normalizeWorkflowName} BEFORE the comparison — the gate's
113
+ * `metaName` is already stripped, so an operator-pasted control-char
114
+ * variant (trailing newline, copied config value) matches the same
115
+ * identity instead of silently degrading to unknown. The operator's config
116
+ * array is NEVER mutated — the normalization happens at the boundary only.
117
+ */
118
+ export declare function workflowNameUnknown(config: Config, metaName: string): boolean;
119
+ /** One workflow name's resolved P-c decision (the cache value). */
120
+ export type WorkflowAskCacheDecision = 'allow' | 'deny';
121
+ /**
122
+ * The P-c per-session first-seen cache: workflow name → resolved decision.
123
+ * Apply-scoped (one instance per plugin `apply`, owned by the host adapter —
124
+ * see the module doc for the lifecycle). Records ONLY resolved decisions; a
125
+ * miss means first-seen (or an unanswered ask) → the policy asks again.
126
+ *
127
+ * W-1 (qc2 fix-wave): the cache ALSO tracks which names received an `ask`
128
+ * verdict in this apply ({@link markAsked} — the gate marks EVERY ask
129
+ * decision at the policy's single ask point). The run-start observation
130
+ * (workflow-ledger.ts) records `allow` ONLY for names marked-asked — a run
131
+ * that happened without an ask (P-b advisory under `ask` mode, a
132
+ * `warn`/`off`-mode run) is NOT an approval resolution and must not
133
+ * pre-authorize the name.
134
+ *
135
+ * F-302 (qc3 fix-wave): the WRITE seams ({@link record} / {@link markAsked})
136
+ * normalize their keys internally — the "ONE shared normalization" contract
137
+ * holds even for a caller that passes a raw spelling (today's production
138
+ * callers already normalize before calling; this is defense-in-depth for the
139
+ * documented answerer-integration seam). {@link get} stays a verbatim read —
140
+ * the gate always passes the normalized `metaName`, and the raw spelling is
141
+ * never stored (test 13 pins that).
142
+ */
143
+ export declare class WorkflowAskCache {
144
+ private readonly decisions;
145
+ /** Names that received an `ask` verdict in this apply (W-1 — the observation promotes ONLY asked names to allow). */
146
+ private readonly asked;
147
+ /** The resolved decision for `name`, or undefined when never resolved. */
148
+ get(name: string): WorkflowAskCacheDecision | undefined;
149
+ /**
150
+ * Record the approval-flow outcome for `name` (the ask's ANSWER — the
151
+ * answerer integration's seam; see the module doc). Subsequent same-name
152
+ * calls under `ask` reuse the cached decision without re-asking. The key
153
+ * is normalized internally (F-302).
154
+ */
155
+ record(name: string, decision: WorkflowAskCacheDecision): void;
156
+ /**
157
+ * Mark `name` as having received an `ask` verdict in this apply (W-1).
158
+ * Called by the policy on EVERY ask decision, BEFORE the verdict is
159
+ * returned — the run-start observation promotes only marked names to
160
+ * `allow`. The key is normalized internally.
161
+ */
162
+ markAsked(name: string): void;
163
+ /** Whether `name` received an `ask` verdict in this apply (the observation's promote gate). Normalizes internally. */
164
+ wasAsked(name: string): boolean;
165
+ }
166
+ /** The four-tier policy decision vocabulary (plan W-B3, Task 2). */
167
+ export type WorkflowPolicyDecision = 'allow' | 'warn' | 'ask' | 'deny';
168
+ /**
169
+ * One policy verdict: the decision + a human-readable reason + the violation
170
+ * code for the warn/ask/deny vocabulary (the caller emits `code` in the
171
+ * advisory row — the gate never guesses which policy fired).
172
+ */
173
+ export type WorkflowPolicyVerdict = {
174
+ decision: 'allow';
175
+ reason?: undefined;
176
+ code?: undefined;
177
+ } | {
178
+ decision: 'warn' | 'ask' | 'deny';
179
+ reason: string;
180
+ code: string;
181
+ };
182
+ /**
183
+ * The workflow/ralph gate policy — P-a name allowlist + P-c first-seen ask
184
+ * (plan `20260815-dsh-workflow-gate` Task 2). `config` + cache + composed
185
+ * input → verdict; the caller (dispatch gate) maps the verdict to the
186
+ * `PreToolDecision` refusal vocabulary and owns the advisory emit/log
187
+ * infrastructure. The ONE cache write is contained: an `ask` verdict marks
188
+ * the name via `WorkflowAskCache.markAsked` (W-1 — see the cache doc)
189
+ * before returning; `markAsked` never throws. NEVER throws.
190
+ *
191
+ * P-b: `input.uncoveredPlanId` (the dispatch gate computes it from the
192
+ * calling workspace's status.json — see dispatch.ts `writableFanOutUncovered`)
193
+ * → hard denies (reason cites the plan id), warn/ask emit the advisory
194
+ * verdict; `off` always allows. Preempts P-a/P-c: an orphan InProgress plan
195
+ * is a workspace red line independent of the workflow name.
196
+ * P-a: `metaName ∈ workflowNames` (empty/absent list ⇒ every name unknown);
197
+ * allowlisted → allow under ANY mode. Unknown → per-mode: `warn` → advisory
198
+ * verdict; `hard` → deny (reason names the workflow name); `ask` → first-seen
199
+ * `{kind:'ask'}` verdict, afterwards the cached decision (allow/deny), never
200
+ * a re-ask for a resolved name. Ralph carries no `meta.name` — P-a/P-c never
201
+ * apply to it (P-b applies).
202
+ */
203
+ export declare function workflowPolicy(config: Config, cache: WorkflowAskCache, input: WorkflowGateInput): WorkflowPolicyVerdict;
package/dist/index.d.ts CHANGED
@@ -17,8 +17,8 @@ import type { DispatchGateAdvisory } from './gates/dispatch.ts';
17
17
  export { DshMstar } from './service.ts';
18
18
  export type { DshMstarOptions } from './service.ts';
19
19
  export type { MstarEngineStatusSource, MstarHarnessState, MstarIterationGateView, AgentFlowEventView, AgentFlowSummaryRow, AgentFlowView, } from './types.ts';
20
- export { AGENT_FLOW_FILE, AGENT_FLOW_MAX_EVENTS, SETTLE_SEAM, readAgentFlow, recordDispatch, recordSettle, } from './gates/agent-flow.ts';
21
- export type { AgentFlowEvent, DispatchVerdict, SettleOutcome } from './gates/agent-flow.ts';
20
+ export { AGENT_FLOW_FILE, AGENT_FLOW_MAX_EVENTS, SETTLE_SEAM, readAgentFlow, recordDispatch, recordSettle, recordWorkflowVerdict, } from './gates/agent-flow.ts';
21
+ export type { AgentFlowEvent, DispatchVerdict, SettleOutcome, WorkflowGateMode, WorkflowVerdict, WorkflowVerdictInput, } from './gates/agent-flow.ts';
22
22
  export { Config, HarnessResolver, skillLocalConfig } from './gates/_shared.ts';
23
23
  export type { StatusGateAdvisory } from './gates/status.ts';
24
24
  export { SkillLintVetoError, lintSkillDoc, lintSkillWrite } from './gates/skill-lint.ts';
@@ -26,14 +26,20 @@ export type { SkillLintAdvisory } from './gates/skill-lint.ts';
26
26
  export { SeamVetoError, lintSeamWrite, lintDesignMdWrite, lintAuditWrite, lintCompoundWrite, lintRolesWrite } from './gates/seams.ts';
27
27
  export type { SeamId, SeamLintAdvisory } from './gates/seams.ts';
28
28
  export type { DispatchGateAdvisory } from './gates/dispatch.ts';
29
+ export { DECORATION_LOGGER, PERSONA_SECTION_NAME, PERSONA_SECTION_ORDER, decorateSubagentStart, setDecorationAgentsDir, setDecorationLogger, } from './gates/fallbacks-decoration.ts';
30
+ export type { DecorationLogLevel, DecorationLogSink, SubagentRunInfoView } from './gates/fallbacks-decoration.ts';
31
+ export { ADVISORY_LOGGER, runFallbacksAdvisory, setAdvisoryLogger } from './gates/fallbacks-advisory.ts';
32
+ export type { AdvisoryLogLevel, AdvisoryLogSink } from './gates/fallbacks-advisory.ts';
29
33
  export { DshHostAdapter } from './gates/adapter.ts';
30
34
  export type { DshHostAdapterOptions } from './gates/adapter.ts';
31
35
  /** Cordis function-plugin name registered by the Loader. */
32
36
  export declare const name = "dsh";
33
37
  /**
34
38
  * Services required before this plugin's `apply` fiber starts.
35
- * Empty for the scaffold: the plan's gates register on events (`fs/write-intent`,
36
- * `tools/pre-execute`), not on injected services; `inject` grows if a service seam is needed.
39
+ * `loader` the dsh profile loader's entry tree is guaranteed before
40
+ * apply (the plugin row is composed BY the loader), so the Task 1 probe can
41
+ * read `ctx.loader.entries()` at decision points (plugin-inventory
42
+ * precedent; no runtime dependency on `@deepseek-ai/cordis-plugin-loader`).
37
43
  */
38
44
  export declare const inject: string[];
39
45
  declare module '@deepseek-ai/cordis' {