@hegemonart/get-design-done 1.30.6 → 1.31.5

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 (175) hide show
  1. package/.claude-plugin/marketplace.json +6 -3
  2. package/.claude-plugin/plugin.json +5 -2
  3. package/CHANGELOG.md +105 -0
  4. package/NOTICE +224 -0
  5. package/README.md +22 -1
  6. package/SKILL.md +1 -0
  7. package/agents/design-authority-watcher.md +1 -1
  8. package/agents/perf-analyzer.md +2 -2
  9. package/bin/gdd-mcp +78 -0
  10. package/bin/gdd-sdk +34 -24
  11. package/bin/gdd-state-mcp +78 -0
  12. package/{README.de.md → docs/i18n/README.de.md} +1 -1
  13. package/{README.fr.md → docs/i18n/README.fr.md} +1 -1
  14. package/{README.it.md → docs/i18n/README.it.md} +1 -1
  15. package/{README.ja.md → docs/i18n/README.ja.md} +1 -1
  16. package/{README.ko.md → docs/i18n/README.ko.md} +1 -1
  17. package/{README.zh-CN.md → docs/i18n/README.zh-CN.md} +1 -1
  18. package/hooks/_hook-emit.js +1 -1
  19. package/hooks/budget-enforcer.ts +5 -5
  20. package/hooks/context-exhaustion.ts +2 -2
  21. package/hooks/gdd-precompact-snapshot.js +3 -3
  22. package/hooks/gdd-read-injection-scanner.ts +2 -2
  23. package/hooks/gdd-sessionstart-recap.js +1 -1
  24. package/hooks/gdd-turn-closeout.js +1 -1
  25. package/package.json +24 -10
  26. package/recipes/.gitkeep +0 -0
  27. package/reference/schemas/recipe.schema.json +33 -0
  28. package/scripts/cli/gdd-events.mjs +5 -5
  29. package/scripts/lib/cache/gdd-cache-manager.cjs +1 -1
  30. package/scripts/lib/cli/index.ts +22 -160
  31. package/scripts/lib/connection-probe/index.cjs +1 -1
  32. package/scripts/lib/discuss-parallel-runner/aggregator.ts +1 -1
  33. package/scripts/lib/discuss-parallel-runner/index.ts +1 -1
  34. package/scripts/lib/error-classifier.cjs +24 -227
  35. package/scripts/lib/event-stream/index.ts +25 -193
  36. package/scripts/lib/figma-extract/digest.cjs +430 -0
  37. package/scripts/lib/figma-extract/parse-url.cjs +87 -0
  38. package/scripts/lib/figma-extract/payload-schema.json +108 -0
  39. package/scripts/lib/figma-extract/pull.cjs +394 -0
  40. package/scripts/lib/figma-extract/receiver.cjs +273 -0
  41. package/scripts/lib/figma-extract/render-md.cjs +143 -0
  42. package/scripts/lib/figma-extract/styles-resolver.cjs +147 -0
  43. package/scripts/lib/figma-extract/walk.cjs +100 -0
  44. package/scripts/lib/gdd-errors/index.ts +24 -213
  45. package/scripts/lib/gdd-state/index.ts +23 -161
  46. package/scripts/lib/health-mirror/index.cjs +88 -1
  47. package/scripts/lib/iteration-budget.cjs +23 -199
  48. package/scripts/lib/jittered-backoff.cjs +24 -107
  49. package/scripts/lib/lockfile.cjs +23 -195
  50. package/scripts/lib/logger/index.ts +1 -1
  51. package/scripts/lib/parallelism-engine/concurrency-tuner.cjs +1 -1
  52. package/scripts/lib/perf-analyzer/index.cjs +1 -1
  53. package/scripts/lib/pipeline-runner/index.ts +4 -4
  54. package/scripts/lib/pipeline-runner/state-machine.ts +1 -1
  55. package/scripts/lib/prompt-dedup/index.cjs +1 -1
  56. package/scripts/lib/rate-guard.cjs +2 -2
  57. package/scripts/lib/recipe-loader.cjs +142 -0
  58. package/scripts/lib/session-runner/errors.ts +3 -3
  59. package/scripts/lib/session-runner/index.ts +3 -3
  60. package/scripts/lib/session-runner/transcript.ts +1 -1
  61. package/scripts/lib/tool-scoping/index.ts +1 -1
  62. package/scripts/mcp-servers/gdd-mcp/server.ts +29 -311
  63. package/scripts/mcp-servers/gdd-state/server.ts +28 -282
  64. package/sdk/README.md +45 -0
  65. package/{scripts/lib → sdk}/cli/commands/audit.ts +3 -3
  66. package/{scripts/lib → sdk}/cli/commands/init.ts +3 -3
  67. package/{scripts/lib → sdk}/cli/commands/query.ts +4 -4
  68. package/{scripts/lib → sdk}/cli/commands/run.ts +5 -5
  69. package/{scripts/lib → sdk}/cli/commands/stage.ts +5 -5
  70. package/sdk/cli/index.js +8091 -0
  71. package/sdk/cli/index.ts +172 -0
  72. package/{scripts/lib → sdk}/cli/parse-args.ts +2 -2
  73. package/{scripts/lib/gdd-errors → sdk/errors}/classification.ts +1 -1
  74. package/sdk/errors/index.ts +218 -0
  75. package/{scripts/lib → sdk}/event-stream/emitter.ts +1 -1
  76. package/sdk/event-stream/index.ts +197 -0
  77. package/{scripts/lib → sdk}/event-stream/reader.ts +1 -1
  78. package/{scripts/lib → sdk}/event-stream/types.ts +2 -2
  79. package/{scripts/lib → sdk}/event-stream/writer.ts +1 -1
  80. package/sdk/index.ts +19 -0
  81. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/README.md +3 -3
  82. package/sdk/mcp/gdd-mcp/server.js +1924 -0
  83. package/sdk/mcp/gdd-mcp/server.ts +325 -0
  84. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_cycle_recap.ts +3 -3
  85. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_decisions_list.ts +2 -2
  86. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_events_tail.ts +3 -3
  87. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_health.ts +2 -2
  88. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_intel_get.ts +2 -2
  89. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_learnings_digest.ts +2 -2
  90. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phase_current.ts +2 -2
  91. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phases_list.ts +2 -2
  92. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_plans_list.ts +2 -2
  93. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_reflections_latest.ts +2 -2
  94. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_status.ts +3 -3
  95. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_telemetry_query.ts +3 -3
  96. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/index.ts +2 -2
  97. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/shared.ts +3 -3
  98. package/sdk/mcp/gdd-state/server.js +2790 -0
  99. package/sdk/mcp/gdd-state/server.ts +294 -0
  100. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_blocker.ts +3 -3
  101. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_decision.ts +3 -3
  102. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_must_have.ts +3 -3
  103. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/checkpoint.ts +2 -2
  104. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/frontmatter_update.ts +2 -2
  105. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/get.ts +3 -3
  106. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/index.ts +1 -1
  107. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/probe_connections.ts +3 -3
  108. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/resolve_blocker.ts +3 -3
  109. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/set_status.ts +2 -2
  110. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/shared.ts +8 -8
  111. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/transition_stage.ts +4 -4
  112. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/update_progress.ts +2 -2
  113. package/sdk/primitives/error-classifier.cjs +232 -0
  114. package/sdk/primitives/iteration-budget.cjs +205 -0
  115. package/sdk/primitives/jittered-backoff.cjs +112 -0
  116. package/sdk/primitives/lockfile.cjs +201 -0
  117. package/{scripts/lib/gdd-state → sdk/state}/gates.ts +1 -1
  118. package/sdk/state/index.ts +167 -0
  119. package/{scripts/lib/gdd-state → sdk/state}/lockfile.ts +1 -1
  120. package/{scripts/lib/gdd-state → sdk/state}/mutator.ts +1 -1
  121. package/{scripts/lib/gdd-state → sdk/state}/parser.ts +1 -1
  122. package/{scripts/lib/gdd-state → sdk/state}/types.ts +4 -4
  123. package/skills/figma-extract/SKILL.md +64 -0
  124. package/skills/health/SKILL.md +10 -0
  125. package/skills/quality-gate/SKILL.md +2 -2
  126. package/scripts/aggregate-agent-metrics.ts +0 -282
  127. package/scripts/bootstrap-manifest.txt +0 -3
  128. package/scripts/bootstrap.sh +0 -80
  129. package/scripts/build-distribution-bundles.cjs +0 -549
  130. package/scripts/build-intel.cjs +0 -486
  131. package/scripts/codegen-schema-types.ts +0 -149
  132. package/scripts/detect-stale-refs.cjs +0 -107
  133. package/scripts/e2e/run-headless.ts +0 -514
  134. package/scripts/extract-changelog-section.cjs +0 -58
  135. package/scripts/gsd-cleanup-incubator.cjs +0 -367
  136. package/scripts/injection-patterns.cjs +0 -58
  137. package/scripts/lint-agentskills-spec.cjs +0 -457
  138. package/scripts/release-smoke-test.cjs +0 -200
  139. package/scripts/rollback-release.sh +0 -42
  140. package/scripts/run-injection-scanner-ci.cjs +0 -83
  141. package/scripts/tests/test-authority-rejected-kinds.sh +0 -58
  142. package/scripts/tests/test-authority-watcher-diff.sh +0 -113
  143. package/scripts/tests/test-motion-provenance.sh +0 -64
  144. package/scripts/validate-frontmatter.ts +0 -409
  145. package/scripts/validate-incubator-scope.cjs +0 -133
  146. package/scripts/validate-schemas.ts +0 -401
  147. package/scripts/validate-skill-length.cjs +0 -283
  148. package/scripts/verify-version-sync.cjs +0 -30
  149. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_cycle_recap.schema.json +0 -0
  150. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_decisions_list.schema.json +0 -0
  151. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_events_tail.schema.json +0 -0
  152. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_health.schema.json +0 -0
  153. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_intel_get.schema.json +0 -0
  154. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_learnings_digest.schema.json +0 -0
  155. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phase_current.schema.json +0 -0
  156. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phases_list.schema.json +0 -0
  157. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_plans_list.schema.json +0 -0
  158. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_reflections_latest.schema.json +0 -0
  159. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_status.schema.json +0 -0
  160. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_telemetry_query.schema.json +0 -0
  161. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_blocker.schema.json +0 -0
  162. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_decision.schema.json +0 -0
  163. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_must_have.schema.json +0 -0
  164. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/checkpoint.schema.json +0 -0
  165. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/frontmatter_update.schema.json +0 -0
  166. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/get.schema.json +0 -0
  167. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/probe_connections.schema.json +0 -0
  168. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/resolve_blocker.schema.json +0 -0
  169. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/set_status.schema.json +0 -0
  170. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/transition_stage.schema.json +0 -0
  171. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/update_progress.schema.json +0 -0
  172. /package/{scripts/lib → sdk/primitives}/error-classifier.d.cts +0 -0
  173. /package/{scripts/lib → sdk/primitives}/iteration-budget.d.cts +0 -0
  174. /package/{scripts/lib → sdk/primitives}/jittered-backoff.d.cts +0 -0
  175. /package/{scripts/lib → sdk/primitives}/lockfile.d.cts +0 -0
@@ -1,205 +1,29 @@
1
- // scripts/lib/iteration-budget.cjs
2
- //
3
- // Plan 20-14 — bounded fix-loop iteration budget.
1
+ 'use strict';
2
+ // scripts/lib/iteration-budget.cjs — GDD-DEPRECATION-SHIM (Plan 31-5-06, SDK-05, D-02).
4
3
  //
5
- // Stops infinite fix cycles from burning unbounded context. Every fix-
6
- // iteration consumes 1 unit; Layer-B cache hits refund 1 unit so
7
- // cached answers don't count against the ceiling. When the budget
8
- // reaches 0, consume() throws and the caller must surface to user.
4
+ // Thin deprecation shim. The real implementation moved to
5
+ // sdk/primitives/iteration-budget.cjs in Plan 31-5-04 (SDK consolidation).
6
+ // This file is re-created at the OLD path so undocumented EXTERNAL importers
7
+ // (anyone who reached into node_modules/@hegemonart/get-design-done/scripts/
8
+ // lib/iteration-budget.cjs directly) keep working for one minor grace window.
9
9
  //
10
- // State file: `.design/iteration-budget.json`. All mutations go through
11
- // `scripts/lib/lockfile.cjs` with atomic temp+rename writes so
12
- // concurrent callers (hook + fix-loop + verify) don't clobber each
13
- // other. The lock scope is the state file itself, so refund + consume
14
- // from different children serialize correctly.
10
+ // REMOVED IN v1.33.0 (D-02). Grace window: 1.31.5 ships with shims →
11
+ // 1.32.0 still has them 1.33.0 removes them. Internal callers already use
12
+ // the sdk/ path (Plan 31-5-04/05) this shim is external-only and 31-5-10's
13
+ // no-stale-internal-refs guard excludes files carrying the GDD-DEPRECATION-SHIM
14
+ // marker above.
15
15
  //
16
- // Shape on disk matches reference/schemas/iteration-budget.schema.json:
17
- // { budget, remaining, consumed, refunded, updatedAt }
18
-
19
- 'use strict';
20
-
21
- const fs = require('node:fs');
22
- const path = require('node:path');
23
-
24
- const { acquire, renameWithRetry } = require('./lockfile.cjs');
25
-
26
- const STATE_PATH_REL = path.join('.design', 'iteration-budget.json');
27
- const DEFAULT_BUDGET = 50;
28
- const LOCK_MAX_WAIT_MS = 5_000;
29
-
30
- /** Error thrown by `consume()` when the remaining budget would go below 0. */
31
- class IterationBudgetExhaustedError extends Error {
32
- constructor(amount, state) {
33
- super(
34
- `IterationBudgetExhausted: cannot consume ${amount} — remaining=${state.remaining}, budget=${state.budget}, consumed=${state.consumed}. Caller must surface to user (fix-loop has stopped converging).`,
35
- );
36
- this.name = 'IterationBudgetExhaustedError';
37
- this.amount = amount;
38
- this.state = state;
39
- }
40
- }
41
-
42
- function stateAbsPath() {
43
- return path.join(process.cwd(), STATE_PATH_REL);
44
- }
45
-
46
- /** Read and validate the state file. Returns null on missing/corrupt. */
47
- function readStateSync() {
48
- const p = stateAbsPath();
49
- if (!fs.existsSync(p)) return null;
50
- try {
51
- const raw = fs.readFileSync(p, 'utf8');
52
- const parsed = JSON.parse(raw);
53
- if (
54
- parsed &&
55
- typeof parsed === 'object' &&
56
- Number.isInteger(parsed.budget) && parsed.budget >= 0 &&
57
- Number.isInteger(parsed.remaining) && parsed.remaining >= 0 &&
58
- Number.isInteger(parsed.consumed) && parsed.consumed >= 0 &&
59
- Number.isInteger(parsed.refunded) && parsed.refunded >= 0 &&
60
- typeof parsed.updatedAt === 'string'
61
- ) {
62
- return parsed;
63
- }
64
- return null;
65
- } catch {
66
- return null;
67
- }
68
- }
69
-
70
- /** Atomically write state under a file-lock. */
71
- async function writeStateAtomic(state) {
72
- const p = stateAbsPath();
73
- const dir = path.dirname(p);
74
- fs.mkdirSync(dir, { recursive: true });
75
- const release = await acquire(p, { maxWaitMs: LOCK_MAX_WAIT_MS });
76
- try {
77
- // Re-read inside the lock so we merge against the very latest
78
- // on-disk state, not the value the caller observed pre-lock. This
79
- // is what makes concurrent consume() from 10 children add up to
80
- // consumed=10 rather than racing each other.
81
- const latest = readStateSync();
82
- const merged = state.mergeFn ? state.mergeFn(latest || state.seed) : state.seed;
83
- const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
84
- fs.writeFileSync(tmp, JSON.stringify(merged, null, 2) + '\n', 'utf8');
85
- await renameWithRetry(tmp, p);
86
- return merged;
87
- } finally {
88
- await release();
89
- }
90
- }
91
-
92
- /**
93
- * Initialize or restart the iteration budget.
94
- *
95
- * @param {number} [budget] default 50
96
- * @returns {Promise<{budget, remaining, consumed, refunded, updatedAt}>}
97
- */
98
- async function reset(budget = DEFAULT_BUDGET) {
99
- if (!Number.isFinite(budget) || budget < 0) {
100
- throw new Error(`iteration-budget.reset: budget must be a non-negative finite number, got ${budget}`);
101
- }
102
- const b = Math.floor(budget);
103
- const state = {
104
- budget: b,
105
- remaining: b,
106
- consumed: 0,
107
- refunded: 0,
108
- updatedAt: new Date().toISOString(),
109
- };
110
- return writeStateAtomic({ seed: state, mergeFn: () => state });
111
- }
112
-
113
- /**
114
- * Consume N units from the remaining budget. Throws
115
- * IterationBudgetExhaustedError when N would send remaining below zero.
116
- *
117
- * @param {number} [amount] default 1
118
- * @returns {Promise<{budget, remaining, consumed, refunded, updatedAt}>}
119
- * the new on-disk state after consumption.
120
- */
121
- async function consume(amount = 1) {
122
- const n = normalizeAmount(amount);
123
- // Seed for the case when no state exists yet: auto-init to default budget.
124
- const seed = defaultState();
125
- return writeStateAtomic({
126
- seed,
127
- mergeFn: (current) => {
128
- const base = current || seed;
129
- const nextRemaining = base.remaining - n;
130
- if (nextRemaining < 0) {
131
- // Throw without writing — atomic: either consume fully or not at all.
132
- throw new IterationBudgetExhaustedError(n, base);
133
- }
134
- return {
135
- budget: base.budget,
136
- remaining: nextRemaining,
137
- consumed: base.consumed + n,
138
- refunded: base.refunded,
139
- updatedAt: new Date().toISOString(),
140
- };
141
- },
142
- });
143
- }
144
-
145
- /**
146
- * Refund N units to the remaining budget, capped at `budget`.
147
- *
148
- * @param {number} [amount] default 1
149
- * @returns {Promise<{budget, remaining, consumed, refunded, updatedAt}>}
150
- */
151
- async function refund(amount = 1) {
152
- const n = normalizeAmount(amount);
153
- const seed = defaultState();
154
- return writeStateAtomic({
155
- seed,
156
- mergeFn: (current) => {
157
- const base = current || seed;
158
- const nextRemaining = Math.min(base.budget, base.remaining + n);
159
- // Only count the portion that actually landed — if we were already
160
- // at budget, the refund is a no-op.
161
- const actuallyRefunded = nextRemaining - base.remaining;
162
- return {
163
- budget: base.budget,
164
- remaining: nextRemaining,
165
- consumed: base.consumed,
166
- refunded: base.refunded + actuallyRefunded,
167
- updatedAt: new Date().toISOString(),
168
- };
169
- },
170
- });
171
- }
172
-
173
- /**
174
- * Return the current on-disk state. Reads without the lock — callers
175
- * using this for UI display only see a best-effort snapshot; mutating
176
- * paths (consume/refund) always re-read inside the lock.
177
- */
178
- function remaining() {
179
- return readStateSync() || defaultState();
180
- }
181
-
182
- function normalizeAmount(amount) {
183
- if (!Number.isFinite(amount) || amount <= 0) {
184
- throw new Error(`iteration-budget: amount must be a positive finite number, got ${amount}`);
185
- }
186
- return Math.floor(amount);
187
- }
16
+ // Emits a DeprecationWarning exactly ONCE per process: the module-level
17
+ // `warned` flag plus Node's module cache (this file is evaluated once per
18
+ // process regardless of how many times it is required).
188
19
 
189
- function defaultState() {
190
- return {
191
- budget: DEFAULT_BUDGET,
192
- remaining: DEFAULT_BUDGET,
193
- consumed: 0,
194
- refunded: 0,
195
- updatedAt: new Date().toISOString(),
196
- };
20
+ let warned = false;
21
+ if (!warned) {
22
+ warned = true;
23
+ process.emitWarning(
24
+ 'scripts/lib/iteration-budget.cjs is deprecated; import sdk/primitives/iteration-budget instead. Removed in v1.33.0.',
25
+ 'DeprecationWarning',
26
+ );
197
27
  }
198
28
 
199
- module.exports = {
200
- consume,
201
- refund,
202
- remaining,
203
- reset,
204
- IterationBudgetExhaustedError,
205
- };
29
+ module.exports = require('../../sdk/primitives/iteration-budget.cjs');
@@ -1,112 +1,29 @@
1
- // scripts/lib/jittered-backoff.cjs
2
- //
3
- // Plan 20-14 — jittered exponential backoff primitive.
4
- //
5
- // Replaces fixed-interval retry sleeps across the codebase (update-check,
6
- // watch-authorities, figma probes, connection probes, hook retry loops) with
7
- // a deterministic, capped, jittered backoff curve.
8
- //
9
- // Formula:
10
- // base = min(maxMs, baseMs * factor^attempt)
11
- // delay = base * (1 + rand(-jitter, +jitter))
12
- // clamp to [0, maxMs * (1 + jitter)]
13
- //
14
- // The zero-based `attempt` counter gives `baseMs` on the first call, then
15
- // multiplies by `factor` per attempt up to the cap. Jitter is full-width
16
- // symmetric (not AWS-style equal/decorrelated) because our consumers are
17
- // single-threaded retry loops — there's no thundering-herd problem to
18
- // smooth; the jitter is cosmetic protection against synchronized retries
19
- // between siblings like the watcher + update-check running at the same
20
- // session boundary.
1
+ 'use strict';
2
+ // scripts/lib/jittered-backoff.cjs — GDD-DEPRECATION-SHIM (Plan 31-5-06, SDK-05, D-02).
21
3
  //
22
- // Defaults (baseMs=100, maxMs=30_000, factor=2, jitter=0.2) yield:
23
- // attempt 0 80-120ms
24
- // attempt 1 160-240ms
25
- // attempt 2 320-480ms
26
- // ...
27
- // attempt 9 → ~24s-30s (capped)
4
+ // Thin deprecation shim. The real implementation moved to
5
+ // sdk/primitives/jittered-backoff.cjs in Plan 31-5-04 (SDK consolidation).
6
+ // This file is re-created at the OLD path so undocumented EXTERNAL importers
7
+ // (anyone who reached into node_modules/@hegemonart/get-design-done/scripts/
8
+ // lib/jittered-backoff.cjs directly) keep working for one minor grace window.
28
9
  //
29
- // This module is `.cjs` (not `.ts`) per Plan 20-14 D-01 so it can be
30
- // `require()`d from both the `.ts` runtime (hooks, MCP server) and future
31
- // `.cjs` CLI invocations without needing `--experimental-strip-types` at
32
- // every consumer site. Types live in the paired `jittered-backoff.d.cts`.
10
+ // REMOVED IN v1.33.0 (D-02). Grace window: 1.31.5 ships with shims
11
+ // 1.32.0 still has them 1.33.0 removes them. Internal callers already use
12
+ // the sdk/ path (Plan 31-5-04/05) — this shim is external-only and 31-5-10's
13
+ // no-stale-internal-refs guard excludes files carrying the GDD-DEPRECATION-SHIM
14
+ // marker above.
33
15
  //
34
- // No external dependencies; pure Math.random + setTimeout.
35
-
36
- 'use strict';
37
-
38
- /**
39
- * Default backoff parameters — chosen to cover retry-after-Xms through
40
- * retry-after-30s with reasonable mid-range distribution.
41
- */
42
- const DEFAULTS = Object.freeze({
43
- baseMs: 100,
44
- maxMs: 30_000,
45
- factor: 2,
46
- jitter: 0.2,
47
- });
48
-
49
- /**
50
- * Compute the jittered delay in milliseconds for a given attempt number.
51
- *
52
- * @param {number} attempt zero-based attempt counter (0 = first retry)
53
- * @param {object} [opts]
54
- * @param {number} [opts.baseMs] initial delay before any jitter. Default 100.
55
- * @param {number} [opts.maxMs] maximum un-jittered base. Default 30_000.
56
- * @param {number} [opts.factor] per-attempt multiplier. Default 2.
57
- * @param {number} [opts.jitter] symmetric jitter fraction in [0, 1). Default 0.2.
58
- * @returns {number} delay in ms. Never negative. May exceed `maxMs` by up
59
- * to `jitter * maxMs` on the high side.
60
- *
61
- * Invariants:
62
- * - `delayMs(n, opts) >= 0` for every non-negative `n`.
63
- * - `delayMs(n, opts) <= maxMs * (1 + jitter)` for every non-negative `n`.
64
- * - The distribution has nonzero stddev whenever `jitter > 0`.
65
- */
66
- function delayMs(attempt, opts) {
67
- const baseMs = (opts && Number.isFinite(opts.baseMs)) ? opts.baseMs : DEFAULTS.baseMs;
68
- const maxMs = (opts && Number.isFinite(opts.maxMs)) ? opts.maxMs : DEFAULTS.maxMs;
69
- const factor = (opts && Number.isFinite(opts.factor)) ? opts.factor : DEFAULTS.factor;
70
- const jitter = (opts && Number.isFinite(opts.jitter)) ? opts.jitter : DEFAULTS.jitter;
71
-
72
- // Guard against nonsense inputs — callers that pass garbage shouldn't
73
- // cause NaN or Infinity to propagate into setTimeout.
74
- const a = Math.max(0, Number.isFinite(attempt) ? Math.floor(attempt) : 0);
75
- const safeBase = Math.max(0, baseMs);
76
- const safeMax = Math.max(safeBase, maxMs);
77
- const safeFactor = factor > 0 ? factor : DEFAULTS.factor;
78
- // Clamp jitter to [0, 1) — full-range (>=1) would allow negative values
79
- // after subtraction, which we want to forbid by invariant.
80
- const safeJitter = Math.min(0.999, Math.max(0, jitter));
81
-
82
- // Exponential growth capped at safeMax.
83
- // Math.pow with a huge attempt count can overflow to Infinity; the
84
- // Math.min picks up safeMax before Infinity escapes.
85
- const unjittered = Math.min(safeMax, safeBase * Math.pow(safeFactor, a));
86
-
87
- // Symmetric jitter in [-jitter, +jitter).
88
- // Math.random() returns [0, 1); 2r - 1 maps to [-1, 1); scale by jitter.
89
- const noise = (Math.random() * 2 - 1) * safeJitter;
90
- const delay = unjittered * (1 + noise);
91
-
92
- // Floor at zero (noise could in theory go slightly negative due to FP
93
- // precision even with safeJitter < 1, for very small unjittered values).
94
- return Math.max(0, delay);
95
- }
96
-
97
- /**
98
- * Sleep for a jittered backoff interval. Convenience wrapper around
99
- * `delayMs` + `setTimeout`.
100
- *
101
- * @param {number} attempt zero-based attempt counter
102
- * @param {object} [opts] see {@link delayMs}
103
- * @returns {Promise<number>} resolves to the actual delay that was applied
104
- */
105
- function sleep(attempt, opts) {
106
- const ms = delayMs(attempt, opts);
107
- return new Promise((resolve) => {
108
- setTimeout(() => resolve(ms), ms);
109
- });
16
+ // Emits a DeprecationWarning exactly ONCE per process: the module-level
17
+ // `warned` flag plus Node's module cache (this file is evaluated once per
18
+ // process regardless of how many times it is required).
19
+
20
+ let warned = false;
21
+ if (!warned) {
22
+ warned = true;
23
+ process.emitWarning(
24
+ 'scripts/lib/jittered-backoff.cjs is deprecated; import sdk/primitives/jittered-backoff instead. Removed in v1.33.0.',
25
+ 'DeprecationWarning',
26
+ );
110
27
  }
111
28
 
112
- module.exports = { delayMs, sleep, DEFAULTS };
29
+ module.exports = require('../../sdk/primitives/jittered-backoff.cjs');
@@ -1,201 +1,29 @@
1
- // scripts/lib/lockfile.cjs
2
- //
3
- // Plan 20-14 — PID+timestamp sibling lockfile for `.cjs` consumers.
1
+ 'use strict';
2
+ // scripts/lib/lockfile.cjs — GDD-DEPRECATION-SHIM (Plan 31-5-06, SDK-05, D-02).
4
3
  //
5
- // Algorithm mirrors scripts/lib/gdd-state/lockfile.ts (Plan 20-01):
6
- // Lock path: `${target}.lock`
7
- // Payload: { pid: number, host: string, acquired_at: ISO8601 }
8
- // Acquire: atomic `writeFileSync(..., { flag: 'wx' })`
9
- // Stale rule: pid dead (ESRCH via `kill(pid, 0)`) OR `acquired_at` older
10
- // than `staleMs` OR unparseable payload
11
- // Release: unlink; ENOENT is not an error; idempotent
4
+ // Thin deprecation shim. The real implementation moved to
5
+ // sdk/primitives/lockfile.cjs in Plan 31-5-04 (SDK consolidation).
6
+ // This file is re-created at the OLD path so undocumented EXTERNAL importers
7
+ // (anyone who reached into node_modules/@hegemonart/get-design-done/scripts/
8
+ // lib/lockfile.cjs directly) keep working for one minor grace window.
12
9
  //
13
- // Windows: AV scanners and file-indexers can hold a file briefly after
14
- // close. `wx` create may fail with EPERM/EBUSY even when the target is
15
- // free; we treat these as transient and loop (same code path as EEXIST).
10
+ // REMOVED IN v1.33.0 (D-02). Grace window: 1.31.5 ships with shims →
11
+ // 1.32.0 still has them 1.33.0 removes them. Internal callers already use
12
+ // the sdk/ path (Plan 31-5-04/05) this shim is external-only and 31-5-10's
13
+ // no-stale-internal-refs guard excludes files carrying the GDD-DEPRECATION-SHIM
14
+ // marker above.
16
15
  //
17
- // Dependency-cycle note: Plan 20-14's rate-guard + iteration-budget
18
- // consume this module, and both are required to stay dependency-light so
19
- // that hooks/budget-enforcer.ts can import them without dragging the
20
- // gdd-state MCP graph along. Hence this standalone .cjs port instead of
21
- // calling the .ts version.
22
-
23
- 'use strict';
24
-
25
- const fs = require('node:fs');
26
- const os = require('node:os');
27
-
28
- const DEFAULT_STALE_MS = 60_000;
29
- const DEFAULT_MAX_WAIT_MS = 5_000;
30
- const DEFAULT_POLL_MS = 50;
31
-
32
- /**
33
- * Acquire an advisory lock at `${path}.lock`. Returns an idempotent
34
- * async release function.
35
- *
36
- * @param {string} path path being locked (we append `.lock`)
37
- * @param {object} [opts]
38
- * @param {number} [opts.staleMs] ms after which an existing lock is stale. Default 60_000.
39
- * @param {number} [opts.maxWaitMs] total ms to wait before throwing. Default 5_000.
40
- * @param {number} [opts.pollMs] ms between retry attempts. Default 50.
41
- * @returns {Promise<() => Promise<void>>} release function
42
- * @throws {Error} with name === 'LockAcquisitionError' when maxWaitMs elapses
43
- */
44
- async function acquire(path, opts) {
45
- const o = opts || {};
46
- const staleMs = Number.isFinite(o.staleMs) ? o.staleMs : DEFAULT_STALE_MS;
47
- const maxWaitMs = Number.isFinite(o.maxWaitMs) ? o.maxWaitMs : DEFAULT_MAX_WAIT_MS;
48
- const pollMs = Number.isFinite(o.pollMs) ? o.pollMs : DEFAULT_POLL_MS;
49
-
50
- if (staleMs < 0 || maxWaitMs < 0 || pollMs < 0) {
51
- throw new Error(
52
- `lockfile.acquire: invalid options (staleMs=${staleMs}, maxWaitMs=${maxWaitMs}, pollMs=${pollMs})`,
53
- );
54
- }
55
-
56
- const lockPath = `${path}.lock`;
57
- const payload = JSON.stringify({
58
- pid: process.pid,
59
- host: os.hostname(),
60
- acquired_at: new Date().toISOString(),
61
- });
62
- const startedAt = Date.now();
63
-
64
- while (true) {
65
- try {
66
- fs.writeFileSync(lockPath, payload, { flag: 'wx', encoding: 'utf8' });
67
- return makeRelease(lockPath);
68
- } catch (err) {
69
- const code = err && typeof err === 'object' ? err.code : undefined;
70
- if (code !== 'EEXIST' && code !== 'EPERM' && code !== 'EBUSY') {
71
- throw err;
72
- }
73
- // Try to read the current holder; if it vanished between EEXIST and
74
- // read, loop immediately.
75
- const existing = readLockSafe(lockPath);
76
- if (existing === null) continue;
77
-
78
- const parsed = parseLock(existing);
79
- // Only clear when we're confident the lock is stale: the payload
80
- // parses AND the PID/age check says so. An unparseable payload is
81
- // treated as fresh — on Windows, AV/indexer can transiently deny
82
- // reads (EACCES/EPERM/EBUSY), and clearing under that condition
83
- // would let two writers race and lose increments.
84
- if (parsed !== null && isStale(parsed, staleMs)) {
85
- // Clear stale lock; race-tolerant — if it's already gone we get
86
- // ENOENT, no-op.
87
- try { fs.unlinkSync(lockPath); } catch { /* ignore */ }
88
- continue;
89
- }
90
-
91
- if (Date.now() - startedAt >= maxWaitMs) {
92
- const e = new Error(
93
- `lockfile: failed to acquire ${lockPath} within ${maxWaitMs}ms (held by ${existing})`,
94
- );
95
- e.name = 'LockAcquisitionError';
96
- e.lockPath = lockPath;
97
- e.holder = existing;
98
- e.waitedMs = Date.now() - startedAt;
99
- throw e;
100
- }
101
- await sleep(pollMs);
102
- }
103
- }
104
- }
105
-
106
- function makeRelease(lockPath) {
107
- let released = false;
108
- return async function release() {
109
- if (released) return;
110
- released = true;
111
- try {
112
- fs.unlinkSync(lockPath);
113
- } catch (err) {
114
- const code = err && typeof err === 'object' ? err.code : undefined;
115
- if (code === 'ENOENT') return; // idempotent — already gone
116
- if (code === 'EPERM' || code === 'EBUSY') {
117
- // Windows AV/indexer: retry once.
118
- await sleep(50);
119
- try {
120
- if (fs.existsSync(lockPath)) fs.unlinkSync(lockPath);
121
- } catch { /* give up; stale-detection will reclaim */ }
122
- return;
123
- }
124
- // Any other errno: swallow. Best-effort cleanup; stale-age check
125
- // will eventually reclaim the lock.
126
- }
127
- };
128
- }
129
-
130
- function readLockSafe(p) {
131
- try {
132
- return fs.readFileSync(p, 'utf8');
133
- } catch (err) {
134
- const code = err && typeof err === 'object' ? err.code : undefined;
135
- if (code === 'ENOENT') return null;
136
- return '<unreadable>';
137
- }
138
- }
139
-
140
- function parseLock(raw) {
141
- try {
142
- const obj = JSON.parse(raw);
143
- if (
144
- obj && typeof obj === 'object' &&
145
- typeof obj.pid === 'number' &&
146
- typeof obj.host === 'string' &&
147
- typeof obj.acquired_at === 'string'
148
- ) {
149
- return obj;
150
- }
151
- return null;
152
- } catch {
153
- return null;
154
- }
155
- }
156
-
157
- function isStale(payload, staleMs) {
158
- if (!isPidAlive(payload.pid, payload.host)) return true;
159
- const t = Date.parse(payload.acquired_at);
160
- if (!Number.isFinite(t)) return true;
161
- return Date.now() - t > staleMs;
162
- }
163
-
164
- function isPidAlive(pid, host) {
165
- if (host !== os.hostname()) return true; // can't introspect other hosts
166
- if (pid === process.pid) return true;
167
- try {
168
- process.kill(pid, 0); // signal 0 = validate, don't deliver
169
- return true;
170
- } catch (err) {
171
- const code = err && typeof err === 'object' ? err.code : undefined;
172
- if (code === 'ESRCH') return false;
173
- // EPERM / EACCES: process exists but is unsignalable; treat as alive.
174
- return true;
175
- }
176
- }
177
-
178
- function sleep(ms) {
179
- return new Promise((resolve) => setTimeout(resolve, ms));
180
- }
16
+ // Emits a DeprecationWarning exactly ONCE per process: the module-level
17
+ // `warned` flag plus Node's module cache (this file is evaluated once per
18
+ // process regardless of how many times it is required).
181
19
 
182
- /**
183
- * `fs.renameSync` wrapper that retries once on Windows EPERM/EBUSY/EACCES.
184
- * AV scanners and the file-indexer can briefly hold a destination open
185
- * after another process closed it, causing rename to fail even when the
186
- * advisory lock is correctly held.
187
- *
188
- * Mirrors the inline retry in scripts/lib/gdd-state/index.ts mutate().
189
- */
190
- async function renameWithRetry(from, to) {
191
- try {
192
- fs.renameSync(from, to);
193
- } catch (err) {
194
- const code = err && typeof err === 'object' ? err.code : undefined;
195
- if (code !== 'EPERM' && code !== 'EBUSY' && code !== 'EACCES') throw err;
196
- await sleep(50);
197
- fs.renameSync(from, to);
198
- }
20
+ let warned = false;
21
+ if (!warned) {
22
+ warned = true;
23
+ process.emitWarning(
24
+ 'scripts/lib/lockfile.cjs is deprecated; import sdk/primitives/lockfile instead. Removed in v1.33.0.',
25
+ 'DeprecationWarning',
26
+ );
199
27
  }
200
28
 
201
- module.exports = { acquire, renameWithRetry };
29
+ module.exports = require('../../sdk/primitives/lockfile.cjs');
@@ -29,7 +29,7 @@
29
29
  // process.stderr); the logger contract never propagates event-stream
30
30
  // failures back to the caller.
31
31
 
32
- import { appendEvent } from '../event-stream/index.ts';
32
+ import { appendEvent } from '../../../sdk/event-stream/index.ts';
33
33
  import { ConsoleSink, JsonlSink, MultiSink } from './sinks.ts';
34
34
  import {
35
35
  LEVEL_ORDER,
@@ -59,7 +59,7 @@ function getAppendEvent() {
59
59
  // with --experimental-strip-types (or Node 24 built-in TS) can
60
60
  // require it. If require fails (e.g., older runtime, missing
61
61
  // module), fall through to the no-op.
62
- const m = require('../event-stream');
62
+ const m = require('../../../sdk/event-stream');
63
63
  if (m && typeof m.appendEvent === 'function') return m.appendEvent;
64
64
  } catch {
65
65
  // Swallow — best-effort telemetry. Losing one verdict is
@@ -6,7 +6,7 @@
6
6
  * `.design/telemetry/trajectories/<cycle>.jsonl` files (agent trace
7
7
  * lines per Phase 22).
8
8
  *
9
- * JSONL discipline (same as scripts/lib/event-stream/reader.ts):
9
+ * JSONL discipline (same as sdk/event-stream/reader.ts):
10
10
  * - One JSON object per line.
11
11
  * - Blank lines / whitespace-only lines ignored silently.
12
12
  * - Malformed lines tolerated — counted in skipped_count, NOT thrown.
@@ -23,11 +23,11 @@
23
23
  //
24
24
  // NEVER throws — every failure becomes a `PipelineResult`.
25
25
 
26
- import { appendEvent } from '../event-stream/index.ts';
27
- import type { BaseEvent } from '../event-stream/index.ts';
26
+ import { appendEvent } from '../../../sdk/event-stream/index.ts';
27
+ import type { BaseEvent } from '../../../sdk/event-stream/index.ts';
28
28
  import { getLogger } from '../logger/index.ts';
29
- import { transition as defaultTransition, TransitionGateFailed } from '../gdd-state/index.ts';
30
- import { ValidationError } from '../gdd-errors/index.ts';
29
+ import { transition as defaultTransition, TransitionGateFailed } from '../../../sdk/state/index.ts';
30
+ import { ValidationError } from '../../../sdk/errors/index.ts';
31
31
 
32
32
  import {
33
33
  STAGE_ORDER,
@@ -15,7 +15,7 @@
15
15
  // name is in the set. Unknown stage names in `skipStages` are
16
16
  // tolerated (no-op) — the filter is a membership check.
17
17
 
18
- import { ValidationError } from '../gdd-errors/index.ts';
18
+ import { ValidationError } from '../../../sdk/errors/index.ts';
19
19
  import type { Stage } from './types.ts';
20
20
 
21
21
  /**
@@ -27,7 +27,7 @@ const DEFAULT_THRESHOLD = 3; // D-11 — '>= 3 agents'
27
27
  */
28
28
  function getAppendEvent() {
29
29
  try {
30
- const m = require('../event-stream');
30
+ const m = require('../../../sdk/event-stream');
31
31
  if (m && typeof m.appendEvent === 'function') return m.appendEvent;
32
32
  } catch { /* swallow — event-stream not on path */ }
33
33
  return function noopAppend(_ev) {};