@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
@@ -17,6 +17,44 @@ export declare const AGENT_FLOW_DEFAULT_LIMIT = 50;
17
17
  * small-file append path free of a full read per dispatch).
18
18
  */
19
19
  export declare const AGENT_FLOW_SIZE_GATE_BYTES: number;
20
+ /**
21
+ * WORKFLOW field length caps (qc2 W-3 fix-wave) — ONE constant family at
22
+ * the ledger boundary, enforced on BOTH the consumer (workflow-ledger
23
+ * `rowOf`) and the read narrow (`eventFromUnknown`): a hostile or
24
+ * model-controlled multi-MB string must never defeat the
25
+ * `AGENT_FLOW_MAX_EVENTS` line-count truncation or reach the panel
26
+ * unbounded. ID-sized fields (`runId`, `childId`) SKIP the row when
27
+ * oversized — truncating them could forge collisions; display fields
28
+ * (`name`, `label`, `phase`) are truncated deterministically with a suffix
29
+ * marker. `2^31` bounds every sequence number (envelope + member) — the
30
+ * cursor-math safe range (see the consumer's durable watermark).
31
+ */
32
+ export declare const WORKFLOW_LEDGER_MAX_ID_LENGTH = 512;
33
+ /** Cap for label-sized display fields (`label`, `phase`). */
34
+ export declare const WORKFLOW_LEDGER_MAX_LABEL_LENGTH = 512;
35
+ /** Cap for the run display `name`. */
36
+ export declare const WORKFLOW_LEDGER_MAX_NAME_LENGTH = 1024;
37
+ /** The deterministic suffix marker appended to capped display fields. */
38
+ export declare const WORKFLOW_LEDGER_TRUNCATION_MARKER = "\u2026";
39
+ /** Upper bound (exclusive) for envelope + member sequence numbers. */
40
+ export declare const WORKFLOW_LEDGER_MAX_SEQ: number;
41
+ /**
42
+ * Deterministically cap one display field: values at or under the cap pass
43
+ * through unchanged; longer values are truncated to `cap − marker` chars
44
+ * plus the {@link WORKFLOW_LEDGER_TRUNCATION_MARKER} suffix (the marker
45
+ * guarantees the truncation is visible in the panel — never a silent cut).
46
+ * CODE-POINT gated AND sliced (plan QC fix wave — qc2 S-5; PR #97 finding
47
+ * 2): `String.prototype.slice` operates on UTF-16 code units and can split
48
+ * a surrogate pair at the cap boundary (a lone surrogate renders as U+FFFD
49
+ * in the log line), and a UTF-16 `.length` gate would false-positive
50
+ * truncate astral-dense values (≤ cap code points but > cap UTF-16 units)
51
+ * then re-inflate the result to cap code points — up to ~2× the cap in the
52
+ * unit the gate measured. ONE `Array.from` drives both the gate and the
53
+ * slice, so the bound is measured and enforced in the same unit; a
54
+ * multi-byte character at the boundary is kept whole, at the cost of at
55
+ * most one code point of slack. Pure — NEVER throws.
56
+ */
57
+ export declare function truncateLedgerField(value: string, cap: number): string;
20
58
  /** Logger label for the agent-flow ledger (dsh logger naming: `<scope>/<subject>`). */
21
59
  export declare const AGENT_FLOW_LOGGER = "mstar/agent-flow";
22
60
  /**
@@ -46,10 +84,71 @@ export declare const SETTLE_SEAM_PAIRING_NOTE = "settle seam \"tools/post-execut
46
84
  export type DispatchVerdict = 'ok' | 'advisory' | 'denied';
47
85
  /** Settle outcome vocabulary (spec §2.1.3). */
48
86
  export type SettleOutcome = 'ok' | 'error' | 'denied';
87
+ /** Workflow run terminal reason (`tool-workflow` vocabulary — `workflow/src/types.ts:63`). */
88
+ export type WorkflowStopReason = 'completed' | 'cancelled' | 'error';
49
89
  /**
50
- * One v1 ledger event (spec §2.1.3 schema — the JSONL line). Optional fields
51
- * are OMITTED from the serialized line when absent (Session.append's lossless
52
- * JSON discipline starts at the record boundary).
90
+ * Workflow/ralph gate verdict vocabulary (plan `20260815-dsh-workflow-gate`
91
+ * Task 4): the RESOLVED outcomes reuse the dispatch verdict vocabulary
92
+ * (`ok`/`advisory`/`denied` the plan interface "the workflow verdict
93
+ * vocabulary"); `ask` is the PENDING-decision member — the first-seen ask
94
+ * itself is a gated call, so its row carries `ask` until the approval
95
+ * waterfall resolves it ("one ledger row per gated call").
96
+ */
97
+ export type WorkflowVerdict = DispatchVerdict | 'ask';
98
+ /**
99
+ * The workflow/ralph gate mode (Config `workflowGate` — plan
100
+ * `20260815-dsh-workflow-gate` Task 1), recorded on every verdict row.
101
+ * `off` rows never exist: the gate short-circuits `off` BEFORE the policy,
102
+ * so no verdict is produced.
103
+ */
104
+ export type WorkflowGateMode = 'off' | 'warn' | 'ask' | 'hard';
105
+ /**
106
+ * One v1 workflow ledger event (plan `20260815-dsh-workflow-ledger` Task 2 —
107
+ * the W-B2 schema). Produced from the durable `tool-workflow/*` session events
108
+ * by the Task 3 consumer (`run-start` → `workflow-run`, `agent-start` →
109
+ * `workflow-agent`, `run-end` → `workflow-run-end`; the upstream `agent-end`
110
+ * member outcome has no ledger kind). Optional fields (`agent` / `phase`) are
111
+ * OMITTED from the serialized line when absent (lossless-JSON discipline).
112
+ */
113
+ export type AgentFlowWorkflowEvent = {
114
+ v: 1;
115
+ ts: number;
116
+ kind: 'workflow-run';
117
+ /** The workflow run's stable id (upstream `runId`). */
118
+ runId: string;
119
+ /** The run's display name (upstream `name`). */
120
+ name: string;
121
+ /** The calling session's stable id, when carried. */
122
+ agent?: string;
123
+ } | {
124
+ v: 1;
125
+ ts: number;
126
+ kind: 'workflow-agent';
127
+ /** The workflow run's stable id (upstream `runId`). */
128
+ runId: string;
129
+ /** 1-based `agent()` call sequence within the run (upstream `seq`). */
130
+ seq: number;
131
+ /** The published member's display label (upstream `label`). */
132
+ label: string;
133
+ /** The member's phase, when carried (upstream `phase?`). */
134
+ phase?: string;
135
+ /** The published child session's stable id (upstream `childId`). */
136
+ childId: string;
137
+ } | {
138
+ v: 1;
139
+ ts: number;
140
+ kind: 'workflow-run-end';
141
+ /** The workflow run's stable id (upstream `runId`). */
142
+ runId: string;
143
+ /** Terminal run reason (upstream `stopReason`). */
144
+ stopReason: WorkflowStopReason;
145
+ };
146
+ /**
147
+ * One v1 ledger event (spec §2.1.3 + plan `20260815-dsh-workflow-ledger`
148
+ * Task 2 — the JSONL line). Optional fields are OMITTED from the serialized
149
+ * line when absent (Session.append's lossless JSON discipline starts at the
150
+ * record boundary). The three `workflow-*` kinds are the W-B2 addition —
151
+ * see {@link AgentFlowWorkflowEvent}.
53
152
  */
54
153
  export type AgentFlowEvent = {
55
154
  v: 1;
@@ -90,7 +189,25 @@ export type AgentFlowEvent = {
90
189
  role?: string;
91
190
  planId?: string;
92
191
  taskId?: string;
93
- };
192
+ } | {
193
+ v: 1;
194
+ ts: number;
195
+ kind: 'workflow-verdict';
196
+ /** The calling session's stable id, when carried. */
197
+ agent?: string;
198
+ /** The matched tool name (`'workflow'` | `'ralph'`). */
199
+ tool: 'workflow' | 'ralph';
200
+ /** `meta.name` for workflow calls — the P-a/P-c allowlist identity. */
201
+ workflow?: string;
202
+ /** `objective` for ralph calls — the ralph identity. */
203
+ objective?: string;
204
+ /** The effective `workflowGate` mode at decision time (never `off` — off records nothing). */
205
+ mode: WorkflowGateMode;
206
+ /** The gate verdict (ok/advisory/denied/ask). */
207
+ verdict: WorkflowVerdict;
208
+ /** The policy violation code (advisory/ask/denied rows only). */
209
+ code?: string;
210
+ } | AgentFlowWorkflowEvent;
94
211
  /**
95
212
  * The identity of one recorded dispatch, carried by the pairing store so a
96
213
  * later completion (post-execute settle / onJobDone terminal) can record a
@@ -225,6 +342,71 @@ export declare function recordSettle(input: {
225
342
  planId?: string;
226
343
  taskId?: string;
227
344
  }): void;
345
+ /**
346
+ * Record one workflow ledger event (plan `20260815-dsh-workflow-ledger` Task 2
347
+ * — the W-B2 schema). Fully try/catch-contained — NEVER throws into the
348
+ * session-event consumer (Task 3 wires it); a failing record logs only
349
+ * (`mstar/agent-flow`), so a ledger write never crashes or alters a workflow
350
+ * run (Global Constraint: observe-only events are never refusal channels).
351
+ * The event is serialized lossless — optional fields (`agent` / `phase`) are
352
+ * omitted when absent at the record boundary; the caller shapes the event
353
+ * from the durable `tool-workflow/*` session event (`ts` takes the envelope's
354
+ * `time`). Malformed input is a caller bug — the strict narrowing applies on
355
+ * READ (`eventFromUnknown`), never here.
356
+ * @param input - harness dir + the fully-shaped v1 workflow event.
357
+ * @returns `true` when the row was appended (the caller may durably advance
358
+ * its watermark); `false` on a contained append failure — the caller must
359
+ * leave the cursor behind so the row is re-attempted at the next scan
360
+ * (qc3 R-401: advance-then-record made a failed append permanent loss).
361
+ */
362
+ export declare function recordWorkflowEvent(input: {
363
+ harnessDir: string;
364
+ event: AgentFlowWorkflowEvent;
365
+ }): boolean;
366
+ /**
367
+ * Input for {@link recordWorkflowVerdict} — one gated workflow/ralph call's
368
+ * decision identity (plan `20260815-dsh-workflow-gate` Task 4: verdict +
369
+ * metaName/objective + mode).
370
+ */
371
+ export interface WorkflowVerdictInput {
372
+ /** The resolved `{HARNESS_DIR}` the row records into. */
373
+ harnessDir: string;
374
+ /** The in-flight tool call (structural read for the carrying session id). */
375
+ exec?: unknown;
376
+ /** The matched tool name (`'workflow'` | `'ralph'`). */
377
+ tool: 'workflow' | 'ralph';
378
+ /** `meta.name` for workflow calls (the P-a/P-c allowlist identity). */
379
+ workflow?: string;
380
+ /** `objective` for ralph calls (the ralph identity). */
381
+ objective?: string;
382
+ /** The effective `workflowGate` mode at decision time. */
383
+ mode: WorkflowGateMode;
384
+ /** The gate verdict. */
385
+ verdict: WorkflowVerdict;
386
+ /** The policy violation code (advisory/ask/denied rows only). */
387
+ code?: string;
388
+ }
389
+ /**
390
+ * Record one workflow/ralph gate verdict row (plan `20260815-dsh-workflow-gate`
391
+ * Task 4 — "one ledger row per gated workflow/ralph call" via the ledger
392
+ * plan's record path, the `workflow-verdict` kind). Fully
393
+ * try/catch-contained — NEVER throws into the gate (`gateWorkflow` calls
394
+ * this on EVERY policy decision); a failing record logs only
395
+ * (`mstar/agent-flow`), so a ledger write never crashes or alters a
396
+ * workflow call. The event is serialized lossless — optional fields
397
+ * (`agent` / `workflow` / `objective` / `code`) omit when absent; the
398
+ * display identity fields (`workflow` / `objective`) are capped at the
399
+ * ledger boundary (same discipline as the W-B2 name cap) and the ralph
400
+ * `objective` is routed through the SHARED `normalizeWorkflowName` strip at
401
+ * this write boundary (qc2 W-2 — control-char discipline parity with the
402
+ * name/label/phase fields; the gate already normalizes `workflow`). The
403
+ * JSONL line is line-safe either way (`JSON.stringify` escapes), but the
404
+ * panel view passes the objective through raw — a hostile objective
405
+ * (ESC/ANSI/NEL) must never reach it. Malformed input is a caller bug —
406
+ * the strict narrowing applies on READ (`eventFromUnknown`), never here.
407
+ * @param input - the gate's decision identity (see {@link WorkflowVerdictInput}).
408
+ */
409
+ export declare function recordWorkflowVerdict(input: WorkflowVerdictInput): void;
228
410
  /**
229
411
  * Read the agent-flow ledger as the catalog view (spec §2.1.3 — fix-wave
230
412
  * qc1 F-001 / qc2 F-6): the latest events first (bounded by `limit`) plus
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The role-id shape the mirror lookup accepts — upstream `ROLE_ID_PATTERN`
3
+ * semantics (`/^[a-z0-9-]{1,32}$/`, dsh-llm-fallbacks `src/config.ts`),
4
+ * implemented locally (no import-shape assumptions). The role id flows from
5
+ * the child's Assignment header (`Execute as`) into a filesystem path, so
6
+ * it is constrained to this shape BEFORE any path join: no `..`, no path
7
+ * separators, no absolute roots, bounded length. All 14 mirror stems comply.
8
+ */
9
+ export declare const ROLE_ID_PATTERN: RegExp;
10
+ /** One resolved persona: the text and its source. */
11
+ export interface PersonaResult {
12
+ text: string;
13
+ source: 'config' | 'default';
14
+ }
15
+ /** The lookup surface {@link personaFor} consumes. */
16
+ export interface PersonaLookup {
17
+ /** Config-sourced persona map (`rolePersonas`) — wins over the mirror default. */
18
+ rolePersonas?: Record<string, string> | null;
19
+ /** The mirror root (`harness-agents/`); absent → mirror defaults are skipped. */
20
+ agentsDir?: string;
21
+ }
22
+ /** Warn sink for extraction-time hazards (bound by the decoration to its warn channel). */
23
+ export type PersonaWarnSink = (message: string) => void;
24
+ /**
25
+ * The decoration's single persona lookup: `rolePersonas[roleId]` → mirror
26
+ * default → undefined. Pure — the mirror root is passed explicitly (the
27
+ * decoration supplies the apply-bound packaged root; tests supply fixtures).
28
+ *
29
+ * @param roleId - the mstar role id (Assignment `Execute as`).
30
+ * @param lookup - the config override map and the mirror root.
31
+ * @param warn - optional extraction-time warn sink (hazard defaults).
32
+ */
33
+ export declare function personaFor(roleId: string, lookup: PersonaLookup, warn?: PersonaWarnSink): PersonaResult | undefined;
34
+ /**
35
+ * The mstar role-id set for one mirror: the file stems of the shells
36
+ * eligible as subagent role defaults (`mode` absent-or-`subagent` — a
37
+ * `primary` shell like `project-manager` is excluded), sorted for
38
+ * deterministic warn listings. The adoption advisory (plan
39
+ * `20260815-dsh-fallbacks-personas` Task 4) derives its taxonomy reference
40
+ * from here — never hardcoded. Reads the mirror directory + each shell's
41
+ * frontmatter once per call (the advisory invokes it once per apply — no
42
+ * cache needed). Returns `[]` for an unreadable/absent mirror directory.
43
+ */
44
+ export declare function subagentRoleIds(agentsDir: string): string[];
@@ -8,12 +8,26 @@ import type { DshHostAdapter } from './adapter.ts';
8
8
  export declare const DISPATCH_LOGGER = "mstar/dispatch-gate";
9
9
  /**
10
10
  * Default delegation tool names the dispatch gate matches (tool-subagent
11
- * default id). Exported SHARED with the agent-flow settle pairing
11
+ * default id + its fork sibling — roadmap §9 W-B1: fork dispatches carry the
12
+ * same Assignment-shaped `{ description, prompt }` args and must be gated
13
+ * like `subagent`). Exported SHARED with the agent-flow settle pairing
12
14
  * (`registerSettleListener` matches the same tool set — plan
13
15
  * `20260811-panel-f4-timeliness` Task 1) so the default cannot drift between
14
16
  * the gate and the settle seam.
15
17
  */
16
- export declare const DEFAULT_DISPATCH_TOOLS: readonly ["subagent"];
18
+ export declare const DEFAULT_DISPATCH_TOOLS: readonly ["subagent", "subagent_fork"];
19
+ /**
20
+ * The fixed workflow/ralph tool names the workflow gate matches (plan
21
+ * `20260815-dsh-workflow-gate` — architect-verified): the workflow tool
22
+ * registers under Config-default name `'workflow'` and is RENAMEABLE per
23
+ * instance (`toolName`, `tool-workflow/src/index.ts:41`); `ralph` is a
24
+ * fixed name (`tool-ralph/src/index.ts:413`). A renamed instance is out
25
+ * of reach until its name is configured — the same documented caveat as
26
+ * `DEFAULT_DISPATCH_TOOLS`. The workflow tools are gated by THEIR OWN
27
+ * branch, never by addition to `DEFAULT_DISPATCH_TOOLS` (plan Global
28
+ * Constraint — that list stays `['subagent', 'subagent_fork']`).
29
+ */
30
+ export declare const DEFAULT_WORKFLOW_TOOLS: readonly ["workflow", "ralph"];
17
31
  /**
18
32
  * Advisory emitted on warn-mode dispatch-gate passes (the * `mstar/status-gate` decision reused for the dispatch gate — dsh's
19
33
  * `agent/status` lifecycle event stays untouched). Consumers (later tasks,
@@ -128,6 +142,54 @@ export declare function dispatchGateCore(config: Config, harnessDir: string | nu
128
142
  violations: ValidationResult[];
129
143
  writable: boolean | undefined;
130
144
  };
145
+ /**
146
+ * The workflow-gate input composed from one `workflow`/`ralph` tool call
147
+ * (plan `20260815-dsh-workflow-gate` Task 1 — consumed by the Task 2 P-a /
148
+ * P-c and Task 3 P-b policies): the tool name + the structural reads of
149
+ * `meta` (workflow) / `objective` (ralph) + the in-flight call.
150
+ */
151
+ export interface WorkflowGateInput {
152
+ /** The matched tool name (`'workflow'` | `'ralph'` — the gate name-guards on {@link DEFAULT_WORKFLOW_TOOLS} first). */
153
+ tool: 'workflow' | 'ralph';
154
+ /** `meta.name` for workflow calls — the P-a allowlist input; absent for ralph (no meta). */
155
+ metaName?: string;
156
+ /** `objective` for ralph calls (objective-based ledger logging, P-b); absent for workflow. */
157
+ objective?: string;
158
+ /** The in-flight tool call — Task 3 P-b lease attribution reads the calling agent/session off it. */
159
+ exec: ToolExecution;
160
+ /**
161
+ * P-b lease attribution (plan Task 3): the calling workspace's first
162
+ * `InProgress` plan lacking `execution_lease` coverage (computed by
163
+ * {@link writableFanOutUncovered} from the status.json read through the
164
+ * contained resolver path — `preExecuteListener` already resolved the
165
+ * harness dir from the calling agent's session workspace). Undefined → no
166
+ * uncovered plan (allow axis). Applies to workflow AND ralph (P-b needs no
167
+ * `meta.name`).
168
+ */
169
+ uncoveredPlanId?: string;
170
+ }
171
+ /**
172
+ * Compose the {@link WorkflowGateInput} from one workflow/ralph tool call's
173
+ * arguments — structural reads, NEVER throws (plan
174
+ * `20260815-dsh-workflow-gate` Task 1; args shapes architect-verified:
175
+ * workflow `{ script, meta: { name, description, whenToUse?, phases? },
176
+ * args? }` (`tool-workflow/src/index.ts:152-161`); ralph
177
+ * `{ objective, maxRounds?, maxHandoffChars? }`
178
+ * (`tool-ralph/src/index.ts:76,416-419`)). Returns undefined for malformed
179
+ * args — workflow without a non-empty string `meta.name`, ralph without a
180
+ * string `objective` — the fail-open + one-warn path.
181
+ *
182
+ * `meta.name` is NORMALIZED through {@link normalizeWorkflowName} (control
183
+ * chars stripped) BEFORE the empty check — the P-c cache-key congruence
184
+ * fold-in (plan Task 5): the run-start observation (workflow-ledger.ts)
185
+ * keys the ask cache with the SAME normalized name, so a control-char name
186
+ * (`au\u0000dit`) asks once and observes under one key instead of re-asking
187
+ * forever. The length is NEVER capped here (the gate's identity axis is
188
+ * uncapped — the ledger ROW display field is capped separately). A name
189
+ * that is ONLY control characters normalizes to '' → malformed (fail-open),
190
+ * matching the ledger's read boundary.
191
+ */
192
+ export declare function workflowGateInputOf(exec: ToolExecution): WorkflowGateInput | undefined;
131
193
  /**
132
194
  * `tools/pre-execute` listener. The waterfall refusal channel is the returned
133
195
  * decision: a deny is returned WITHOUT calling `next()` (short-circuits the
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Warn-only adoption advisory for the OPTIONAL `dsh-llm-fallbacks` plugin
3
+ * (plan `20260815-dsh-fallbacks-personas` Task 4 + `20260816-dsh-b4-seeds`
4
+ * Task 3): when the capability is mounted, ONE advisory pass per apply
5
+ * reports the deployment's fallbacks taxonomy state (bounded: ≤1 warn per
6
+ * category, logger `mstar/fallbacks-advisory`):
7
+ *
8
+ * - Service present (seeds-aware path): the decision point FIRST awaits the
9
+ * idempotent re-declare (`declareMstarSeeds`) — converging the boot
10
+ * dual-inject-child race window — then reads the EFFECTIVE state
11
+ * (`getEffectiveRoles`, sync) and reports per mstar role id:
12
+ * (i) missing row → missing warn; (ii) `seeded && !personaOverridden` →
13
+ * seeded (silent, one debug); (iii) `personaOverridden` → persona-
14
+ * overridden warn (ids + the revert entry: the `fallbacks/revert-seed`
15
+ * gateway / settings-card rollback button). Empty-persona warns fire only
16
+ * for non-seeded rows or overridden-empty rows. The declare outcome's
17
+ * skips/conflicts (upstream conflict code `'persona-source'`) merge into
18
+ * one warn. The legacy-keys check runs through the service's own
19
+ * `detectLegacyKeys` on the row config.
20
+ * - Loader-fallback path (no service): the structural read is preserved
21
+ * (`readRowConfig`/`readRolesList` — (b) missing ids / (c) empty personas
22
+ * over the raw `roles.list`); the legacy check is SKIPPED (never
23
+ * reimplemented) and no revert entry appears (no seeds surface).
24
+ *
25
+ * The mstar role-id set is derived from the `harness-agents/` mirror
26
+ * (`subagentRoleIds` — shell file stems filtered to `mode: subagent`),
27
+ * never hardcoded.
28
+ *
29
+ * Unreadable row config (absent field / non-object, or an unreadable
30
+ * `roles.list`) → skip + one debug log. Unmounted → the pass is not invoked
31
+ * (returns `false`, no logs). The advisory NEVER writes the fallbacks config
32
+ * — the read is read-only over the deployment's config layer (never the
33
+ * fallbacks plugin's module internals); the only write path is the
34
+ * idempotent seeds re-declare through the released seeds surface (no-delta
35
+ * → no settings write upstream). The advisory never throws (the caller's
36
+ * dispatch/apply flow is never affected).
37
+ *
38
+ * Module boundary: no barrel — the entry imports this module by explicit
39
+ * relative path (the decoration-module pattern).
40
+ */
41
+ import type { Context } from '@deepseek-ai/cordis';
42
+ /** Logger label for the adoption advisory (dsh logger naming: `<scope>/<subject>`). */
43
+ export declare const ADVISORY_LOGGER = "mstar/fallbacks-advisory";
44
+ /** Advisory log levels the module sink understands. */
45
+ export type AdvisoryLogLevel = 'debug' | 'warn';
46
+ /** Module-level advisory log sink — bound by `apply` to `ctx.logger(ADVISORY_LOGGER)` (decoration-module pattern). */
47
+ export type AdvisoryLogSink = (level: AdvisoryLogLevel, message: string) => void;
48
+ /**
49
+ * Bind the advisory log sink (the entry `apply` binds it to
50
+ * `ctx.logger(ADVISORY_LOGGER)`). Returns the PRIOR sink so a caller can
51
+ * restore it (test pattern: {@link setDecorationLogger}).
52
+ */
53
+ export declare function setAdvisoryLogger(sink: AdvisoryLogSink): AdvisoryLogSink;
54
+ /**
55
+ * Warn id-list cap: an id-list warn line lists at most this many ids before
56
+ * the `… and K more` suffix — a huge registry (thousands of rows) must not
57
+ * produce a multi-KB log line (plan QC fix wave S-cap). Exported for the
58
+ * suite's cap assertions; module surface only — the entry's frozen 47-name
59
+ * export surface deliberately does not re-export it.
60
+ */
61
+ export declare const ADVISORY_ID_LIST_CAP = 20;
62
+ /**
63
+ * One advisory pass: unmounted → not invoked (`false`, no logs); mounted →
64
+ * report the taxonomy adoption state (bounded: ≤1 warn per category). With
65
+ * the service present the pass is ASYNC: it awaits the idempotent re-declare
66
+ * before the effective-state readback (report determinism — the boot
67
+ * dual-inject-child race window is closed). Never throws — every failure
68
+ * mode degrades to skip + one debug/warn. Never writes the fallbacks config.
69
+ *
70
+ * @param ctx - the plugin's registrant context (the app composition root).
71
+ * @param agentsDir - the `harness-agents/` mirror root the mstar role-id set
72
+ * is derived from; absent → the taxonomy checks are skipped (one debug
73
+ * log; the legacy-keys check is mirror-independent and still runs).
74
+ * @returns `true` when the pass ran (mounted), `false` when unmounted — the
75
+ * caller (entry `apply`) uses the boolean for the one-pass-per-apply latch.
76
+ */
77
+ export declare function runFallbacksAdvisory(ctx: Context, agentsDir: string | undefined): Promise<boolean>;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Role-based subagent decoration at the `subagent/start` seam (plan
3
+ * `20260814-dsh-fallbacks-integration` Task 2).
4
+ *
5
+ * Decoration rides the `subagent/start` EMIT — NOT `tools/pre-execute`:
6
+ * tool args are deep-frozen snapshots and persona/`agentOptions` come from
7
+ * the tool-subagent's own Config, never call args. The listener is
8
+ * SYNCHRONOUS (the section must register before the child's first LLM call)
9
+ * and resolves the published child via `ctx.get('agents')?.get(info.id)` —
10
+ * documented in the `@deepseek-ai/dsh-subagent` event contract ("For
11
+ * in-process providers, `ctx.agents.get(info.id)` resolves during this
12
+ * notification"). The registered section is agent-scoped on `Agent.ctx`
13
+ * (contributions are agent-local and unwind on disposal — the
14
+ * hooks-claude-code precedent).
15
+ *
16
+ * Role identity uses the engine Assignment header grammar — the SAME
17
+ * parsers the dispatch gate uses (`assignmentHeaderRegion` +
18
+ * `parseAssignmentFields`) — over the child's seeded task prompt (the
19
+ * child session's first `user/message`). Persona lookup (plan
20
+ * `20260815-dsh-fallbacks-personas` Task 3) is the single
21
+ * {@link personaFor} surface — `Config.rolePersonas[executeAs]` →
22
+ * `harness-agents/` mirror default → skip (never gated on `roleMap` or on
23
+ * the fallbacks mounted state: unmounted fallbacks degrades to the same
24
+ * injection with exactly one debug log; probe at the decision point, no
25
+ * cache). `roleMap` is a taxonomy bridge for logging + future rule-driven
26
+ * interop only. The mirror root is bound at apply (`setDecorationAgentsDir`
27
+ * ← `packagedAgentsDir()`), package-relative so the shipped bundle works
28
+ * from any launch cwd.
29
+ *
30
+ * Degradation (the listener never throws — contained like the dispatch
31
+ * gate's degrade path): `agents` service absent → skip + one debug log
32
+ * (documented Known Limitation for compositions without dsh-agent);
33
+ * child unresolved / non-Assignment / role-unmatched → silent no-op
34
+ * (role-unmatched with the mirror present and no eligible shell stays
35
+ * silent; with NO mirror the config-miss path logs ONE debug per apply —
36
+ * case (e) semantics). A throwing log sink is contained inside the log
37
+ * helper itself (plan QC F-002) — the sink must not escape the listener
38
+ * either.
39
+ *
40
+ * Persona text is rendered by dsh system-prompt's STRICT `{{...}}`
41
+ * interpolation, so persona values MUST NOT contain `{{` paired with a
42
+ * later `}}` — the Config schema rejects such values at plugin mount (see
43
+ * `_shared.ts` `rolePersonas` / `PERSONA_INTERPOLATION_HAZARD`); a mirror
44
+ * default carrying the hazard is warned + skipped at extraction (never a
45
+ * boot throw).
46
+ *
47
+ * Module boundary: no barrel — the entry imports this module by explicit
48
+ * relative path and re-exports the public names verbatim.
49
+ */
50
+ import type { Context } from '@deepseek-ai/cordis';
51
+ import type { Config } from './_shared.ts';
52
+ /** Logger label for the subagent decoration (dsh logger naming: `<scope>/<subject>`). */
53
+ export declare const DECORATION_LOGGER = "mstar/subagent-decoration";
54
+ /** The decoration's system-prompt section name (agent-scoped on `Agent.ctx`). */
55
+ export declare const PERSONA_SECTION_NAME = "mstar:role-persona";
56
+ /** Prompt order of the persona section — renders right after the deployment persona slot (order 0). */
57
+ export declare const PERSONA_SECTION_ORDER = 1;
58
+ /**
59
+ * Structural view of the `subagent/start` emit payload the decoration
60
+ * consumes (`@deepseek-ai/dsh-subagent` `SubagentRunInfo` — the plugin
61
+ * carries no dsh-subagent dependency; same pattern as the probe's
62
+ * `LoaderEntryView` and agent-flow's `TaskDoneSnapshot`). Only `id` is
63
+ * consumed; the rest keeps the view faithful to the published contract.
64
+ */
65
+ export interface SubagentRunInfoView {
66
+ /** Unique identity shared with the paired terminal event. */
67
+ readonly runId: unknown;
68
+ /** Provider name recorded when the child was first created. */
69
+ readonly provider: string;
70
+ /** The child agent's id. */
71
+ readonly id: string;
72
+ /** Snapshot of whether the run's local agent was present when start fulfilled. */
73
+ readonly local: boolean;
74
+ }
75
+ /** Decoration log levels the module sink understands. */
76
+ export type DecorationLogLevel = 'debug' | 'info' | 'warn';
77
+ /** Module-level decoration log sink — bound by `apply` to `ctx.logger(DECORATION_LOGGER)` (agent-flow ledger precedent). */
78
+ export type DecorationLogSink = (level: DecorationLogLevel, message: string) => void;
79
+ /**
80
+ * Bind the decoration log sink (the entry `apply` binds it to
81
+ * `ctx.logger(DECORATION_LOGGER)`). Returns the PRIOR sink so a caller can
82
+ * restore it (test pattern: agent-flow `setAgentFlowLogger`).
83
+ */
84
+ export declare function setDecorationLogger(sink: DecorationLogSink): DecorationLogSink;
85
+ /**
86
+ * Bind the persona-defaults mirror root. Returns the PRIOR binding so a
87
+ * caller can restore it (test pattern: {@link setDecorationLogger}).
88
+ * @param dir - the mirror root, or `undefined` to disable mirror defaults.
89
+ */
90
+ export declare function setDecorationAgentsDir(dir: string | undefined): string | undefined;
91
+ /**
92
+ * Decorate one `subagent/start` emit: resolve the child, extract its seeded
93
+ * task prompt, and — when the prompt is Assignment-shaped and
94
+ * `rolePersonas[executeAs]` is configured — register the persona as the
95
+ * child's `mstar:role-persona` system-prompt section (agent-scoped on
96
+ * `Agent.ctx`, unwinds on child disposal). Synchronous by design: the
97
+ * section must register before the child's first LLM call.
98
+ *
99
+ * Never throws — every failure mode degrades (skip + one log line at most),
100
+ * matching the dispatch gate's contained degrade path; the dispatch itself
101
+ * is never affected.
102
+ *
103
+ * @param ctx - the plugin's registrant context (the app composition root).
104
+ * @param config - validated plugin configuration (`rolePersonas` is the only
105
+ * payload source; `roleMap` is never consulted for injection).
106
+ * @param info - the `subagent/start` emit payload.
107
+ */
108
+ export declare function decorateSubagentStart(ctx: Context, config: Config, info: SubagentRunInfoView): void;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Capability probes for the OPTIONAL `dsh-llm-fallbacks` plugin (plan
3
+ * `20260814-dsh-fallbacks-integration` Task 1 — probe foundation).
4
+ *
5
+ * The fallbacks plugin is registry-declared (`dependencies`) and external in
6
+ * the build, and every import here is TYPE-ONLY: `dist/index.js` must carry
7
+ * ZERO runtime references to `dsh-llm-fallbacks`. The package's
8
+ * `declare module '@deepseek-ai/cordis'` augmentation types
9
+ * `ctx.get('llm-fallbacks')` for importers.
10
+ *
11
+ * Two views:
12
+ * - {@link fallbacksService} — the named cordis service while the plugin is
13
+ * applied. Registration is per-apply, so the service is `undefined` during
14
+ * HMR/fiber-swap windows even when the loader entry lives (the entry is
15
+ * declarative and outlives a fiber swap).
16
+ * - {@link fallbacksMounted} — capability view, service-first with a
17
+ * loader-entries fallback: the loader entry is present, enabled, and has a
18
+ * live fiber. Point-in-time read (no cache) — loader mounts entries
19
+ * concurrently (plugin-inventory philosophy).
20
+ */
21
+ import type { Context } from '@deepseek-ai/cordis';
22
+ import type { FallbacksService } from 'dsh-llm-fallbacks';
23
+ /** Loader entry name of the `dsh-llm-fallbacks` plugin row. */
24
+ export declare const FALLBACKS_ENTRY_NAME = "dsh-llm-fallbacks";
25
+ /**
26
+ * Minimal structural view of one cordis loader `Entry` the probe reads
27
+ * (`@deepseek-ai/cordis-plugin-loader` `Entry`/`EntryOptions` contract — the
28
+ * plugin carries no loader dependency; this is the consumed surface, same
29
+ * pattern as the optional `jobs` seam in agent-flow.ts).
30
+ */
31
+ export interface LoaderEntryView {
32
+ options: {
33
+ name: string;
34
+ group?: boolean | null;
35
+ /**
36
+ * Deployment row config (`EntryOptions.config` — "Config passed to the
37
+ * plugin", the same value the plugin's `apply()` receives; architect-
38
+ * verified field 2026-08-15). ABSENT when the user never configured the
39
+ * row — the adoption advisory treats that as an unreadable config.
40
+ */
41
+ config?: unknown;
42
+ };
43
+ disabled: boolean;
44
+ fiber: unknown;
45
+ }
46
+ /** Service view: the named `llm-fallbacks` cordis service while applied. */
47
+ export declare function fallbacksService(ctx: Context): FallbacksService | undefined;
48
+ /**
49
+ * The fallbacks loader row when present and enabled (group rows skipped).
50
+ * Unlike {@link fallbacksMounted}, NO live-fiber requirement: the entry is
51
+ * declarative and `options.config` is set at entry creation, so the adoption
52
+ * advisory (plan `20260815-dsh-fallbacks-personas` Task 4) can read the
53
+ * deployment's row config even during HMR/fiber-swap windows — the advisory
54
+ * caller gates on `fallbacksMounted` first.
55
+ */
56
+ export declare function fallbacksEntry(ctx: Context): LoaderEntryView | undefined;
57
+ /**
58
+ * Capability view: `true` when the fallbacks capability is mounted — the
59
+ * service is applied, or (service absent: HMR/fiber-swap window, older
60
+ * version, not yet applied) the loader entry is present, enabled, and its
61
+ * fiber is live.
62
+ */
63
+ export declare function fallbacksMounted(ctx: Context): boolean;