@hegemonart/get-design-done 1.58.0 → 1.59.1

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.
@@ -29,6 +29,13 @@
29
29
  "argument_hint": "[--retroactive] [--quick] [--no-reflect]",
30
30
  "tools": "Read, Write, Task, Glob, Bash"
31
31
  },
32
+ {
33
+ "name": "bandit-reset",
34
+ "description": "Confirm-then-reset the per-(agent, bin, delegate) bandit posterior - backs up .design/telemetry/posterior.json to posterior.json.bak, then clears it to a fresh empty envelope. Mutation companion to read-only bandit-status. Use when the posterior is corrupted/unparseable, after a major agent/skill roster change invalidates accumulated arms, or when you deliberately want to rebootstrap adaptive routing from informed priors.",
35
+ "argument_hint": "[--yes to skip the confirmation prompt]",
36
+ "tools": "Read, Write, Bash, AskUserQuestion",
37
+ "disable_model_invocation": true
38
+ },
32
39
  {
33
40
  "name": "bandit-status",
34
41
  "description": "Surface read-only per-(agent, bin, delegate) bandit posterior snapshot - alpha/beta/mean/stddev/count/last-used per arm. Phase 27.5 (v1.27.5) diagnostic. Use when investigating 'why did the bandit pick tier X for agent Y?' or when verifying posterior convergence after enabling adaptive_mode: full.",
@@ -89,6 +89,11 @@ export interface AuditReport {
89
89
  readonly connections: readonly ConnectionReport[];
90
90
  readonly must_haves: readonly MustHaveReport[];
91
91
  readonly baseline?: BaselineReport;
92
+ // `true` when there is no active cycle (.design/STATE.md absent). The audit
93
+ // then runs only the static checks that do not require cycle state and exits
94
+ // 0 with this flag set, rather than failing. Omitted (undefined) on a normal
95
+ // run with an active cycle.
96
+ readonly degraded?: boolean;
92
97
  readonly summary: {
93
98
  readonly connections_ok: boolean;
94
99
  readonly must_haves_ok: boolean;
@@ -135,14 +140,23 @@ export async function auditCommand(
135
140
 
136
141
  const cwd: string =
137
142
  typeof flags['cwd'] === 'string' ? (flags['cwd'] as string) : process.cwd();
138
- const statePath: string =
139
- typeof flags['state-path'] === 'string' && (flags['state-path'] as string).length > 0
140
- ? resolvePath(cwd, flags['state-path'] as string)
141
- : resolvePath(cwd, '.design', 'STATE.md');
143
+ const explicitStatePath: boolean =
144
+ typeof flags['state-path'] === 'string' && (flags['state-path'] as string).length > 0;
145
+ const statePath: string = explicitStatePath
146
+ ? resolvePath(cwd, flags['state-path'] as string)
147
+ : resolvePath(cwd, '.design', 'STATE.md');
142
148
 
143
149
  if (!existsSync(statePath)) {
144
- stderr.write(`gdd-sdk audit: STATE.md not found at ${statePath}\n`);
145
- return 3;
150
+ // An explicit --state-path that does not exist is an arg error: the
151
+ // caller pointed us at a specific file that should be present.
152
+ if (explicitStatePath) {
153
+ stderr.write(`gdd-sdk audit: STATE.md not found at ${statePath}\n`);
154
+ return 3;
155
+ }
156
+ // No active cycle (default .design/STATE.md absent). Graceful degrade:
157
+ // emit a clear message + a degraded report covering the static checks
158
+ // that do not require cycle state, then exit 0 — never throw.
159
+ return emitDegraded(flags, stdout, stderr);
146
160
  }
147
161
 
148
162
  const readFn = deps.readState ?? read;
@@ -218,6 +232,46 @@ export async function auditCommand(
218
232
  return overallOk ? 0 : 1;
219
233
  }
220
234
 
235
+ // ---------------------------------------------------------------------------
236
+ // Degraded (no active cycle) path.
237
+ // ---------------------------------------------------------------------------
238
+
239
+ /**
240
+ * Emit a degraded audit report when there is no active cycle (no
241
+ * `.design/STATE.md`). Connections + must-haves are sourced from STATE.md,
242
+ * so without a cycle there is nothing cycle-bound to evaluate; we report
243
+ * empty sets and exit 0. The `degraded` flag signals callers (and the JSON
244
+ * consumers) that this was a no-cycle run, not a clean active-cycle audit.
245
+ */
246
+ function emitDegraded(
247
+ flags: Record<string, unknown>,
248
+ stdout: NodeJS.WritableStream,
249
+ stderr: NodeJS.WritableStream,
250
+ ): number {
251
+ // Human-facing notice on stderr so JSON on stdout stays machine-parseable.
252
+ stderr.write('gdd-sdk audit: no active cycle — run /gdd:start\n');
253
+
254
+ const report: AuditReport = {
255
+ connections: Object.freeze([]),
256
+ must_haves: Object.freeze([]),
257
+ degraded: true,
258
+ summary: {
259
+ connections_ok: true,
260
+ must_haves_ok: true,
261
+ baseline_ok: true,
262
+ overall_ok: true,
263
+ },
264
+ };
265
+
266
+ if (flags['json'] === true) {
267
+ stdout.write(JSON.stringify(report, null, 2) + '\n');
268
+ } else {
269
+ stdout.write(renderHuman(report));
270
+ }
271
+
272
+ return 0;
273
+ }
274
+
221
275
  // ---------------------------------------------------------------------------
222
276
  // Baseline comparison.
223
277
  // ---------------------------------------------------------------------------
@@ -352,6 +406,12 @@ function parseBaselineStateSync(raw: string): Pick<ParsedState, 'connections' |
352
406
 
353
407
  function renderHuman(report: AuditReport): string {
354
408
  const lines: string[] = [];
409
+ if (report.degraded === true) {
410
+ lines.push('audit: degraded (no active cycle — run /gdd:start)');
411
+ lines.push('');
412
+ lines.push('No active cycle: skipped connection + must-have checks.');
413
+ return lines.join('\n') + '\n';
414
+ }
355
415
  lines.push(`audit: ${report.summary.overall_ok ? 'clean' : 'REGRESSIONS'}`);
356
416
  lines.push('');
357
417
  lines.push(`connections (${report.summary.connections_ok ? 'ok' : 'degraded'}):`);
package/sdk/cli/index.js CHANGED
@@ -8774,11 +8774,15 @@ async function auditCommand(args, deps = {}) {
8774
8774
  return 3;
8775
8775
  }
8776
8776
  const cwd = typeof flags["cwd"] === "string" ? flags["cwd"] : process.cwd();
8777
- const statePath = typeof flags["state-path"] === "string" && flags["state-path"].length > 0 ? (0, import_node_path17.resolve)(cwd, flags["state-path"]) : (0, import_node_path17.resolve)(cwd, ".design", "STATE.md");
8777
+ const explicitStatePath = typeof flags["state-path"] === "string" && flags["state-path"].length > 0;
8778
+ const statePath = explicitStatePath ? (0, import_node_path17.resolve)(cwd, flags["state-path"]) : (0, import_node_path17.resolve)(cwd, ".design", "STATE.md");
8778
8779
  if (!(0, import_node_fs17.existsSync)(statePath)) {
8779
- stderr.write(`gdd-sdk audit: STATE.md not found at ${statePath}
8780
+ if (explicitStatePath) {
8781
+ stderr.write(`gdd-sdk audit: STATE.md not found at ${statePath}
8780
8782
  `);
8781
- return 3;
8783
+ return 3;
8784
+ }
8785
+ return emitDegraded(flags, stdout, stderr);
8782
8786
  }
8783
8787
  const readFn = deps.readState ?? read;
8784
8788
  let state;
@@ -8841,6 +8845,26 @@ async function auditCommand(args, deps = {}) {
8841
8845
  }
8842
8846
  return overallOk ? 0 : 1;
8843
8847
  }
8848
+ function emitDegraded(flags, stdout, stderr) {
8849
+ stderr.write("gdd-sdk audit: no active cycle \u2014 run /gdd:start\n");
8850
+ const report = {
8851
+ connections: Object.freeze([]),
8852
+ must_haves: Object.freeze([]),
8853
+ degraded: true,
8854
+ summary: {
8855
+ connections_ok: true,
8856
+ must_haves_ok: true,
8857
+ baseline_ok: true,
8858
+ overall_ok: true
8859
+ }
8860
+ };
8861
+ if (flags["json"] === true) {
8862
+ stdout.write(JSON.stringify(report, null, 2) + "\n");
8863
+ } else {
8864
+ stdout.write(renderHuman(report));
8865
+ }
8866
+ return 0;
8867
+ }
8844
8868
  function computeBaselineDrift(current, baselineDir) {
8845
8869
  const baselinePath = (0, import_node_path17.resolve)(baselineDir, "STATE.md");
8846
8870
  if (!(0, import_node_fs17.existsSync)(baselinePath)) {
@@ -8923,6 +8947,12 @@ function parseBaselineStateSync(raw) {
8923
8947
  }
8924
8948
  function renderHuman(report) {
8925
8949
  const lines = [];
8950
+ if (report.degraded === true) {
8951
+ lines.push("audit: degraded (no active cycle \u2014 run /gdd:start)");
8952
+ lines.push("");
8953
+ lines.push("No active cycle: skipped connection + must-have checks.");
8954
+ return lines.join("\n") + "\n";
8955
+ }
8926
8956
  lines.push(`audit: ${report.summary.overall_ok ? "clean" : "REGRESSIONS"}`);
8927
8957
  lines.push("");
8928
8958
  lines.push(`connections (${report.summary.connections_ok ? "ok" : "degraded"}):`);
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: gdd-bandit-reset
3
+ description: "Confirm-then-reset the per-(agent, bin, delegate) bandit posterior - backs up .design/telemetry/posterior.json to posterior.json.bak, then clears it to a fresh empty envelope. Mutation companion to read-only bandit-status. Use when the posterior is corrupted/unparseable, after a major agent/skill roster change invalidates accumulated arms, or when you deliberately want to rebootstrap adaptive routing from informed priors."
4
+ argument-hint: "[--yes to skip the confirmation prompt]"
5
+ tools: Read, Write, Bash, AskUserQuestion
6
+ disable-model-invocation: true
7
+ ---
8
+
9
+ # gdd-bandit-reset
10
+
11
+ ## Role
12
+
13
+ You are a deterministic, destructive maintenance skill. You are the ONLY skill that clears the bandit posterior - the mutation companion to read-only `/gdd:bandit-status`. You read the posterior path declared by `scripts/lib/bandit-router.cjs`'s `DEFAULT_POSTERIOR_PATH` (`.design/telemetry/posterior.json`), REQUIRE explicit confirmation, back the file up to `posterior.json.bak`, then overwrite it with a fresh empty envelope so the next bandit pull rebootstraps from informed priors. See `./reference/bandit-integration.md` for setup, interpretation, and convergence guidance.
14
+
15
+ ## Invocation Contract
16
+
17
+ - **Input**: optional `--yes` to skip the interactive confirmation (for non-interactive/automated runs).
18
+ - **Output**: a Markdown reset receipt to stdout (backup path + arms cleared + envelope written).
19
+
20
+ ## Procedure
21
+
22
+ ### 1. Locate the posterior file
23
+
24
+ Read `.design/telemetry/posterior.json` (path declared by `scripts/lib/bandit-router.cjs`'s `DEFAULT_POSTERIOR_PATH` - never hardcode a different path). Missing → nothing to reset; emit and skip to Section 5 (Record):
25
+
26
+ ```
27
+ ## Bandit Posterior Reset
28
+
29
+ No posterior file found at `.design/telemetry/posterior.json` — nothing to reset.
30
+
31
+ The next bandit pull with `adaptive_mode: full` will bootstrap a fresh posterior from informed priors. See `reference/bandit-integration.md`.
32
+ ```
33
+
34
+ If present, count the arms (`arms.length`, treating a missing/non-array `arms` as `0`) so the confirmation and receipt can report what will be cleared. A corrupted/unparseable file is still resettable - report `arms: unknown (file unparseable)` and continue.
35
+
36
+ ### 2. Require explicit confirmation
37
+
38
+ This is a DESTRUCTIVE operation. Do NOT proceed without confirmation.
39
+
40
+ - If `--yes` was passed, skip straight to Section 3.
41
+ - Otherwise, prompt via AskUserQuestion: "Reset the bandit posterior at `.design/telemetry/posterior.json`? This clears <N> learned arms. A backup will be written to `posterior.json.bak` first." with options **Reset** and **Cancel**.
42
+ - On **Cancel** (or any non-affirmative answer), abort WITHOUT touching either the posterior or the backup, and emit:
43
+
44
+ ```
45
+ ## Bandit Posterior Reset — Cancelled
46
+
47
+ No changes made. The posterior at `.design/telemetry/posterior.json` is untouched (<N> arms).
48
+ ```
49
+
50
+ Then skip to Section 5 (Record) with `reset: false`.
51
+
52
+ ### 3. Back up the current posterior
53
+
54
+ Copy the live posterior to `.design/telemetry/posterior.json.bak` (sibling backup) BEFORE clearing it, so the previous state is always recoverable. Overwrite any existing `.bak` from a prior reset. If the backup write fails, ABORT before clearing; never clear without a successful backup.
55
+
56
+ ### 4. Clear to a fresh empty envelope
57
+
58
+ Overwrite `.design/telemetry/posterior.json` with a fresh empty envelope matching `scripts/lib/bandit-router.cjs`'s `loadPosterior()` shape (`SCHEMA_VERSION` = `1.0.0`, current ISO `generated_at`, empty `arms`):
59
+
60
+ ```json
61
+ {
62
+ "schema_version": "1.0.0",
63
+ "generated_at": "<ISO>",
64
+ "arms": []
65
+ }
66
+ ```
67
+
68
+ Write atomically where possible (`.tmp` + rename, mirroring `savePosterior()`). Then emit the receipt:
69
+
70
+ ```
71
+ ## Bandit Posterior Reset
72
+
73
+ Posterior cleared. The next bandit pull with `adaptive_mode: full` will rebootstrap from informed priors.
74
+
75
+ - Backup: `.design/telemetry/posterior.json.bak`
76
+ - Arms cleared: <N>
77
+ - Fresh envelope: `.design/telemetry/posterior.json` (schema_version 1.0.0, 0 arms)
78
+
79
+ Restore the previous state with: `cp .design/telemetry/posterior.json.bak .design/telemetry/posterior.json`
80
+ Verify the cleared state with `/gdd:bandit-status`. See `reference/bandit-integration.md`.
81
+ ```
82
+
83
+ ### 5. Record
84
+
85
+ Append one JSONL line to `.design/skill-records.jsonl`: `{"skill":"gdd-bandit-reset","ts":"<ISO>","reset":<bool>,"arms_cleared":<count>,"backup_written":<bool>}`. The skill mutates ONLY the posterior (+ its `.bak`) and appends to skill-records.jsonl (telemetry); it touches no other state.
86
+
87
+ ## Cross-references
88
+
89
+ - `/gdd:bandit-status` - read-only companion; inspect the posterior before/after a reset.
90
+ - `./reference/bandit-integration.md` - operator guide; interpretation patterns and when a reset is warranted.
91
+ - `scripts/lib/bandit-router.cjs` - posterior shape, `DEFAULT_POSTERIOR_PATH`, `SCHEMA_VERSION`, `loadPosterior()`, `savePosterior()`, `reset()`.