@zhchxiao123/dsh-devflow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Runtime stage vocabulary: the ordered stage list, location narrowing, and the
3
+ * card-id factory. Kept beside the type-only module so `types.ts` stays free of
4
+ * runtime code.
5
+ * @module @zhchxiao123/dsh-devflow/src/stages
6
+ */
7
+ /** The pipeline stages in flow order; `blocked` is a bypass, not a member. */
8
+ export const DEV_STAGES = [
9
+ 'draft',
10
+ 'designing',
11
+ 'ready',
12
+ 'developing',
13
+ 'reviewing',
14
+ 'testing',
15
+ 'done',
16
+ ];
17
+ /**
18
+ * Narrow an unknown value to a pipeline stage.
19
+ * @param value - the candidate value.
20
+ * @returns `true` when `value` is one of {@link DEV_STAGES}.
21
+ */
22
+ export function isDevStage(value) {
23
+ return typeof value === 'string' && DEV_STAGES.includes(value);
24
+ }
25
+ /**
26
+ * Narrow an unknown value to a card location (a stage or `blocked`).
27
+ * @param value - the candidate value.
28
+ * @returns `true` when `value` is a stage or the `blocked` bypass.
29
+ */
30
+ export function isCardLocation(value) {
31
+ return value === 'blocked' || isDevStage(value);
32
+ }
33
+ /**
34
+ * Brand a raw string as a {@link DevflowCardId}. The id equals the card's
35
+ * directory name; construction lives here because this package owns the brand.
36
+ * @param value - the card directory name.
37
+ * @returns the branded id.
38
+ */
39
+ export function DevflowCardId(value) {
40
+ return value;
41
+ }
42
+ /** Forward and rework edges of the pipeline; `blocked` legality lives in {@link isLegalTransition}. */
43
+ const FLOW = {
44
+ draft: ['designing'],
45
+ designing: ['ready'],
46
+ ready: ['developing'],
47
+ developing: ['reviewing'],
48
+ reviewing: ['testing', 'developing'],
49
+ testing: ['done', 'developing'],
50
+ done: [],
51
+ };
52
+ /**
53
+ * Whether one stage move is a legal edge of the state machine.
54
+ *
55
+ * Main flow follows the pipeline order; `reviewing` and `testing` may rework
56
+ * to `developing`; any non-terminal location may enter `blocked`; a blocked
57
+ * card may only recover to the exact stage it interrupted.
58
+ * @param from - the card's current location.
59
+ * @param to - the requested target location.
60
+ * @param blockedFrom - the remembered origin stage while `from` is `blocked`.
61
+ * @returns `true` when the move is a legal edge.
62
+ */
63
+ export function isLegalTransition(from, to, blockedFrom) {
64
+ if (from === to)
65
+ return false;
66
+ if (from === 'blocked')
67
+ return to === blockedFrom;
68
+ if (to === 'blocked')
69
+ return from !== 'done';
70
+ return FLOW[from].includes(to);
71
+ }
72
+ /**
73
+ * Whether a legal edge moves the card backwards (a rework). Rework edges
74
+ * require a recorded `reason` so the next holder knows what to fix.
75
+ * @param from - the departing location.
76
+ * @param to - the target location.
77
+ * @returns `true` for `reviewing -> developing` and `testing -> developing`.
78
+ */
79
+ export function isReworkEdge(from, to) {
80
+ return to === 'developing' && (from === 'reviewing' || from === 'testing');
81
+ }
82
+ //# sourceMappingURL=stages.js.map
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Vocabulary types of the `ctx.devflow` capability seam: card identity, stages,
3
+ * journal entries, and the read-side card value. Runtime helpers (stage set,
4
+ * id factory, journal fold) live in `./journal.ts` and the package root.
5
+ * @module @zhchxiao123/dsh-devflow/types
6
+ */
7
+ import type { DevflowCardId } from './stages.ts';
8
+ export type { DevflowCardId } from './stages.ts';
9
+ declare module '@deepseek-ai/cordis' {
10
+ interface Events {
11
+ /**
12
+ * Single-decision transition pipeline. The store dispatches this after the
13
+ * revision and edge checks and before the journal commit; a policy
14
+ * listener that owns the decision returns `{ allowed: false, reason }`
15
+ * without calling `next()`, while an observing listener must delegate.
16
+ * @param attempt - the resolved transition about to commit, including its departure location.
17
+ * @param next - delegate to the remaining listeners, finally `{ allowed: true }`.
18
+ * @mode waterfall
19
+ */
20
+ 'devflow/transition'(attempt: TransitionAttempt, next: () => Promise<TransitionDecision>): Promise<TransitionDecision>;
21
+ /**
22
+ * A card settled at a new location after a committed transition.
23
+ * @mode emit
24
+ * @param card - the card after the move, `stageRevision` already advanced.
25
+ * @param from - the location the card departed.
26
+ */
27
+ 'devflow/stage-changed'(card: DevCard, from: CardLocation): void;
28
+ /**
29
+ * A new card entered the active set: its journal committed the first
30
+ * `created` entry. Dispatched once per creation, after the projection
31
+ * write.
32
+ * @mode emit
33
+ * @param card - the created card, at `draft` with revision 1.
34
+ */
35
+ 'devflow/card-created'(card: DevCard): void;
36
+ }
37
+ }
38
+ /**
39
+ * The closed set of pipeline stages a card moves through. `blocked` is not a
40
+ * stage: it is a bypass location that remembers the stage it interrupted (see
41
+ * {@link CardLocation}).
42
+ */
43
+ export type DevStage = 'draft' | 'designing' | 'ready' | 'developing' | 'reviewing' | 'testing' | 'done';
44
+ /** Where a card currently sits: a pipeline stage, or the `blocked` bypass. */
45
+ export type CardLocation = DevStage | 'blocked';
46
+ /** Who performed a journal action; `command` marks the human-command intervention plane. */
47
+ export type DevActor = {
48
+ kind: 'human';
49
+ name?: string;
50
+ } | {
51
+ kind: 'agent';
52
+ session?: string;
53
+ } | {
54
+ kind: 'command';
55
+ name?: string;
56
+ };
57
+ /** First journal entry of every card; `rev` is always 1. */
58
+ export interface JournalCreated {
59
+ rev: number;
60
+ at: string;
61
+ type: 'created';
62
+ by: DevActor;
63
+ /** The card this one decomposes, fixed here at creation and never changed. */
64
+ parent?: DevflowCardId;
65
+ }
66
+ /**
67
+ * One stage move. A move to `blocked` remembers `from`; the matching recovery
68
+ * must return to exactly that stage.
69
+ */
70
+ export interface JournalTransition {
71
+ rev: number;
72
+ at: string;
73
+ type: 'transition';
74
+ from: CardLocation;
75
+ to: CardLocation;
76
+ by?: DevActor;
77
+ reason?: string;
78
+ /** Gate facts attached by the transition waterfall, e.g. the human approval signature. */
79
+ gate?: {
80
+ approvedBy: DevActor;
81
+ };
82
+ }
83
+ /** Registration of a stage deliverable produced under `artifacts/`. */
84
+ export interface JournalArtifact {
85
+ rev: number;
86
+ at: string;
87
+ type: 'artifact';
88
+ path: string;
89
+ stage: DevStage;
90
+ by?: DevActor;
91
+ }
92
+ /** Takeover of a stale lease: the previous holder's heartbeat lapsed. */
93
+ export interface JournalClaimExpired {
94
+ rev: number;
95
+ at: string;
96
+ type: 'claim-expired';
97
+ previousOwner: DevActor;
98
+ by: DevActor;
99
+ }
100
+ /** The journal entry union; the discriminant is `type`. */
101
+ export type DevflowJournalEntry = JournalCreated | JournalTransition | JournalArtifact | JournalClaimExpired;
102
+ /** Read-side value of one card, current state derived by journal replay. */
103
+ export interface DevCard {
104
+ id: DevflowCardId;
105
+ /** Resolved devflow root directory this card belongs to (absolute path). */
106
+ root: string;
107
+ /** Human title from the card file's frontmatter. */
108
+ title: string;
109
+ /** Current location derived from the journal, never from the frontmatter projection. */
110
+ stage: CardLocation;
111
+ /** Revision of the last journal entry; optimistic-concurrency token for transitions. */
112
+ stageRevision: number;
113
+ /** The stage a `blocked` card returns to on recovery; absent unless `stage` is `blocked`. */
114
+ blockedFrom?: DevStage;
115
+ /**
116
+ * The card this one decomposes; absent for a top-level card. Only one level
117
+ * exists, so a card carrying `parent` is never itself a parent.
118
+ */
119
+ parent?: DevflowCardId;
120
+ /** Markdown body of the card file below its frontmatter. */
121
+ body: string;
122
+ /** Display path of the card file. */
123
+ path: string;
124
+ /** Artifact paths registered in the journal, in registration order. */
125
+ artifacts: string[];
126
+ }
127
+ /** Read filter accepted by {@link import('./index.ts').DevflowStore.list}. */
128
+ export interface CardFilter {
129
+ /** Only cards currently at this location. */
130
+ stage?: CardLocation;
131
+ /** Only cards decomposing this one; an id with no children matches nothing. */
132
+ parent?: DevflowCardId;
133
+ }
134
+ /** Caller view of one card creation; `resolveCreate` turns it into a {@link CreateSpec}. */
135
+ export interface CreateRequest {
136
+ /** Human title recorded in the card file's frontmatter. */
137
+ title: string;
138
+ /** Markdown body below the frontmatter: the requirement and its acceptance criteria. */
139
+ body: string;
140
+ /** Directory-name slug; omitted derives one from the title. */
141
+ slug?: string;
142
+ /** Who creates the card; recorded in the journal's first entry. */
143
+ by: DevActor;
144
+ /**
145
+ * The card this one decomposes; omitted creates a top-level card. The parent
146
+ * must be an active top-level card of the same root — only one level exists.
147
+ */
148
+ parent?: DevflowCardId;
149
+ /** Devflow root receiving the card; omitted uses the implementation's default root. */
150
+ root?: string;
151
+ }
152
+ /** Fully specified creation input produced by `resolveCreate`, never a raw request. */
153
+ export interface CreateSpec extends CreateRequest {
154
+ /** The resolved directory-name slug. */
155
+ slug: string;
156
+ /** Creation timestamp stamped at resolution. */
157
+ at: string;
158
+ /** The resolved devflow root (absolute path). */
159
+ root: string;
160
+ }
161
+ /**
162
+ * Stable rejection codes of {@link CreateResult}; the discriminant is `code`.
163
+ * The parent codes name the three illegal edges: no such card in this root,
164
+ * a parent that is itself a child, and a parent past taking new work.
165
+ */
166
+ export type CreateRejectionCode = 'empty-title' | 'invalid-slug' | 'exists' | 'unknown-parent' | 'nested-parent' | 'parent-settled';
167
+ /**
168
+ * Creation outcome. Domain rejections resolve with `ok: false` and a stable
169
+ * code; only infrastructure failures (unwritable root, unreadable directory
170
+ * listing) reject the promise.
171
+ */
172
+ export type CreateResult = {
173
+ ok: true;
174
+ card: DevCard;
175
+ } | {
176
+ ok: false;
177
+ code: CreateRejectionCode;
178
+ message: string;
179
+ };
180
+ /** Caller view of one intended stage move; `resolve` turns it into a {@link TransitionSpec}. */
181
+ export interface TransitionRequest {
182
+ id: DevflowCardId;
183
+ /** Target location; legality is checked against the card's current location. */
184
+ to: CardLocation;
185
+ /** Optimistic-concurrency token: the `stageRevision` the caller last observed. */
186
+ expectedRevision: number;
187
+ /** Who requests the move; recorded in the journal on commit. */
188
+ by: DevActor;
189
+ /** Move rationale; recorded in the journal when present. */
190
+ reason?: string;
191
+ /** Devflow root holding the card; omitted uses the implementation's default root. */
192
+ root?: string;
193
+ }
194
+ /** Fully specified transition input produced by `resolve`, never a raw request. */
195
+ export interface TransitionSpec extends TransitionRequest {
196
+ /** Commit timestamp stamped at resolution. */
197
+ at: string;
198
+ /** The resolved devflow root (absolute path). */
199
+ root: string;
200
+ }
201
+ /**
202
+ * The complete attempt the `devflow/transition` waterfall decides on: the
203
+ * resolved spec plus the departure location the store derived from the
204
+ * journal. Policy listeners key gate rules on the `from -> to` edge.
205
+ */
206
+ export interface TransitionAttempt extends TransitionSpec {
207
+ from: CardLocation;
208
+ }
209
+ /** Decision value of the `devflow/transition` waterfall; not calling `next()` vetoes. */
210
+ export type TransitionDecision = {
211
+ allowed: true;
212
+ /** The human signature a policy listener collected; recorded as the journal entry's `gate.approvedBy`. */
213
+ approvedBy?: DevActor;
214
+ } | {
215
+ allowed: false;
216
+ reason: string;
217
+ };
218
+ /** Stable rejection codes of {@link TransitionResult}; the discriminant is `code`. */
219
+ export type TransitionRejectionCode = 'revision-mismatch' | 'illegal-edge' | 'reason-required' | 'vetoed';
220
+ /**
221
+ * Transition outcome. Domain rejections resolve with `ok: false` and a stable
222
+ * code; only infrastructure failures (unwritable journal, unreadable card)
223
+ * reject the promise.
224
+ */
225
+ export type TransitionResult = {
226
+ ok: true;
227
+ card: DevCard;
228
+ from: CardLocation;
229
+ } | {
230
+ ok: false;
231
+ code: TransitionRejectionCode;
232
+ message: string;
233
+ };
234
+ /** Caller view of one artifact registration against the card's current stage. */
235
+ export interface ArtifactRequest {
236
+ id: DevflowCardId;
237
+ /** Artifact path relative to the card directory, e.g. `artifacts/design.md`. */
238
+ path: string;
239
+ /** Optimistic-concurrency token: the `stageRevision` the caller last observed. */
240
+ expectedRevision: number;
241
+ by: DevActor;
242
+ /** Devflow root holding the card; omitted uses the implementation's default root. */
243
+ root?: string;
244
+ }
245
+ /** Artifact-registration outcome; domain rejections resolve like {@link TransitionResult}. */
246
+ export type ArtifactResult = {
247
+ ok: true;
248
+ card: DevCard;
249
+ } | {
250
+ ok: false;
251
+ code: 'revision-mismatch' | 'illegal-edge';
252
+ message: string;
253
+ };
254
+ /** Current lease facts of one card, read from its claim record. */
255
+ export interface ClaimHolder {
256
+ /** The lease's recorded owner. */
257
+ owner: DevActor;
258
+ /** The owner's last liveness mark (ISO timestamp; empty when the record carries none). */
259
+ heartbeatAt: string;
260
+ }
261
+ /**
262
+ * Aggregated Remote detail of one card: the read value, its complete decoded
263
+ * journal in revision order, and the current lease holder (absent while
264
+ * unclaimed).
265
+ */
266
+ export interface DevCardDetail {
267
+ /** The card's read value, consistent with `entries` (same last revision). */
268
+ card: DevCard;
269
+ /** The complete decoded journal, oldest first. */
270
+ entries: DevflowJournalEntry[];
271
+ /** The current lease holder; absent while the card is unclaimed. */
272
+ holder?: ClaimHolder;
273
+ }
274
+ /** Options accepted by {@link import('./index.ts').DevflowStore.claim}. */
275
+ export interface ClaimOptions {
276
+ /**
277
+ * Take over a held lease whose last heartbeat is older than this many
278
+ * milliseconds; the takeover is journaled as a `claim-expired` entry.
279
+ * Omitted means a held lease is never taken over.
280
+ */
281
+ staleAfterMs?: number;
282
+ /** Devflow root holding the card; omitted uses the implementation's default root. */
283
+ root?: string;
284
+ }
285
+ /** Claim outcome; a held lease resolves with the current holder instead of rejecting. */
286
+ export type ClaimResult = {
287
+ ok: true;
288
+ handle: ClaimHandle;
289
+ } | {
290
+ ok: false;
291
+ holder: DevActor;
292
+ message: string;
293
+ };
294
+ /** Live lease on one card, exclusive until released. */
295
+ export interface ClaimHandle {
296
+ id: DevflowCardId;
297
+ owner: DevActor;
298
+ /** Refresh the lease's liveness mark. */
299
+ heartbeat(): Promise<void>;
300
+ /** Release the lease; releasing twice is a no-op. */
301
+ release(): Promise<void>;
302
+ }
303
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Vocabulary types of the `ctx.devflow` capability seam: card identity, stages,
3
+ * journal entries, and the read-side card value. Runtime helpers (stage set,
4
+ * id factory, journal fold) live in `./journal.ts` and the package root.
5
+ * @module @zhchxiao123/dsh-devflow/types
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@zhchxiao123/dsh-devflow",
3
+ "description": "Service Definition for the ctx.devflow task-card capability seam of the DeepSeek Harness",
4
+ "version": "0.1.0",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/zhchxiao123/dsh-devflow-plugins.git",
11
+ "directory": "packages/devflow"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./types": {
26
+ "types": "./lib/types/types.d.ts",
27
+ "default": "./lib/types/types.js"
28
+ },
29
+ "./client": {
30
+ "types": "./lib/types/client.d.ts",
31
+ "default": "./lib/types/client.js"
32
+ },
33
+ "./src/*": "./src/*",
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "lib/index.js",
38
+ "lib/invariant.js",
39
+ "lib/types/**/*.js",
40
+ "lib/types/**/*.d.ts",
41
+ "LICENSE",
42
+ "README.md"
43
+ ],
44
+ "license": "MIT",
45
+ "dependencies": {
46
+ "zod": "^4.4.3"
47
+ },
48
+ "peerDependencies": {
49
+ "@deepseek-ai/cordis": "4.0.1",
50
+ "@deepseek-ai/dsh-brand": "0.1.1-rc.2",
51
+ "@deepseek-ai/dsh-invariants": "0.1.1-rc.2"
52
+ },
53
+ "devDependencies": {
54
+ "@deepseek-ai/cordis": "4.0.1",
55
+ "@deepseek-ai/dsh-brand": "0.1.1-rc.2",
56
+ "@deepseek-ai/dsh-invariants": "0.1.1-rc.2",
57
+ "@deepseek-ai/dsh-session": "0.1.1-rc.2",
58
+ "@deepseek-ai/dsh-session-persistence": "0.1.1-rc.2"
59
+ },
60
+ "scripts": {}
61
+ }