@opengsd/gsd-core 1.20.1 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +33 -2
  4. package/agents/gsd-executor.md +1 -1
  5. package/agents/gsd-plan-checker.md +1 -1
  6. package/agents/gsd-planner.md +3 -3
  7. package/agents/gsd-verifier.md +2 -2
  8. package/bin/install.js +234 -126
  9. package/commands/gsd/autonomous.md +1 -1
  10. package/gsd-core/bin/gsd-tools.cjs +450 -753
  11. package/gsd-core/bin/lib/capability-activation.cjs +8 -2
  12. package/gsd-core/bin/lib/capability-command-router.cjs +6 -2
  13. package/gsd-core/bin/lib/capability-ledger.cjs +1 -1
  14. package/gsd-core/bin/lib/capability-lock.cjs +19 -27
  15. package/gsd-core/bin/lib/capability-registry.cjs +74 -71
  16. package/gsd-core/bin/lib/capability-validator.cjs +1 -0
  17. package/gsd-core/bin/lib/capability-writer.cjs +7 -4
  18. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +142 -62
  19. package/gsd-core/bin/lib/claude-orchestration.cjs +214 -122
  20. package/gsd-core/bin/lib/cli-surface.cjs +545 -0
  21. package/gsd-core/bin/lib/command-arg-projection.cjs +356 -103
  22. package/gsd-core/bin/lib/commands.cjs +80 -2
  23. package/gsd-core/bin/lib/config-file-read.cjs +85 -0
  24. package/gsd-core/bin/lib/config-loader.cjs +23 -15
  25. package/gsd-core/bin/lib/config.cjs +139 -101
  26. package/gsd-core/bin/lib/configuration.cjs +41 -2
  27. package/gsd-core/bin/lib/criterion-baseline.cjs +346 -0
  28. package/gsd-core/bin/lib/criterion-replay.cjs +380 -0
  29. package/gsd-core/bin/lib/estimate-cli.cjs +5 -1
  30. package/gsd-core/bin/lib/gap-checker.cjs +12 -1
  31. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +593 -27
  32. package/gsd-core/bin/lib/gate-phase-context.cjs +16 -4
  33. package/gsd-core/bin/lib/graphify.cjs +19 -2
  34. package/gsd-core/bin/lib/health-diagnostic.cjs +5 -1
  35. package/gsd-core/bin/lib/init.cjs +13 -11
  36. package/gsd-core/bin/lib/install-effort-resolver.cjs +24 -4
  37. package/gsd-core/bin/lib/install-engine.cjs +11 -19
  38. package/gsd-core/bin/lib/install-fs-adapter.cjs +5 -1
  39. package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -3
  40. package/gsd-core/bin/lib/io.cjs +6 -0
  41. package/gsd-core/bin/lib/markdown-sectionizer.cjs +62 -31
  42. package/gsd-core/bin/lib/markdown-table.cjs +39 -1
  43. package/gsd-core/bin/lib/milestone.cjs +8 -6
  44. package/gsd-core/bin/lib/model-resolver.cjs +8 -4
  45. package/gsd-core/bin/lib/phase-entry-grammar.cjs +14 -0
  46. package/gsd-core/bin/lib/phase-locator.cjs +8 -3
  47. package/gsd-core/bin/lib/phase.cjs +107 -31
  48. package/gsd-core/bin/lib/plan-dependency-graph.cjs +46 -23
  49. package/gsd-core/bin/lib/planning-snapshot.cjs +8 -4
  50. package/gsd-core/bin/lib/planning-workspace.cjs +20 -10
  51. package/gsd-core/bin/lib/profile-output.cjs +9 -6
  52. package/gsd-core/bin/lib/project-context.cjs +439 -0
  53. package/gsd-core/bin/lib/project-root.cjs +9 -230
  54. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +1 -1
  55. package/gsd-core/bin/lib/report-parser.cjs +16 -0
  56. package/gsd-core/bin/lib/roadmap-parser.cjs +328 -50
  57. package/gsd-core/bin/lib/roadmap-upgrade.cjs +24 -5
  58. package/gsd-core/bin/lib/roadmap.cjs +2 -2
  59. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +7 -4
  60. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +81 -3
  61. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +279 -8
  62. package/gsd-core/bin/lib/runtime-slash.cjs +7 -2
  63. package/gsd-core/bin/lib/shell-command-projection.cjs +1010 -10
  64. package/gsd-core/bin/lib/stale-bake-guard.cjs +4 -1
  65. package/gsd-core/bin/lib/state-transition.cjs +91 -41
  66. package/gsd-core/bin/lib/state.cjs +446 -61
  67. package/gsd-core/bin/lib/surface.cjs +4 -7
  68. package/gsd-core/bin/lib/tdd-red-evidence.cjs +10 -3
  69. package/gsd-core/bin/lib/verify-command-router.cjs +6 -1
  70. package/gsd-core/bin/lib/verify.cjs +49 -6
  71. package/gsd-core/bin/lib/worktree-safety.cjs +8 -111
  72. package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
  73. package/gsd-core/references/autonomous-smart-discuss.md +99 -18
  74. package/gsd-core/references/checkpoints.md +1 -1
  75. package/gsd-core/references/planner-guidance.md +11 -0
  76. package/gsd-core/references/planner-human-verify-mode.md +1 -1
  77. package/gsd-core/references/planner-reversibility.md +2 -0
  78. package/gsd-core/references/planner-verify-command-grounding.md +22 -6
  79. package/gsd-core/workflows/autonomous.md +4 -4
  80. package/gsd-core/workflows/code-review-fix.md +5 -7
  81. package/gsd-core/workflows/execute-phase.md +21 -7
  82. package/gsd-core/workflows/plan-phase/steps/plan-pre-dispatch.md +101 -0
  83. package/gsd-core/workflows/plan-phase.md +18 -19
  84. package/hooks/dist/gsd-agent-isolation-guard.js +3 -2
  85. package/hooks/dist/gsd-config-reload.js +2 -2
  86. package/hooks/dist/gsd-context-monitor.js +2 -1
  87. package/hooks/dist/gsd-cursor-subagent-start.js +8 -5
  88. package/hooks/dist/gsd-read-injection-scanner.js +2 -2
  89. package/hooks/dist/gsd-secret-read-guard.js +141 -6
  90. package/hooks/dist/gsd-statusline.js +3 -2
  91. package/hooks/dist/gsd-workflow-guard.js +2 -1
  92. package/hooks/dist/lib/config-read.js +49 -0
  93. package/hooks/dist/lib/git-cmd.js +3 -1
  94. package/hooks/dist/lib/git-head-tree.js +474 -0
  95. package/hooks/dist/lib/isolation-sentinel.js +97 -17
  96. package/hooks/dist/lib/secret-read-exemptions.js +161 -0
  97. package/hooks/gsd-agent-isolation-guard.js +3 -2
  98. package/hooks/gsd-config-reload.js +2 -2
  99. package/hooks/gsd-context-monitor.js +2 -1
  100. package/hooks/gsd-cursor-subagent-start.js +8 -5
  101. package/hooks/gsd-read-injection-scanner.js +2 -2
  102. package/hooks/gsd-secret-read-guard.js +141 -6
  103. package/hooks/gsd-statusline.js +3 -2
  104. package/hooks/gsd-workflow-guard.js +2 -1
  105. package/hooks/lib/config-read.js +49 -0
  106. package/hooks/lib/git-cmd.js +3 -1
  107. package/hooks/lib/git-head-tree.js +474 -0
  108. package/hooks/lib/isolation-sentinel.js +97 -17
  109. package/hooks/lib/secret-read-exemptions.js +161 -0
  110. package/package.json +7 -2
  111. package/pi/package-agents/gsd-executor.md +1 -1
  112. package/pi/package-agents/gsd-plan-checker.md +1 -1
  113. package/pi/package-agents/gsd-planner.md +3 -3
  114. package/pi/package-agents/gsd-verifier.md +2 -2
  115. package/pi/package-agents/manifest.json +4 -4
  116. package/pi/package-skills/gsd-autonomous/SKILL.md +1 -1
  117. package/scripts/changeset/cli.cjs +2 -2
  118. package/scripts/changeset/serialize.cjs +12 -2
  119. package/scripts/ci-compact-v8-coverage.cjs +204 -0
  120. package/scripts/ci-fix-next-rerun.cjs +149 -0
  121. package/scripts/ci-next-health.cjs +52 -9
  122. package/scripts/ci-test-scope.cjs +77 -71
  123. package/scripts/command-contract-helpers.cjs +56 -7
  124. package/scripts/docs-guard-registry.cjs +3 -0
  125. package/scripts/gen-cli-surface-manifest.cjs +287 -0
  126. package/scripts/gen-platform-conformance-tier.cjs +258 -19
  127. package/scripts/lib/macos-conformance-tier.generated.cjs +55 -47
  128. package/scripts/lib/platform-conformance-tier.generated.cjs +19 -64
  129. package/scripts/lib/shellcheck-fetch.cjs +71 -4
  130. package/scripts/lint-removed-but-needed.cjs +562 -50
  131. package/scripts/release-notes/changelog-release-notes.cjs +345 -0
  132. package/scripts/release-notes/discord-release-summary.cjs +82 -1
  133. package/scripts/timeout-evidence-from-ci.cjs +964 -0
  134. package/skills/gsd-autonomous/SKILL.md +1 -1
  135. package/vscode/package.json +1 -1
@@ -15,6 +15,14 @@
15
15
  * reader (no third-party archive library), and caches the extracted binary
16
16
  * for reuse across runs.
17
17
  *
18
+ * The download retries a transient failure (HTTP 408/429/5xx, network error,
19
+ * request timeout) up to DOWNLOAD_ATTEMPTS times, sleeping DOWNLOAD_BACKOFF_MS
20
+ * between attempts (#6095). The attempt count and the DOWNLOAD_BACKOFF_MS
21
+ * sleeps are bounded; total wall time is not bounded by this module, because
22
+ * DOWNLOAD_TIMEOUT_MS is a per-hop socket idle timeout (a body that keeps
23
+ * trickling bytes never trips it). The CI job's timeout-minutes is the outer
24
+ * bound. A non-transient failure (404, too many redirects) fails at once.
25
+ *
18
26
  * Zip-slip defense: unlike `decompress`, which wrote extracted files using
19
27
  * PATHS TAKEN FROM THE ARCHIVE (the exact defect class in GHSA-mp2f-45pm-
20
28
  * 3cg9 — a malicious archive entry named e.g. `../../etc/passwd` gets
@@ -62,9 +70,18 @@ const MAX_REDIRECTS = 5;
62
70
  // SHELLCHECK_TIMEOUT_MS bound on the ShellCheck subprocess. A bare `timeout`
63
71
  // option on the request does NOT abort it by itself — Node only emits a
64
72
  // 'timeout' event, which must be handled by destroying the request (see the
65
- // `req.on('timeout', ...)` below).
73
+ // `req.on('timeout', ...)` below). This is a per-hop IDLE timeout (socket
74
+ // inactivity): a stalled hop fails after it, but a body that keeps trickling
75
+ // bytes never trips it. downloadWithRetry bounds the attempt count and the
76
+ // DOWNLOAD_BACKOFF_MS sleeps; total wall time is not bounded by this module
77
+ // (the CI job's timeout-minutes is the outer bound).
66
78
  const DOWNLOAD_TIMEOUT_MS = 30_000;
67
79
 
80
+ // Total download attempts and the sleeps (ms) between them: a sleep of
81
+ // DOWNLOAD_BACKOFF_MS[i] follows failed attempt i + 1 (#6095).
82
+ const DOWNLOAD_ATTEMPTS = 3;
83
+ const DOWNLOAD_BACKOFF_MS = [2000, 5000];
84
+
68
85
  /**
69
86
  * Issue one real HTTPS request. Exists as its own function purely so tests
70
87
  * can inject a fake in its place (see `httpsGetFollowingRedirects`'s
@@ -95,7 +112,9 @@ function httpsGetFollowingRedirects(url, redirectsLeft = MAX_REDIRECTS, requestF
95
112
  if (status >= 300 && status < 400 && res.headers.location) {
96
113
  res.resume(); // drain so the socket can be reused/closed
97
114
  if (redirectsLeft <= 0) {
98
- reject(new Error(`too many redirects fetching ${url}`));
115
+ const err = new Error(`too many redirects fetching ${url}`);
116
+ err.code = 'TOO_MANY_REDIRECTS';
117
+ reject(err);
99
118
  return;
100
119
  }
101
120
  const next = new URL(res.headers.location, url).toString();
@@ -104,7 +123,9 @@ function httpsGetFollowingRedirects(url, redirectsLeft = MAX_REDIRECTS, requestF
104
123
  }
105
124
  if (status !== 200) {
106
125
  res.resume();
107
- reject(new Error(`unexpected HTTP ${status} fetching ${url}`));
126
+ const err = new Error(`unexpected HTTP ${status} fetching ${url}`);
127
+ err.status = status;
128
+ reject(err);
108
129
  return;
109
130
  }
110
131
  const chunks = [];
@@ -124,6 +145,48 @@ function httpsGetFollowingRedirects(url, redirectsLeft = MAX_REDIRECTS, requestF
124
145
  });
125
146
  }
126
147
 
148
+ /**
149
+ * True when a download failure is worth retrying: HTTP 408, 429 or 5xx, or an
150
+ * error with no HTTP status (network error, request timeout, socket reset).
151
+ * False for any other status (e.g. 404, a wrong pinned asset) and for
152
+ * `too many redirects`, which is deterministic.
153
+ */
154
+ function isTransientDownloadError(err) {
155
+ const status = err && err.status;
156
+ if (typeof status === 'number') {
157
+ return status === 408 || status === 429 || (status >= 500 && status <= 599);
158
+ }
159
+ return !(err && err.code === 'TOO_MANY_REDIRECTS');
160
+ }
161
+
162
+ /**
163
+ * GET `url` via `getFn`, retrying a transient failure up to `attempts` times
164
+ * with `backoffMs[i]` sleeps between attempts. A non-transient error rejects
165
+ * at once; exhausting the attempts rejects with one Error listing every
166
+ * attempt. `sleep` and `getFn` are injectable so tests never wait.
167
+ */
168
+ async function downloadWithRetry(
169
+ url,
170
+ {
171
+ attempts = DOWNLOAD_ATTEMPTS,
172
+ backoffMs = DOWNLOAD_BACKOFF_MS,
173
+ sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
174
+ getFn = httpsGetFollowingRedirects,
175
+ } = {},
176
+ ) {
177
+ const failures = [];
178
+ for (let attempt = 1; attempt <= attempts; attempt++) {
179
+ try {
180
+ return await getFn(url);
181
+ } catch (e) {
182
+ if (!isTransientDownloadError(e)) throw e;
183
+ failures.push(`attempt ${attempt}: ${e.message}`);
184
+ if (attempt < attempts) await sleep(backoffMs[Math.min(attempt - 1, backoffMs.length - 1)]);
185
+ }
186
+ }
187
+ throw new Error(failures.join('; '));
188
+ }
189
+
127
190
  /**
128
191
  * Find the entry named (or path-ending-in) `targetName` inside a raw
129
192
  * (already gunzipped) POSIX tar byte stream and return its content as a
@@ -204,7 +267,7 @@ async function resolveShellcheckBin() {
204
267
 
205
268
  let gz;
206
269
  try {
207
- gz = await httpsGetFollowingRedirects(url);
270
+ gz = await downloadWithRetry(url);
208
271
  } catch (e) {
209
272
  throw new ExitError(1, `lint-workflow-shellcheck: failed to download ShellCheck (${url}): ${e.message}`);
210
273
  }
@@ -241,7 +304,11 @@ module.exports = {
241
304
  CACHED_BIN_PATH,
242
305
  MAX_REDIRECTS,
243
306
  DOWNLOAD_TIMEOUT_MS,
307
+ DOWNLOAD_ATTEMPTS,
308
+ DOWNLOAD_BACKOFF_MS,
244
309
  extractFileFromTar,
245
310
  httpsGetFollowingRedirects,
311
+ isTransientDownloadError,
312
+ downloadWithRetry,
246
313
  resolveShellcheckBin,
247
314
  };