dsh-doublecheck 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.es.md +237 -39
- package/README.hi.md +231 -41
- package/README.md +221 -28
- package/README.pt.md +237 -39
- package/README.zh.md +313 -118
- package/lib/domain/gate.d.ts +263 -0
- package/lib/domain/gate.js +658 -0
- package/lib/domain/projection.d.ts +5 -0
- package/lib/domain/projection.js +12 -0
- package/lib/events.d.ts +44 -2
- package/lib/guard/command.js +3 -0
- package/lib/guard/gate.d.ts +170 -0
- package/lib/guard/gate.js +518 -0
- package/lib/guard/index.d.ts +12 -0
- package/lib/guard/index.js +77 -2
- package/lib/guard/prose.d.ts +17 -0
- package/lib/guard/prose.js +14 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/invariant.d.ts +2 -0
- package/lib/invariant.js +15 -0
- package/lib/types.d.ts +7 -0
- package/lib/types.js +2 -0
- package/package.json +44 -3
- package/skills/delivery-proof/SKILL.md +12 -0
- package/strict.patch.yml +37 -0
package/lib/guard/index.js
CHANGED
|
@@ -33,6 +33,8 @@ import { emptyDisciplineState, foldDisciplineRange } from "../domain/stages.js";
|
|
|
33
33
|
import { isVagueTask } from "../domain/vagueness.js";
|
|
34
34
|
import { runAdversaryReview, reviewInjection } from "./review.js";
|
|
35
35
|
import { PROSE } from "./prose.js";
|
|
36
|
+
import { gateHandler, gateInjection, GateConfigSchema, runGate, settleGate, validateGateConfig, } from "./gate.js";
|
|
37
|
+
import { countRedChecks } from "../domain/gate.js";
|
|
36
38
|
import { applyDoublecheckEvent, emptyDoublecheckState, viewDoublecheck, } from "../domain/projection.js";
|
|
37
39
|
import { doublecheckViewSchema } from "../types.js";
|
|
38
40
|
import { installInvariant, PACKAGE_NAME } from "../invariant.js";
|
|
@@ -59,6 +61,7 @@ export const Config = z.object({
|
|
|
59
61
|
testToolNames: z.array(z.string()).default([...DEFAULT_TEST_TOOL_NAMES]),
|
|
60
62
|
testCommandPatterns: z.array(z.string()).default([...DEFAULT_TEST_COMMAND_PATTERNS]),
|
|
61
63
|
testFilePatterns: z.array(z.string()).default([...DEFAULT_TEST_FILE_PATTERNS]),
|
|
64
|
+
gate: GateConfigSchema,
|
|
62
65
|
});
|
|
63
66
|
/**
|
|
64
67
|
* Install the guard listeners.
|
|
@@ -68,6 +71,7 @@ export const Config = z.object({
|
|
|
68
71
|
export function apply(ctx, config) {
|
|
69
72
|
assertPositiveInteger('vagueTaskMaxChars', config.vagueTaskMaxChars);
|
|
70
73
|
assertGuardTools(config.guardTools);
|
|
74
|
+
validateGateConfig(config.gate);
|
|
71
75
|
if (config.modules.adversary) {
|
|
72
76
|
assertBoundedInteger('adversaryMaxFindings', config.adversaryMaxFindings, 20);
|
|
73
77
|
assertPositiveInteger('adversaryTimeoutMs', config.adversaryTimeoutMs);
|
|
@@ -130,6 +134,9 @@ export function apply(ctx, config) {
|
|
|
130
134
|
snapshot.lastReviewSeq = event.seq;
|
|
131
135
|
snapshot.editsAfterReview = 0;
|
|
132
136
|
}
|
|
137
|
+
else if (source.kind === 'doublecheck-gate') {
|
|
138
|
+
snapshot.gateRedReminded = true;
|
|
139
|
+
}
|
|
133
140
|
else if (source.kind === 'user') {
|
|
134
141
|
const parts = [];
|
|
135
142
|
for (const block of event.data.content) {
|
|
@@ -146,6 +153,14 @@ export function apply(ctx, config) {
|
|
|
146
153
|
else if (event.type === 'doublecheck/state') {
|
|
147
154
|
snapshot.stateEnabled = event.data.enabled;
|
|
148
155
|
}
|
|
156
|
+
else if (event.type === 'doublecheck/gate') {
|
|
157
|
+
const gate = event.data;
|
|
158
|
+
snapshot.lastGate = {
|
|
159
|
+
seq: event.seq,
|
|
160
|
+
verdict: gate.verdict,
|
|
161
|
+
redCount: gate.phases === undefined ? 0 : countRedChecks(Object.values(gate.phases)),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
149
164
|
else if (event.type === 'tool/call' && event.seq > snapshot.lastReviewSeq) {
|
|
150
165
|
// Implementation edits after the latest review re-arm the adversary
|
|
151
166
|
// trigger for a second review round.
|
|
@@ -173,6 +188,8 @@ export function apply(ctx, config) {
|
|
|
173
188
|
reportReminded: false,
|
|
174
189
|
lastReviewSeq: -1,
|
|
175
190
|
editsAfterReview: 0,
|
|
191
|
+
lastGate: null,
|
|
192
|
+
gateRedReminded: false,
|
|
176
193
|
};
|
|
177
194
|
foldEvents(snapshot, events, 0, events.length);
|
|
178
195
|
snapshots.set(session, snapshot);
|
|
@@ -237,8 +254,66 @@ export function apply(ctx, config) {
|
|
|
237
254
|
setLocalOverride: (session, enabled) => localOverrides.set(session, enabled),
|
|
238
255
|
}),
|
|
239
256
|
});
|
|
257
|
+
// The delivery gate panel: `/gate` stays available even when the gate or
|
|
258
|
+
// every discipline module is off — it is the user's inspection surface.
|
|
259
|
+
ctx.commands.register({
|
|
260
|
+
name: 'gate',
|
|
261
|
+
description: 'run the delivery quality gate and report the deliverable/rework decision',
|
|
262
|
+
input: { hint: 'status|run|config' },
|
|
263
|
+
handler: gateHandler({
|
|
264
|
+
config: config.gate,
|
|
265
|
+
detection,
|
|
266
|
+
prose,
|
|
267
|
+
runGate: (agent, signal) => runGate(ctx, config.gate, detection, agent, signal, config.language),
|
|
268
|
+
settleGate: (state, agent, signal) => settleGate(ctx, config.gate, state, agent, signal, hostStampsIgnorable()),
|
|
269
|
+
stampsIgnorable: hostStampsIgnorable,
|
|
270
|
+
planMode: ctx.get('planMode'),
|
|
271
|
+
}),
|
|
272
|
+
});
|
|
273
|
+
// The gate settings namespace: the pluggable checklist becomes editable
|
|
274
|
+
// through the harness settings surface when one is mounted (weak seam —
|
|
275
|
+
// a profile without the settings service simply has no settings page).
|
|
276
|
+
const settings = ctx.get('settings');
|
|
277
|
+
if (settings !== undefined) {
|
|
278
|
+
try {
|
|
279
|
+
settings.register('doublecheck.gate', GateConfigSchema, {
|
|
280
|
+
base: config.gate,
|
|
281
|
+
applies: 'restart',
|
|
282
|
+
expose: true,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
ctx.logger.warn(`dsh-doublecheck: gate settings namespace skipped: ${String(error)}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// The gate-red turn notice: once per session, a settled rework verdict
|
|
290
|
+
// suggests re-opening the work in plan mode. Installed even when every
|
|
291
|
+
// discipline module is off — the gate panel is advisory, not a gate.
|
|
292
|
+
if (config.gate.enabled) {
|
|
293
|
+
ctx.on('agent/turn-stopping', async ({ agent }) => {
|
|
294
|
+
if (!doublecheckEnabled(agent.session))
|
|
295
|
+
return;
|
|
296
|
+
const snapshot = snapshotOf(agent.session);
|
|
297
|
+
if (snapshot.gateRedReminded)
|
|
298
|
+
return;
|
|
299
|
+
const gate = snapshot.lastGate;
|
|
300
|
+
if (gate === null || gate.verdict !== 'rework')
|
|
301
|
+
return;
|
|
302
|
+
snapshot.gateRedReminded = true;
|
|
303
|
+
const text = prose.gateRedNotice(gate.redCount);
|
|
304
|
+
agent.inject(gateInjection(gate.verdict, gate.redCount, text));
|
|
305
|
+
ctx.emit('doublecheck/reminder', {
|
|
306
|
+
agent,
|
|
307
|
+
session: agent.session,
|
|
308
|
+
intensity: config.intensity,
|
|
309
|
+
gate: 'gate',
|
|
310
|
+
verdict: 'gate-red',
|
|
311
|
+
reminder: text,
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
}
|
|
240
315
|
if (!config.modules.grill && !config.modules.tdd && !config.modules.adversary) {
|
|
241
|
-
ctx.logger.info('dsh-doublecheck: all discipline gates disabled;
|
|
316
|
+
ctx.logger.info('dsh-doublecheck: all discipline gates disabled; the /doublecheck and /gate commands remain active');
|
|
242
317
|
return;
|
|
243
318
|
}
|
|
244
319
|
// The policy gate. Observe-and-decide: the grill gate owns vague,
|
|
@@ -456,7 +531,7 @@ export function apply(ctx, config) {
|
|
|
456
531
|
init: emptyDoublecheckState,
|
|
457
532
|
apply: (state, event) => applyDoublecheckEvent(state, event, detection),
|
|
458
533
|
view: viewDoublecheck,
|
|
459
|
-
stateVersion:
|
|
534
|
+
stateVersion: 2,
|
|
460
535
|
});
|
|
461
536
|
});
|
|
462
537
|
// The invariant companion, registered from the main plugin so its checks
|
package/lib/guard/prose.d.ts
CHANGED
|
@@ -38,6 +38,18 @@ export interface GuardProse {
|
|
|
38
38
|
reviewHeldBack: (held: number) => string;
|
|
39
39
|
/** The critic's task prompt (model-facing behavior of the review run). */
|
|
40
40
|
criticTask: string;
|
|
41
|
+
/** The gate-red notice injected at the turn boundary (role statement first). */
|
|
42
|
+
gateRedNotice: (redCount: number) => string;
|
|
43
|
+
/** The localized `/gate status` panel header. */
|
|
44
|
+
gateStatusHeader: string;
|
|
45
|
+
/** The localized `/gate status` usage hint. */
|
|
46
|
+
gateStatusHint: string;
|
|
47
|
+
/** The localized `/gate config` header. */
|
|
48
|
+
gateConfigHeader: string;
|
|
49
|
+
/** The localized `/gate` usage error. */
|
|
50
|
+
gateCommandUnknown: (input: string) => string;
|
|
51
|
+
/** The localized `/gate` no-agent error. */
|
|
52
|
+
gateCommandNoAgent: string;
|
|
41
53
|
switchOnDurable: string;
|
|
42
54
|
switchOnLocal: string;
|
|
43
55
|
switchOffDurable: string;
|
|
@@ -76,6 +88,11 @@ export interface CommandStatusFacts {
|
|
|
76
88
|
reviewed: boolean;
|
|
77
89
|
/** Total implementation edits folded so far. */
|
|
78
90
|
editCount: number;
|
|
91
|
+
/** The latest durable gate run, or null before any. */
|
|
92
|
+
gate: {
|
|
93
|
+
verdict: string;
|
|
94
|
+
redCount: number;
|
|
95
|
+
} | null;
|
|
79
96
|
}
|
|
80
97
|
/** The localized prose tables by language. */
|
|
81
98
|
export declare const PROSE: Readonly<Record<ProseLanguage, GuardProse>>;
|
package/lib/guard/prose.js
CHANGED
|
@@ -74,6 +74,12 @@ const EN = {
|
|
|
74
74
|
+ 'what in the session supports it. If — and only if — the evidence '
|
|
75
75
|
+ 'genuinely satisfies every dimension, return an empty findings list. Do '
|
|
76
76
|
+ 'not invent objections; the empty answer is correct when nothing is wrong.',
|
|
77
|
+
gateRedNotice: redCount => `You are the delivery gate panel for this session. The latest gate run is RED with ${redCount} red item(s). Re-open the work in plan mode to re-check, or run /gate run for the full checklist.`,
|
|
78
|
+
gateStatusHeader: 'Delivery gate panel — checklist progress for this session:',
|
|
79
|
+
gateStatusHint: 'Deterministic phases fold live; the reviewer phases show the latest /gate run. Run /gate run to settle everything; /gate config shows the checklist.',
|
|
80
|
+
gateConfigHeader: 'Delivery gate configuration for this session:',
|
|
81
|
+
gateCommandUnknown: input => `Unknown /gate argument "${input}". Usage: /gate status|run|config`,
|
|
82
|
+
gateCommandNoAgent: '/gate needs an agent session to inspect.',
|
|
77
83
|
switchOnDurable: 'Double-check discipline was switched ON for this session (changed by the user): the requirements grill, the red/green gates, and the delivery review are active again.',
|
|
78
84
|
switchOnLocal: 'Double-check discipline was switched ON for this process only (changed by the user; this harness build predates the ignorable append surface, so the switch is not durable): the requirements grill, the red/green gates, and the delivery review are active again.',
|
|
79
85
|
switchOffDurable: 'Double-check discipline was switched OFF for this session (changed by the user): the discipline gates defer to the human approval chain until switched back on.',
|
|
@@ -91,6 +97,7 @@ const EN = {
|
|
|
91
97
|
`Modules: grill=${facts.modules.grill ? 'on' : 'off'}, tdd=${facts.modules.tdd ? 'on' : 'off'}, adversary=${facts.modules.adversary ? 'on' : 'off'} (cordis.yml).`,
|
|
92
98
|
`Intensity: ${facts.intensity}. Default switch: ${facts.defaultEnabled ? 'on' : 'off'}. remindOnce: ${facts.remindOnce ? 'on' : 'off'}.`,
|
|
93
99
|
`Stage: spec=${facts.hasSpec ? 'committed' : 'missing'}, tests=${facts.color}, review=${facts.reviewed ? 'on record' : 'not run'}, edits=${facts.editCount}.`,
|
|
100
|
+
`Gate: ${facts.gate === null ? 'not run' : `${facts.gate.verdict} (${facts.gate.redCount} red item(s))`} (/gate status|run|config).`,
|
|
94
101
|
`Usage: /doublecheck status|report|on|off`,
|
|
95
102
|
].join('\n'),
|
|
96
103
|
};
|
|
@@ -121,6 +128,12 @@ const ZH = {
|
|
|
121
128
|
+ '验收标准、范围或非目标被违反、未处理的失败模式。通过要求的结构化输出作答,'
|
|
122
129
|
+ '每条反对意见占一项,并引用会话中支撑它的内容。当且仅当证据确实满足每一个维度时,'
|
|
123
130
|
+ '才返回空的 findings 列表。不要编造反对意见;没有问题时空答案才是正确的。',
|
|
131
|
+
gateRedNotice: redCount => `你是本会话的交付门禁面板。最近一次门禁结论为红灯,共 ${redCount} 项红灯。建议转 plan mode 复查,或运行 /gate run 查看完整检查单。`,
|
|
132
|
+
gateStatusHeader: '交付门禁面板 — 本会话的检查单进度:',
|
|
133
|
+
gateStatusHint: '确定性阶段实时折叠;评审类阶段显示最近一次 /gate run 的结果。运行 /gate run 结算全部检查;/gate config 查看检查单配置。',
|
|
134
|
+
gateConfigHeader: '本会话的交付门禁配置:',
|
|
135
|
+
gateCommandUnknown: input => `未知的 /gate 参数 "${input}"。用法:/gate status|run|config`,
|
|
136
|
+
gateCommandNoAgent: '/gate 需要代理会话才能查看。',
|
|
124
137
|
switchOnDurable: 'Double-check 纪律已对本会话开启(由用户切换):需求盘问、红绿门和交付审查恢复生效。',
|
|
125
138
|
switchOnLocal: 'Double-check 纪律已对本进程开启(由用户切换;此 harness 版本早于 ignorable 追加接口,切换不持久):需求盘问、红绿门和交付审查恢复生效。',
|
|
126
139
|
switchOffDurable: 'Double-check 纪律已对本会话关闭(由用户切换):在重新开启之前,纪律门让位于人工审批链。',
|
|
@@ -138,6 +151,7 @@ const ZH = {
|
|
|
138
151
|
`模块:grill=${facts.modules.grill ? '开' : '关'}, tdd=${facts.modules.tdd ? '开' : '关'}, adversary=${facts.modules.adversary ? '开' : '关'}(cordis.yml)。`,
|
|
139
152
|
`强度:${facts.intensity}。默认开关:${facts.defaultEnabled ? '开' : '关'}。remindOnce:${facts.remindOnce ? '开' : '关'}。`,
|
|
140
153
|
`阶段:spec=${facts.hasSpec ? '已提交' : '缺失'}, tests=${facts.color}, review=${facts.reviewed ? '有记录' : '未运行'}, edits=${facts.editCount}。`,
|
|
154
|
+
`门禁:${facts.gate === null ? '未运行' : `${facts.gate.verdict}(${facts.gate.redCount} 项红灯)`}(/gate status|run|config)。`,
|
|
141
155
|
'用法:/doublecheck status|report|on|off',
|
|
142
156
|
].join('\n'),
|
|
143
157
|
};
|
package/lib/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from './domain/evidence.ts';
|
|
|
12
12
|
export * from './domain/vagueness.ts';
|
|
13
13
|
export * from './domain/vocabulary.ts';
|
|
14
14
|
export * from './domain/report.ts';
|
|
15
|
+
export * from './domain/gate.ts';
|
|
15
16
|
export { renderSpecMarkdown } from './grill/index.ts';
|
|
16
17
|
export { BundledSkillProvider, parseSkillAsset, PROVIDER_NAME } from './grill/provider.ts';
|
|
17
18
|
export type * from './types.ts';
|
package/lib/index.js
CHANGED
|
@@ -11,5 +11,6 @@ export * from "./domain/evidence.js";
|
|
|
11
11
|
export * from "./domain/vagueness.js";
|
|
12
12
|
export * from "./domain/vocabulary.js";
|
|
13
13
|
export * from "./domain/report.js";
|
|
14
|
+
export * from "./domain/gate.js";
|
|
14
15
|
export { renderSpecMarkdown } from "./grill/index.js";
|
|
15
16
|
export { BundledSkillProvider, parseSkillAsset, PROVIDER_NAME } from "./grill/provider.js";
|
package/lib/invariant.d.ts
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* the tool result, not this event, so equality is not checkable there).
|
|
15
15
|
* 3. Every `doublecheck/review` announcement's findings satisfy the
|
|
16
16
|
* structured finding shape (severity enum, non-empty title and detail).
|
|
17
|
+
* 4. Every `doublecheck/gate` announcement's verdict matches the re-derivation
|
|
18
|
+
* from its own phase results, and all four phases are present.
|
|
17
19
|
*
|
|
18
20
|
* The guard registers a facts-bearing installer from its own context; the
|
|
19
21
|
* `./invariant` export is the standalone companion usable through a separate
|
package/lib/invariant.js
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* the tool result, not this event, so equality is not checkable there).
|
|
15
15
|
* 3. Every `doublecheck/review` announcement's findings satisfy the
|
|
16
16
|
* structured finding shape (severity enum, non-empty title and detail).
|
|
17
|
+
* 4. Every `doublecheck/gate` announcement's verdict matches the re-derivation
|
|
18
|
+
* from its own phase results, and all four phases are present.
|
|
17
19
|
*
|
|
18
20
|
* The guard registers a facts-bearing installer from its own context; the
|
|
19
21
|
* `./invariant` export is the standalone companion usable through a separate
|
|
@@ -22,6 +24,7 @@
|
|
|
22
24
|
*/
|
|
23
25
|
import { SPEC_FIELD_NAMES } from "./domain/vocabulary.js";
|
|
24
26
|
import { deriveReportVerdict, foldReportFacts } from "./domain/report.js";
|
|
27
|
+
import { deriveGateVerdict, GATE_PHASES } from "./domain/gate.js";
|
|
25
28
|
/** Full npm package name owning the reported failures. */
|
|
26
29
|
export const PACKAGE_NAME = 'dsh-doublecheck';
|
|
27
30
|
/** Cordis companion plugin name. */
|
|
@@ -80,6 +83,18 @@ export function installInvariant(facts) {
|
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
});
|
|
86
|
+
ctx.on('doublecheck/gate', (payload) => {
|
|
87
|
+
const state = payload.state;
|
|
88
|
+
const derived = deriveGateVerdict(GATE_PHASES.map(phase => state.phases[phase]).filter(phase => phase !== undefined));
|
|
89
|
+
if (derived !== state.verdict) {
|
|
90
|
+
fail(`doublecheck/gate announces verdict "${state.verdict}" but its phases derive "${derived}"`);
|
|
91
|
+
}
|
|
92
|
+
for (const phase of GATE_PHASES) {
|
|
93
|
+
if (state.phases[phase] === undefined) {
|
|
94
|
+
fail(`doublecheck/gate carries no "${phase}" phase result`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
83
98
|
};
|
|
84
99
|
}
|
|
85
100
|
/**
|
package/lib/types.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { z as zod } from 'zod';
|
|
10
10
|
import type { DisciplineStage, TestColor } from './domain/stages.ts';
|
|
11
|
+
import type { GateVerdict } from './domain/gate.ts';
|
|
11
12
|
/** The whole wire value of the `doublecheck` session projection. */
|
|
12
13
|
export interface DoublecheckView {
|
|
13
14
|
/** The last discipline stage reached on durable evidence. */
|
|
@@ -22,6 +23,10 @@ export interface DoublecheckView {
|
|
|
22
23
|
reviewed: boolean;
|
|
23
24
|
/** Total implementation edits folded so far. */
|
|
24
25
|
editCount: number;
|
|
26
|
+
/** The latest delivery-gate verdict, or 'none' before any gate run. */
|
|
27
|
+
gateVerdict: 'none' | GateVerdict;
|
|
28
|
+
/** Red-light (failing) checks of the latest gate run. */
|
|
29
|
+
gateRedCount: number;
|
|
25
30
|
}
|
|
26
31
|
/** Validates the `doublecheck` projection's wire payload before it leaves the host. */
|
|
27
32
|
export declare const doublecheckViewSchema: zod.ZodObject<{
|
|
@@ -31,6 +36,8 @@ export declare const doublecheckViewSchema: zod.ZodObject<{
|
|
|
31
36
|
specGoal: zod.ZodString;
|
|
32
37
|
reviewed: zod.ZodBoolean;
|
|
33
38
|
editCount: zod.ZodNumber;
|
|
39
|
+
gateVerdict: zod.ZodUnion<readonly [zod.ZodLiteral<"none">, zod.ZodLiteral<"deliverable">, zod.ZodLiteral<"rework">]>;
|
|
40
|
+
gateRedCount: zod.ZodNumber;
|
|
34
41
|
}, zod.core.$strip>;
|
|
35
42
|
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
36
43
|
interface SessionProjectionMap {
|
package/lib/types.js
CHANGED
|
@@ -15,4 +15,6 @@ export const doublecheckViewSchema = zod.object({
|
|
|
15
15
|
specGoal: zod.string(),
|
|
16
16
|
reviewed: zod.boolean(),
|
|
17
17
|
editCount: zod.number(),
|
|
18
|
+
gateVerdict: zod.union([zod.literal('none'), zod.literal('deliverable'), zod.literal('rework')]),
|
|
19
|
+
gateRedCount: zod.number(),
|
|
18
20
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-doublecheck",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Delivery quality gate for DeepSeek Harness: grill the requirements, test the implementation, prove the delivery, and gate the handoff with a deliverable/rework decision.",
|
|
5
5
|
"packageManager": "pnpm@11.7.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -67,7 +67,9 @@
|
|
|
67
67
|
"engineering-discipline",
|
|
68
68
|
"requirements",
|
|
69
69
|
"guard",
|
|
70
|
-
"skill"
|
|
70
|
+
"skill",
|
|
71
|
+
"quality-gate",
|
|
72
|
+
"delivery-gate"
|
|
71
73
|
],
|
|
72
74
|
"author": "PerryLink",
|
|
73
75
|
"license": "Apache-2.0",
|
|
@@ -79,6 +81,45 @@
|
|
|
79
81
|
"patch": "./cordis.patch.yml"
|
|
80
82
|
}
|
|
81
83
|
},
|
|
84
|
+
"dshWorkshop": {
|
|
85
|
+
"schema": "omdsh-workshop-package/v1",
|
|
86
|
+
"type": "plugin",
|
|
87
|
+
"integration": {
|
|
88
|
+
"protocol": "harness-profile",
|
|
89
|
+
"artifact": "cordis.patch.yml"
|
|
90
|
+
},
|
|
91
|
+
"install": {
|
|
92
|
+
"mode": "transactional",
|
|
93
|
+
"adapter": "profile-bundle",
|
|
94
|
+
"failurePolicy": "generation-rollback",
|
|
95
|
+
"touchesCurrentBeforeActivation": false
|
|
96
|
+
},
|
|
97
|
+
"lifecycle": {
|
|
98
|
+
"activation": "restart-profile",
|
|
99
|
+
"dispose": "supported"
|
|
100
|
+
},
|
|
101
|
+
"permissions": [
|
|
102
|
+
"filesystem:read",
|
|
103
|
+
"filesystem:write"
|
|
104
|
+
],
|
|
105
|
+
"compatibility": {
|
|
106
|
+
"dshVersions": [
|
|
107
|
+
"0.1.0-rc.6"
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
"capability": {
|
|
111
|
+
"id": "doublecheck",
|
|
112
|
+
"kind": "command",
|
|
113
|
+
"invocation": "send /gate status in a session with the plugin rows loaded",
|
|
114
|
+
"expected": "a reply reporting the live checklist progress (requirements/test evidence phases), the latest gate run verdict with red items, and the plan-mode state"
|
|
115
|
+
},
|
|
116
|
+
"evidence": {
|
|
117
|
+
"install": null,
|
|
118
|
+
"failureIsolation": null,
|
|
119
|
+
"hotReload": null,
|
|
120
|
+
"remove": null
|
|
121
|
+
}
|
|
122
|
+
},
|
|
82
123
|
"peerDependencies": {
|
|
83
124
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
84
125
|
"@deepseek-ai/dsh-agent": "0.1.0-rc.6",
|
|
@@ -32,6 +32,18 @@ for a proof, a delivery summary, or a "what did we actually do" record.
|
|
|
32
32
|
proven and must not be described as such; `unverified` means not every
|
|
33
33
|
dimension returned a verdict.
|
|
34
34
|
|
|
35
|
+
## Passing the delivery gate
|
|
36
|
+
|
|
37
|
+
Before claiming the delivery is ready to ship, run the quality gate:
|
|
38
|
+
|
|
39
|
+
1. Run `/gate run`. It settles the four-phase checklist — requirements
|
|
40
|
+
interrogation, test evidence, implementation consistency, and the review
|
|
41
|
+
conclusion — and returns a markdown report with a binary decision:
|
|
42
|
+
**deliverable** or **rework required**.
|
|
43
|
+
2. On a rework verdict, fix the red items and re-run `/gate run`; the report
|
|
44
|
+
suggests re-opening the work in plan mode to re-check. Paste the final
|
|
45
|
+
report into the PR description when the decision is deliverable.
|
|
46
|
+
|
|
35
47
|
## Reading the verdict
|
|
36
48
|
|
|
37
49
|
- **grill / draft / red** — the discipline loop is not complete. State what
|
package/strict.patch.yml
CHANGED
|
@@ -31,3 +31,40 @@
|
|
|
31
31
|
testFilePatterns:
|
|
32
32
|
- '(^|[\\/])(tests?|__tests__|specs?)([\\/]|$)'
|
|
33
33
|
- '\\.(test|spec)\\.[A-Za-z0-9]+$'
|
|
34
|
+
gate:
|
|
35
|
+
enabled: true
|
|
36
|
+
planSuggestion: true
|
|
37
|
+
reportFile: 'gate-report.md'
|
|
38
|
+
requirements:
|
|
39
|
+
enabled: true
|
|
40
|
+
checklist:
|
|
41
|
+
- { id: goal, question: 'What outcome must the delivery produce?', specDimension: goal, required: true }
|
|
42
|
+
- { id: scope, question: 'What is in scope, and what is out of scope?', specDimension: scope, required: true }
|
|
43
|
+
- { id: acceptance, question: 'Which observable checks prove the work is done?', specDimension: acceptanceCriteria, required: true }
|
|
44
|
+
- { id: failureModes, question: 'What can go wrong, and what is the correct behavior in each case?', specDimension: failureModes, required: true }
|
|
45
|
+
- { id: priorities, question: 'What is traded when goals conflict; what is optional?', specDimension: priorities, required: true }
|
|
46
|
+
- { id: nonGoals, question: 'What does the user explicitly not want?', specDimension: nonGoals, required: true }
|
|
47
|
+
minConfirmed: 6
|
|
48
|
+
interrogateTool: 'ask_user_question'
|
|
49
|
+
tests:
|
|
50
|
+
enabled: true
|
|
51
|
+
requirePassingRun: true
|
|
52
|
+
allowFailingRuns: 0
|
|
53
|
+
requireCoverage: true
|
|
54
|
+
minCoveragePct: 80
|
|
55
|
+
coveragePattern: 'coverage[^\\d]{0,40}(\\d+(?:\\.\\d+)?)\\s*%'
|
|
56
|
+
consistency:
|
|
57
|
+
enabled: true
|
|
58
|
+
provider: fork
|
|
59
|
+
model: null
|
|
60
|
+
tools: ['read', 'glob', 'grep']
|
|
61
|
+
timeoutMs: 120000
|
|
62
|
+
maxFindings: 5
|
|
63
|
+
review:
|
|
64
|
+
enabled: true
|
|
65
|
+
engine: auto
|
|
66
|
+
provider: fork
|
|
67
|
+
model: null
|
|
68
|
+
tools: ['read', 'glob', 'grep']
|
|
69
|
+
timeoutMs: 120000
|
|
70
|
+
maxFindings: 5
|