@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,98 @@
1
+ /**
2
+ * Zero-config seed declaration for the OPTIONAL `dsh-llm-fallbacks` plugin
3
+ * (plan `20260816-dsh-b4-seeds` Task 2): when the `llm-fallbacks` service is
4
+ * applied, this module declares the 13 `mode: subagent` mstar roles into the
5
+ * fallbacks seed registry — persona = mirror `description` (verbatim, the
6
+ * SSOT stays `mstar-roles`) + one mandatory-load guide line.
7
+ *
8
+ * Batch assembly (per-apply, re-runnable):
9
+ *
10
+ * 1. `getEffectiveRoles()` readback → the currently-seeded NON-mstar ids are
11
+ * merge-preserved (`{ id: row.id.trim(), persona: row.seedPersona }` —
12
+ * seedPersona, NOT the row persona, so an operator override stays flagged
13
+ * `personaOverridden` upstream). Upstream `declare` REPLACES the whole
14
+ * registry, so without preservation a mstar-only batch would strip
15
+ * preset/companion ids of their seeded annotations (rows remain — R2).
16
+ * 2. `subagentRoleIds()` × `personaFor()` resolve the mstar personas from
17
+ * the `harness-agents/` mirror (the decoration's existing lookup surface;
18
+ * `mode: primary` shells like `project-manager` are excluded).
19
+ * 3. Interpolation gate (HARD): any persona carrying the dsh system-prompt
20
+ * STRICT `{{...}}` hazard is skipped + warned BEFORE `declareSeeds` —
21
+ * never declared, never throws (aligned with `agent-personas.ts`
22
+ * extraction semantics; a mirror default is already rejected at
23
+ * extraction, so the gate's live path is the UNFILTERED readback data
24
+ * of a preserved row).
25
+ *
26
+ * The upstream `SeedDeclareOutcome` is passed through verbatim; the
27
+ * structured `SeedOutcomeView` is this module's own view (the service is a
28
+ * structural parameter — fake-testable, no runtime value import; the type
29
+ * imports are type-only, mirroring `fallbacks-probe.ts` — the bundle keeps
30
+ * ZERO runtime references to `dsh-llm-fallbacks`).
31
+ *
32
+ * Failure semantics: a throwing readback is contained (skip + one warn —
33
+ * probe semantics); a rejecting `declareSeeds` PROPAGATES to the caller —
34
+ * the entry wiring attaches a terminal `.catch` (the upstream preset
35
+ * self-declare pattern), so declare never throws out of `apply`.
36
+ *
37
+ * Module boundary: no barrel — the entry imports this module by explicit
38
+ * relative path; the entry does not re-export it.
39
+ */
40
+ import type { EffectiveRolesReadback, SeedDeclaration, SeedDeclareOutcome } from 'dsh-llm-fallbacks';
41
+ /** Logger label for the mstar seeds declaration (dsh logger naming: `<scope>/<subject>`). */
42
+ export declare const SEEDS_LOGGER = "mstar/fallbacks-seeds";
43
+ /** Seed-declaration log levels the module sink understands. */
44
+ export type SeedsLogLevel = 'debug' | 'warn' | 'error';
45
+ /** Per-call log sink — the entry binds it to `ctx.logger(SEEDS_LOGGER)`; tests pass a capture sink. */
46
+ export type SeedsLogSink = (level: SeedsLogLevel, message: string) => void;
47
+ /**
48
+ * The consumed service surface — a structural subset of the upstream
49
+ * `FallbacksService` (the two seed methods the declaration flow uses).
50
+ * Fake-testable: tests pass a spy object; the real service is assignable
51
+ * (structural typing anchors the contract against the installed `.d.ts`).
52
+ */
53
+ export interface SeedsServiceView {
54
+ /** (a) Declare the companion's FULL current seed set (replacement semantics). */
55
+ declareSeeds(seeds: readonly SeedDeclaration[]): Promise<SeedDeclareOutcome>;
56
+ /** (b) Sync readback — effective taxonomy with seed annotations. */
57
+ getEffectiveRoles(): EffectiveRolesReadback;
58
+ }
59
+ /** Options for {@link declareMstarSeeds}. */
60
+ export interface DeclareMstarSeedsOptions {
61
+ /** The `harness-agents/` mirror root; absent → no mstar personas (preserved-only batch). */
62
+ agentsDir: string | undefined;
63
+ /** The module log sink (entry binds `ctx.logger(SEEDS_LOGGER)`). */
64
+ log: SeedsLogSink;
65
+ }
66
+ /** One locally-skipped id with its gate reason (never reached `declareSeeds`). */
67
+ export interface SeedSkipView {
68
+ id: string;
69
+ reason: 'interpolation' | 'no-persona';
70
+ }
71
+ /** One merge-preserved seeded non-mstar id (batch persona = upstream `seedPersona`). */
72
+ export interface PreservedSeedView {
73
+ id: string;
74
+ persona: string;
75
+ }
76
+ /** Structured result of one {@link declareMstarSeeds} call — this module's own view. */
77
+ export interface SeedOutcomeView {
78
+ /** The full declaration batch handed to `declareSeeds` (mstar personas + preserved ids). */
79
+ declared: SeedDeclaration[];
80
+ /** Locally skipped ids (interpolation gate / no usable default) — never declared. */
81
+ skipped: SeedSkipView[];
82
+ /** The seeded non-mstar ids preserved from the readback into the batch. */
83
+ preserved: PreservedSeedView[];
84
+ /** The upstream `SeedDeclareOutcome` — passed through verbatim. */
85
+ outcome: SeedDeclareOutcome;
86
+ }
87
+ /**
88
+ * Declare the mstar subagent seeds: readback → merge-preserve seeded
89
+ * non-mstar ids → resolve mirror personas → interpolation gate → declare.
90
+ * Idempotent by construction (the same inputs produce the same batch; the
91
+ * upstream manager's no-delta check skips the settings write).
92
+ *
93
+ * @param service - the structural seed surface (real service or test fake).
94
+ * @param options - mirror root + log sink.
95
+ * @returns the structured outcome view; rejects only when `declareSeeds`
96
+ * itself rejects (the wiring attaches the terminal catch).
97
+ */
98
+ export declare function declareMstarSeeds(service: SeedsServiceView, options: DeclareMstarSeedsOptions): Promise<SeedOutcomeView>;
@@ -0,0 +1,151 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { Config, HarnessResolver } from './_shared.ts';
3
+ /** Logger label for the goal bridge (dsh logger naming: `<scope>/<subject>`). */
4
+ export declare const GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
5
+ /**
6
+ * Flat `maxGoalRounds` config fallback (architect decision — plan
7
+ * `20260816-dsh-nb2-goal-bridge`): 256, aligned with the GoalService default
8
+ * (`goal/src/index.ts:187`) and ralph `maxRounds` (`tool-ralph/src/index.ts:37`).
9
+ */
10
+ export declare const DEFAULT_MAX_GOAL_ROUNDS = 256;
11
+ /** Consumer log levels the module sink understands. */
12
+ export type GoalBridgeLogLevel = 'debug' | 'warn';
13
+ /** Module-level consumer log sink — bound by `apply` to `ctx.logger(GOAL_BRIDGE_LOGGER)` (agent-flow ledger precedent). */
14
+ export type GoalBridgeLogSink = (level: GoalBridgeLogLevel, message: string) => void;
15
+ /**
16
+ * Bind the goal-bridge log sink (the entry `apply` binds it to
17
+ * `ctx.logger(GOAL_BRIDGE_LOGGER)`). Returns the PRIOR sink so a caller can
18
+ * restore it (test pattern: agent-flow `setAgentFlowLogger`).
19
+ */
20
+ export declare function setGoalBridgeLogger(sink: GoalBridgeLogSink): GoalBridgeLogSink;
21
+ /**
22
+ * Root-agent discriminator (T1-verified; shared with the planMode bridge via
23
+ * explicit no-barrel import): `header.parentSession === undefined` ⇒
24
+ * root-like. Conversation forks also carry `parentSession` (seed lineage) →
25
+ * conservatively excluded from the goal mirror (accepted boundary).
26
+ */
27
+ export declare function isRootLikeAgent(agent: unknown): boolean;
28
+ /** CAS identity for one exact goal revision (upstream `GoalRef`). */
29
+ export interface GoalRefView {
30
+ readonly id: string;
31
+ readonly revision: number;
32
+ }
33
+ /** The one goal surface the bridge reads (`GoalSnapshot` fields used by the mirror). */
34
+ export interface GoalView extends GoalRefView {
35
+ readonly objective: string;
36
+ readonly phase: string;
37
+ readonly maxGoalRounds: number;
38
+ }
39
+ /**
40
+ * Minimal structural view of the goals service the bridge consumes
41
+ * (`@deepseek-ai/dsh-goal` `GoalService` — every method is agent-scoped;
42
+ * the runtime read is `ctx.get('goals')` without the inject requirement,
43
+ * same pattern as the probe's service view). `create` throws
44
+ * `GOAL_ALREADY_EXISTS` on a live non-complete goal and REPLACES a
45
+ * completed one ("A completed goal may be replaced" — `goal/src/
46
+ * index.ts:244-257`); `complete` is a CAS by `{ id, revision }`
47
+ * (`GOAL_STALE_REVISION` on stale). The drift path uses complete+create
48
+ * (never `edit`) so each new iteration gets a FRESH goal with a clean
49
+ * round budget (plan QC fix wave — qc2 W-1 / qc3 F-001/F-008).
50
+ */
51
+ export interface GoalsServiceView {
52
+ get(agent: unknown): GoalView | undefined;
53
+ create(agent: unknown, request: {
54
+ objective: string;
55
+ maxGoalRounds?: number;
56
+ }): unknown;
57
+ complete(agent: unknown, ref: GoalRefView): unknown;
58
+ }
59
+ /** The `agents` service surface the `subagent/start` root walk reads. */
60
+ interface AgentsView {
61
+ get(id: string): unknown;
62
+ }
63
+ /**
64
+ * The mirrored goal objective: the COMPLETE iteration flow with the exit
65
+ * definition (mstar-host `/goal` rule — advancing an iteration means the
66
+ * entire flow, never a sub-stage). Session-level text only — `status.json`
67
+ * stays the harness SSOT.
68
+ */
69
+ export declare function iterationGoalObjective(iterationId: string): string;
70
+ /**
71
+ * Locate the steering iteration compass (mirror of the engine's
72
+ * `resolveCompassEnforcement` scan + the catalog's `steeringCompassPath`):
73
+ * the FIRST `{ITERATION_DIR}/<id>/delivery-compass.md` whose frontmatter
74
+ * `status` is `active` or `locked` — the directory name IS the iteration id
75
+ * (plan-conventions `{ITERATION_DIR}/<id>/`). Completed/status-less/archived
76
+ * compasses do not steer. Silent on any read failure (advisory degrade).
77
+ * Shared with the planMode bridge via explicit no-barrel import (Task 4b —
78
+ * the same "is an active iteration steering" read).
79
+ * @param harnessDir - the resolved `{HARNESS_DIR}`.
80
+ */
81
+ export declare function steeringCompass(harnessDir: string): {
82
+ iterationId: string;
83
+ } | undefined;
84
+ /** Inputs of the mirror: the structural goals view, the per-workspace resolver, and the resolved round cap. */
85
+ export interface MirrorIterationGoalInput {
86
+ resolver: HarnessResolver;
87
+ /** The structural goals view (`ctx.get('goals')`); absent → the mirror is inert. */
88
+ goals?: GoalsServiceView;
89
+ /** The resolved `maxGoalRounds` (flat config key, absent → {@link DEFAULT_MAX_GOAL_ROUNDS}). */
90
+ maxGoalRounds: number;
91
+ }
92
+ /**
93
+ * Mirror the steering iteration objective into the goal of ONE agent.
94
+ * Root-like agent (`header.parentSession === undefined`) + active iteration
95
+ * (compass `status: active|locked`): `get` → absent → `create` (get-先行 —
96
+ * no `GOAL_ALREADY_EXISTS`); present with drifted objective → REPLACE with
97
+ * a fresh goal for the new iteration (see {@link replaceDriftedGoal} — a
98
+ * completed goal is `create`d directly, a live goal is `complete`d first;
99
+ * the new goal starts with a CLEAN round budget); present with the matching
100
+ * objective → no-op (idempotent decision-point re-evaluation). ONE stale
101
+ * re-read retry; a second stale failure is warned and abandoned —
102
+ * goal-service-side concurrency is rare.
103
+ *
104
+ * @returns `true` when the mirror is ensured for this agent (created,
105
+ * replaced, or already in place); `false` when not applicable or a
106
+ * contained failure occurred. Never throws — the caller's listener stays
107
+ * contained.
108
+ */
109
+ export declare function mirrorIterationGoal(agent: unknown, input: MirrorIterationGoalInput): boolean;
110
+ /**
111
+ * Resolve the ROOT agent of a published child via the `parentSession` walk
112
+ * (upstream `subagent/src/continuation.ts:819-831` precedent): in-process
113
+ * subagent children stamp `header.parentSession` = the parent SESSION id,
114
+ * which IS the parent agent id (a session per agent); the walk stops at the
115
+ * first root-like ancestor. `undefined` when unresolvable (fork lineage,
116
+ * non-in-process provider, registry gap, or a cycle) — the decision point
117
+ * then silently skips. Cycle guard: a `seen` set over visited session ids
118
+ * (the upstream `liveLineage` guard — plan QC fix wave qc2 W-2 / qc3
119
+ * F-003) breaks on ANY revisited id — a 1-hop self-loop, a 2+ hop cycle
120
+ * (A→B→A), or a longer malformed lineage — instead of spinning forever on
121
+ * the synchronous `subagent/start` decision-point listeners (reachable via
122
+ * HMR remounts, resumed/forked sessions with stale headers, or a future
123
+ * host change). Shared with the planMode bridge via explicit no-barrel
124
+ * import (Task 4b — the same `subagent/start` decision-point root walk).
125
+ */
126
+ export declare function rootAgentOf(agent: unknown, agents: AgentsView): unknown | undefined;
127
+ /**
128
+ * Register the goal bridge: an `agent/session-start` listener (root filter
129
+ * inside the mirror — root and children alike fire, `runtime-types.ts:217`)
130
+ * plus a decision-point re-evaluation on `subagent/start` (the existing
131
+ * decision point — index.ts decoration slot), resolving the delegating ROOT
132
+ * via the `parentSession` walk — the two mirror edges are idempotent (get +
133
+ * compare when the mirror is in place — no churn) — plus a THIRD, advisory
134
+ * listener on the `session/event` firehose (Task 3): a `goal/change`
135
+ * envelope whose goal is blocked logs ONE warn (code + objective summary +
136
+ * `{HARNESS_DIR}/status.json` residual pointer) with ZERO harness writes
137
+ * (the one-way mirror; see {@link warnBlockedGoal}). The goals service is an
138
+ * OPTIONAL seam (`ctx.get('goals')` structural read): absent → ONE debug log
139
+ * + the mirror stays inert, never a boot failure — the blocked advisory is
140
+ * independent of it (it only needs the firehose + resolver). Every listener
141
+ * body is try/catch-contained.
142
+ *
143
+ * @param ctx - the plugin's registrant context (the app composition root).
144
+ * @param resolver - the shared per-workspace `{HARNESS_DIR}` resolver.
145
+ * @param config - validated plugin configuration (flat `maxGoalRounds`,
146
+ * absent → {@link DEFAULT_MAX_GOAL_ROUNDS}; a non-positive / non-integer
147
+ * value warns once and falls back to the default — see
148
+ * {@link resolveGoalRounds}).
149
+ */
150
+ export declare function registerGoalBridge(ctx: Context, resolver: HarnessResolver, config: Config): void;
151
+ export {};
@@ -0,0 +1,70 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { HarnessResolver } from './_shared.ts';
3
+ /** Logger label for the planMode bridge (dsh logger naming: `<scope>/<subject>`). */
4
+ export declare const PLAN_MODE_BRIDGE_LOGGER = "mstar/plan-mode-bridge";
5
+ /** Consumer log levels the module sink understands. */
6
+ export type PlanModeBridgeLogLevel = 'debug' | 'warn';
7
+ /** Module-level consumer log sink — bound by `apply` to `ctx.logger(PLAN_MODE_BRIDGE_LOGGER)` (goal-bridge precedent). */
8
+ export type PlanModeBridgeLogSink = (level: PlanModeBridgeLogLevel, message: string) => void;
9
+ /**
10
+ * Bind the planMode-bridge log sink (the entry `apply` binds it to
11
+ * `ctx.logger(PLAN_MODE_BRIDGE_LOGGER)`). Returns the PRIOR sink so a caller
12
+ * can restore it (test pattern: goal-bridge `setGoalBridgeLogger`).
13
+ */
14
+ export declare function setPlanModeBridgeLogger(sink: PlanModeBridgeLogSink): PlanModeBridgeLogSink;
15
+ /**
16
+ * Minimal structural view of the planMode service the bridge consumes
17
+ * (`@deepseek-ai/dsh-plan-mode` `PlanModeController` — every method is
18
+ * agent-scoped; the runtime read is `ctx.get('planMode')` without the
19
+ * inject requirement, same pattern as the goal bridge). `set` is idempotent:
20
+ * `'noop'` when the target already matches (`plan-mode/src/index.ts:425-445`).
21
+ */
22
+ export interface PlanModeServiceView {
23
+ get(agent: unknown): {
24
+ active?: unknown;
25
+ pending?: unknown;
26
+ } | undefined;
27
+ set(agent: unknown, active: boolean): unknown;
28
+ }
29
+ /**
30
+ * The planMode target for one harness: `true` iff an active iteration steers
31
+ * (compass `status: active|locked`) AND a Prepare window exists (≥1 plan
32
+ * row `Todo`). No active iteration / no Prepare window → `false` (plan mode
33
+ * OFF — the host default).
34
+ * @param harnessDir - the resolved `{HARNESS_DIR}`.
35
+ */
36
+ export declare function planModeTarget(harnessDir: string): boolean;
37
+ /** Inputs of the sync: the structural planMode view and the per-workspace resolver. */
38
+ export interface PlanModeSyncInput {
39
+ resolver: HarnessResolver;
40
+ /** The structural planMode view (`ctx.get('planMode')`); absent → the sync is inert. */
41
+ planMode?: PlanModeServiceView;
42
+ }
43
+ /**
44
+ * Mirror the harness Prepare state into the planMode selection of ONE agent:
45
+ * root-like agent (`header.parentSession === undefined`) → resolve the
46
+ * workspace → compute {@link planModeTarget} → `planMode.set(agent, target)`.
47
+ * The service's `'noop'` return makes repeated evaluation at multiple
48
+ * decision points churn-free (no new `plan/mode` event when already in
49
+ * target). Non-root agent / unresolvable harness / missing planMode service
50
+ * → no set.
51
+ *
52
+ * @returns `true` when the sync ran for this agent (set called); `false`
53
+ * when not applicable or a contained failure occurred. Never throws — the
54
+ * caller's listener stays contained.
55
+ */
56
+ export declare function syncPlanMode(agent: unknown, input: PlanModeSyncInput): boolean;
57
+ /**
58
+ * Register the planMode bridge: an `agent/session-start` listener (root
59
+ * filter inside — root and children alike fire, `runtime-types.ts:217`) plus
60
+ * the EXISTING `subagent/start` decision point (the goal-bridge precedent),
61
+ * resolving the delegating ROOT via the shared `parentSession` walk — the
62
+ * two edges are idempotent (`'noop'` when already in target — no churn). The
63
+ * planMode service is an OPTIONAL seam (`ctx.get('planMode')` structural
64
+ * read): absent → ONE debug log + the bridge stays inert, never a boot
65
+ * failure. Every listener body is try/catch-contained.
66
+ *
67
+ * @param ctx - the plugin's registrant context (the app composition root).
68
+ * @param resolver - the shared per-workspace `{HARNESS_DIR}` resolver.
69
+ */
70
+ export declare function registerPlanModeBridge(ctx: Context, resolver: HarnessResolver): void;
@@ -52,9 +52,10 @@ export declare class SkillLintVetoError extends Error {
52
52
  }
53
53
  /**
54
54
  * Lint one SKILL.md document with the engine skill-authoring lints
55
- * (`lintFrontmatter` + `lintFiveQuestion` the CLI `mstar skill lint`
56
- * combination; violation codes `lint.frontmatter.*` /
57
- * `skill-authoring.five-question.*`). Pure: no enforcement, no I/O.
55
+ * (`lintFrontmatter` + `lintFiveQuestion` + `findEphemeralCitations` the
56
+ * CLI `mstar skill lint` combination plus the ephemeral-citation gate;
57
+ * violation codes `lint.frontmatter.*` / `skill-authoring.five-question.*`
58
+ * / `skill.ephemeral.*`). Pure: no enforcement, no I/O.
58
59
  * @param doc - the full SKILL.md text.
59
60
  */
60
61
  export declare function lintSkillDoc(doc: string): GateResult;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Harness-rules system-prompt injection (plan `20260816-dsh-nb1-systemprompt`
3
+ * Task 2): the root session's ONE `mstar:harness-rules` pointer section plus
4
+ * the `mstar:engine-status` runtime-context summary, both registered on the
5
+ * GLOBAL prompt layer — visible to the root session AND every dispatched
6
+ * child — without touching the child-scoped `mstar:role-persona` section
7
+ * (fallbacks-decoration; distinct name, distinct layer — duplicate-name
8
+ * throws are per name per layer, verified `scope/src/store.ts`).
9
+ *
10
+ * Content discipline:
11
+ * - The section is a POINTER block (presence / enforcement word / resolved
12
+ * `{HARNESS_DIR}` / one read-mstar-harness-core directive) — deliberately
13
+ * minimal, never a rules dump (plan pointer-block constraint). The
14
+ * OUTPUT is zero-complete-`{{...}}`-groups text: dsh system-prompt
15
+ * renders section AND context text with STRICT `{{variable}}`
16
+ * interpolation and throws on unknown/malformed/undefined references
17
+ * (`interpolate` in `@deepseek-ai/dsh-system-prompt`), so every injected
18
+ * string must carry no complete group. The mechanism is LIVE, not static
19
+ * (plan QC fix wave W-1): every operator-controlled value embedded below
20
+ * (harness dir, plan ids, iteration id, lease fields, direction prose)
21
+ * is passed through `stripInterpolationHazard` — complete `{{…}}` groups
22
+ * are screened so a hostile value can never break prompt assembly, while
23
+ * a lone `{{` stays literal prose.
24
+ * - The harness dir is resolved PER ASSEMBLY from the assembly context's
25
+ * agent (plan QC fix wave W-2 — the catalog pre-step precedent): the
26
+ * session cwd of the agent whose prompt is being assembled, via
27
+ * `resolver.forAgent`, with the boot value (`forWorkspace(undefined)`,
28
+ * the explicit config or null) as the fallback when the assembly carries
29
+ * no agent. Zero-config deployments (no explicit `harnessDir`, the
30
+ * probe-discovers-`.mstar/` default) therefore resolve the pointer and
31
+ * the status context to the session's own workspace instead of rendering
32
+ * a permanent `none`/`soft`. The enforcement word is LIVE — the section
33
+ * text is a provider (the plan:policy precedent) that re-reads
34
+ * `resolveCompassEnforcement` per assembly (the same existing read the
35
+ * gates and the catalog use — no new config key), so a mid-session
36
+ * compass soft/hard flip lands on the next assembly without
37
+ * re-registration, in zero-config and explicit-config deployments alike.
38
+ * - The context provider reuses the catalog's unified machine-summary
39
+ * source (`buildCatalogSources` — the SAME builder the engine-status
40
+ * pre-step catalog row uses) and projects a BOUNDED subset: watermark +
41
+ * iteration gate + compact state line. Full status.json content
42
+ * (residual detail, agent-flow events, knowledge digest, branch/policy
43
+ * anchors) stays out. The build is TTL-memoized PER RESOLVED HARNESS DIR
44
+ * (`DEFAULT_CATALOG_TTL_MS`) so the per-assembly hot path does not
45
+ * re-read status.json / the compass / the ledger on every prompt
46
+ * assembly (the catalog's documented staleness tradeoff).
47
+ *
48
+ * Degradation (boot is never affected — the decoration's contained-degrade
49
+ * discipline):
50
+ * - Structural existence check via `ctx.get('systemPrompt')` — the
51
+ * `ctx.get('agents')` precedent: a DIRECT `ctx.systemPrompt` property
52
+ * read throws "cannot get property without inject" on a started cordis
53
+ * fiber when the service is not composed. Absent service → return `false`
54
+ * + exactly one debug log.
55
+ * - Registration is deferred through `ctx.inject(['systemPrompt'], …)`
56
+ * (HMR-safe re-apply): the `section()`/`context()` calls run on the
57
+ * inject child, and the exact disposers they return are collected on
58
+ * that child via `systemPromptCtx.effect` (plan QC fix wave W-HMR) — the
59
+ * registrations therefore unwind with THIS plugin's apply by explicit
60
+ * ownership, so a re-apply disposes the old registrations before
61
+ * registering fresh ones (no duplicate-name throw, no stale closure from
62
+ * the previous apply). A direct global registration through the service
63
+ * instance without the collected disposers would instead rely on the
64
+ * cordis traceable-proxy `this.ctx` rebind for ownership — implicit and
65
+ * version-fragile; the explicit collection removes that dependency.
66
+ * - Registration errors are contained (warn + return `false`); a throwing
67
+ * log sink is contained inside the log helper (never-throws invariant);
68
+ * the collected disposers run inside a try/catch so an exotic disposal
69
+ * throw can never break the fiber teardown.
70
+ *
71
+ * Module boundary: no barrel — the entry imports this module by explicit
72
+ * relative path and does NOT re-export its public names (plan constraint);
73
+ * tests import from this module directly.
74
+ */
75
+ import type { Context } from '@deepseek-ai/cordis';
76
+ import { type HarnessResolver } from './_shared.ts';
77
+ /** Logger label for the harness-prompt injection (dsh logger naming: `<scope>/<subject>`). */
78
+ export declare const HARNESS_PROMPT_LOGGER = "mstar/harness-prompt";
79
+ /** The global harness-rules pointer section name (root AND child assemblies). */
80
+ export declare const HARNESS_RULES_SECTION_NAME = "mstar:harness-rules";
81
+ /** Prompt order of the harness-rules section — after the deployment persona (0) and the child role persona (1), before plan:policy (50). */
82
+ export declare const HARNESS_RULES_SECTION_ORDER = 2;
83
+ /** The engine-status runtime-context contribution name (durable user-role snapshot). */
84
+ export declare const ENGINE_STATUS_CONTEXT_NAME = "mstar:engine-status";
85
+ /** Prompt order of the engine-status context — first in the runtime snapshot, before the policy sentences (110+). */
86
+ export declare const ENGINE_STATUS_CONTEXT_ORDER = 100;
87
+ /** Harness-prompt log levels the module sink understands. */
88
+ export type HarnessPromptLogLevel = 'debug' | 'warn';
89
+ /** Module-level harness-prompt log sink — bound by `apply` to `ctx.logger(HARNESS_PROMPT_LOGGER)` (decoration precedent). */
90
+ export type HarnessPromptLogSink = (level: HarnessPromptLogLevel, message: string) => void;
91
+ /**
92
+ * Bind the harness-prompt log sink (the entry `apply` binds it to
93
+ * `ctx.logger(HARNESS_PROMPT_LOGGER)`). Returns the PRIOR sink so a caller
94
+ * can restore it (test pattern: {@link setDecorationLogger}).
95
+ */
96
+ export declare function setHarnessPromptLogger(sink: HarnessPromptLogSink): HarnessPromptLogSink;
97
+ /**
98
+ * Register the harness-rules pointer section + the engine-status context on
99
+ * the GLOBAL prompt layer.
100
+ *
101
+ * @param ctx - the registrant context (the plugin's apply ctx; unscoped →
102
+ * the registrations land on the global layer).
103
+ * @param options.resolver - the per-workspace `{HARNESS_DIR}` resolver.
104
+ * The BOOT value (`forWorkspace(undefined)`, the explicit config or null)
105
+ * is the fallback; the section and context providers resolve the harness
106
+ * dir PER ASSEMBLY from the assembly context's agent (the session
107
+ * workspace), and the section's enforcement word is re-read from the
108
+ * compass per assembly — so both stay correct in zero-config deployments
109
+ * and follow mid-session compass flips.
110
+ * @returns `true` when the service exists and registration was scheduled;
111
+ * `false` when `ctx.systemPrompt` is structurally absent (one debug log,
112
+ * boot unaffected) or the synchronous registration path threw (contained
113
+ * warn). The actual registration runs in an inject child that settles
114
+ * ASYNC — a failure there is contained to a warn and cannot be observed
115
+ * through this return value (`true` only guarantees scheduling, not
116
+ * landing; `apply` ignores the value, so boot is never affected). Never
117
+ * throws.
118
+ */
119
+ export declare function registerHarnessPrompt(ctx: Context, options: {
120
+ resolver: HarnessResolver;
121
+ }): boolean;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Workflow-ledger session-event consumer (plan `20260815-dsh-workflow-ledger`
3
+ * Task 3 — the W-B2 producer half).
4
+ *
5
+ * Source of record: the durable `tool-workflow/*` session events appended
6
+ * into the CALLING PARENT session's log (top-level runs only — nested
7
+ * transport calls record nothing upstream; Task 1 seam notes §4). The
8
+ * consumer has THREE halves (the architect-verified seam):
9
+ * 1. COLD SCAN at apply — iterate `ctx.get('sessions').list()`, read each
10
+ * session's `events` snapshot, and record any `tool-workflow/*` rows
11
+ * already present. Constructor-seeded events (replay/resume/fork) NEVER
12
+ * publish on the `session/event` firehose (`firstLiveSeq`), so without
13
+ * the cold scan pre-restart runs would be invisible.
14
+ * 2. LIVE FIREHOSE — `ctx.events.on('session/event', …)`: the post-commit
15
+ * append feed, delivered to ALL sessions for a root-context listener
16
+ * (scope-null event, untagged listeners admitted).
17
+ * 3. SESSION-CREATED BACKFILL — `ctx.events.on('session/created', …)`: a
18
+ * session created AFTER apply with a constructor-seeded log (resumed /
19
+ * forked conversation — `session/created` fires after the seed enters
20
+ * the log, upstream `session/src/index.ts:961-995`) gets its snapshot
21
+ * cold-scanned ONCE on the creation announcement, closing the
22
+ * late-seeded-session gap (qc3 S-304 / qc2 W-1a).
23
+ *
24
+ * DEDUPE (qc2 W-1 / qc3 F-301 fix-wave): ONE DURABLE per-session watermark —
25
+ * the next expected envelope `seq` (session-log position) — persisted to
26
+ * `{HARNESS_DIR}/workflow-ledger-cursors.json` (a small bounded sidecar next
27
+ * to `agent-flow.jsonl`, written atomically temp-file + rename through the
28
+ * same containment discipline). The watermark is consulted AND advanced by
29
+ * every scan (cold / created-backfill / live): envelopes with `seq` below it
30
+ * were already recorded — across cold+live overlap AND across plugin
31
+ * re-applies (a re-registration no longer re-records the same live
32
+ * sessions). The watermark advances only AFTER the ledger row appended
33
+ * successfully (qc3 R-401 — a failing append leaves the cursor behind, so
34
+ * the row is re-attempted at the next scan, never permanently lost). The
35
+ * in-memory Map is the durable file's mirror (module-level
36
+ * cache, bounded by the session cap); any watermark read/write failure
37
+ * degrades to in-memory-only with one warn — a ledger row is never lost and
38
+ * the workflow run is never affected.
39
+ *
40
+ * Mapping (Task 2 schema): `tool-workflow/run-start` → `workflow-run`
41
+ * (`agent` = the carrying parent session id), `tool-workflow/agent-start` →
42
+ * `workflow-agent` (`childId` preserved), `tool-workflow/run-end` →
43
+ * `workflow-run-end`. `tool-workflow/agent-end` is upstream MEMBER
44
+ * bookkeeping with no ledger kind (Task 2 handoff + plan Interfaces — the
45
+ * member `outcome` is intentionally not persisted) and is filtered out.
46
+ * `ts` takes the envelope's `time`.
47
+ *
48
+ * P-c answer observation (plan `20260815-dsh-workflow-gate` Task 4 fold-in —
49
+ * the Task-2 Important handoff): the workflow GATE cannot observe the ask
50
+ * outcome — the tool registry's `serviceAsk` consumes the approval result
51
+ * internally, and the gate invents no answerer. The run-start observation
52
+ * IS the answer seam: when the approval waterfall ALLOWS a workflow call,
53
+ * the call executes and the durable `tool-workflow/run-start` session event
54
+ * (name carried) lands in the parent session log — the consumer maps it to
55
+ * the `workflow-run` row AND records `allow` for the run name into the
56
+ * apply-scoped {@link WorkflowAskCache} (`registerWorkflowLedger`'s third
57
+ * parameter — the host adapter's instance). W-1 (qc2 fix-wave): the record
58
+ * fires ONLY for names the policy marked asked in this apply
59
+ * (`WorkflowAskCache.markAsked` on every ask verdict; the observation
60
+ * promotes via `wasAsked`) — a run observed without a prior ask (P-b
61
+ * advisory under `ask` mode, `warn`/`off`-mode runs) is not an approval
62
+ * resolution and never pre-authorizes the name. A DENIED answer produces no
63
+ * run → no observation → the next same-name call under `ask` re-asks
64
+ * (fail-closed — no grant evidence, never an invented allow). The hook is
65
+ * bounded and contained: it fires only on the FIRST successful recording of
66
+ * a run-start (the watermark gate above), keyed on the UNCAPPED run name
67
+ * (`row.runName` — it must match the gate's `meta.name`, which is never
68
+ * truncated AND is normalized through the SAME `normalizeWorkflowName` the
69
+ * gate composes with — the Task 5 congruence fold-in), and a throwing cache
70
+ * record degrades the observation with one warn — the ledger row is already
71
+ * appended, the run is never affected.
72
+ *
73
+ * Observe-only (plan Global Constraints: W3 / N5): ZERO gating — every read
74
+ * and append is try/catch-contained; a throwing session read logs one warn
75
+ * and the run is unaffected; all appends go through `recordWorkflowEvent`
76
+ * (itself fully contained — a failing ledger write never crashes or alters
77
+ * a workflow run). The `sessions` service is read STRUCTURALLY via
78
+ * `ctx.get('sessions')` — an absent service (composition without
79
+ * dsh-session) → one debug log + consumer disabled. No runtime dependency
80
+ * on `@deepseek-ai/dsh-session` (same pattern as the agents/loader seams).
81
+ * The `session/created` listener NEVER throws synchronously — upstream
82
+ * vetoes a session publication on a throwing creation listener.
83
+ *
84
+ * Depth advisory (P-e / N5): on `agent-start`, resolve the child session
85
+ * via `sessions.get(childId)` and warn when its `header.delegationDepth`
86
+ * is >= 2 — ONCE per run (bounded by a per-runId latch). Observe-time only,
87
+ * NEVER a refusal path.
88
+ */
89
+ import type { Context } from '@deepseek-ai/cordis';
90
+ import type { HarnessResolver } from './_shared.ts';
91
+ import type { WorkflowAskCache } from './workflow-policy.ts';
92
+ /** Logger label for the workflow-ledger consumer (dsh logger naming: `<scope>/<subject>`). */
93
+ export declare const WORKFLOW_LEDGER_LOGGER = "mstar/workflow-ledger";
94
+ /**
95
+ * The durable watermark file name under `{HARNESS_DIR}` (qc2 W-1 / qc3
96
+ * F-301 fix-wave): `{ "v": 1, "cursors": { "<sessionId>": <nextSeq> } }` —
97
+ * the next expected envelope seq per session id. Written atomically
98
+ * (temp-file + rename) after every recorded workflow row; read lazily per
99
+ * harness dir (module-level cache). Absent on first run (silent); a
100
+ * present-but-corrupt file degrades to in-memory-only with one warn.
101
+ */
102
+ export declare const WORKFLOW_LEDGER_WATERMARK_FILE = "workflow-ledger-cursors.json";
103
+ /**
104
+ * Session-count cap for ONE watermark file (bounds the sidecar). Eviction
105
+ * prefers sessions that are no longer live; when every entry is live the
106
+ * oldest entry is dropped (documented residual — a later restore of an
107
+ * evicted session re-records its rows; bounded by the cap).
108
+ */
109
+ export declare const WORKFLOW_LEDGER_WATERMARK_MAX_SESSIONS = 256;
110
+ /** Consumer log levels the module sink understands. */
111
+ export type WorkflowLedgerLogLevel = 'debug' | 'warn';
112
+ /** Module-level consumer log sink — bound by `apply` to `ctx.logger(WORKFLOW_LEDGER_LOGGER)` (agent-flow ledger precedent). */
113
+ export type WorkflowLedgerLogSink = (level: WorkflowLedgerLogLevel, message: string) => void;
114
+ /**
115
+ * Bind the consumer log sink (the entry `apply` binds it to
116
+ * `ctx.logger(WORKFLOW_LEDGER_LOGGER)`). Returns the PRIOR sink so a caller
117
+ * can restore it (test pattern: agent-flow `setAgentFlowLogger`).
118
+ */
119
+ export declare function setWorkflowLedgerLogger(sink: WorkflowLedgerLogSink): WorkflowLedgerLogSink;
120
+ /**
121
+ * Register the workflow-ledger consumer: (1) a `session/created` backfill
122
+ * listener (registered FIRST — qc3 S-305 — so no apply-time window exists
123
+ * between the snapshot and the attach); (2) a bounded cold scan over
124
+ * `ctx.sessions.list()` reading each session's `events` snapshot for
125
+ * `tool-workflow/*` rows (covers pre-restart runs — constructor-seeded
126
+ * events never hit the firehose, `firstLiveSeq`); (3) a live
127
+ * `ctx.events.on('session/event', …)` listener filtering the four types.
128
+ * One DURABLE watermark per session id (session-log `seq` position,
129
+ * persisted to `{HARNESS_DIR}/workflow-ledger-cursors.json`) — re-applies
130
+ * never duplicate; no other cache. The watermark advances only AFTER a
131
+ * successful ledger append (qc3 R-401 — a failing append leaves the cursor
132
+ * behind so the row is re-attempted at the next scan, never lost). Every
133
+ * read/append is try/catch-contained — including `sessions.list()` itself
134
+ * (qc2 S-7: one warn, the cold scan skipped, the consumer stays live); the
135
+ * `sessions` service absent → one debug log + consumer disabled (composition
136
+ * without dsh-session). All appends go through `recordWorkflowEvent` (itself
137
+ * fully contained — a failing ledger write never crashes or alters a
138
+ * workflow run).
139
+ *
140
+ * @param ctx - the plugin's registrant context (the app composition root).
141
+ * @param resolver - the shared per-workspace `{HARNESS_DIR}` resolver
142
+ * (harnessDir attribution from the carrying session's `header.cwd`).
143
+ * @param workflowAskCache - the apply-scoped P-c ask cache (plan
144
+ * `20260815-dsh-workflow-gate` Task 4 fold-in — the host adapter's
145
+ * instance; see the module doc "P-c answer observation"). Absent → the
146
+ * observation hook is disabled (W-B2 tests / compositions without the
147
+ * workflow gate).
148
+ */
149
+ export declare function registerWorkflowLedger(ctx: Context, resolver: HarnessResolver, workflowAskCache?: WorkflowAskCache): void;