@opengsd/gsd-core 1.18.0 → 1.19.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 (257) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.opencode/plugins/gsd-core.js +2 -3
  4. package/agents/gsd-code-fixer.compact.md +2 -2
  5. package/agents/gsd-code-fixer.md +4 -3
  6. package/agents/gsd-verifier.md +1 -1
  7. package/bin/install.js +422 -103
  8. package/gsd-core/bin/gsd-tools.cjs +106 -7
  9. package/gsd-core/bin/lib/agent-install-check.cjs +29 -3
  10. package/gsd-core/bin/lib/args-terminator.cjs +114 -0
  11. package/gsd-core/bin/lib/broken-windows.cjs +59 -1
  12. package/gsd-core/bin/lib/capability-lock.cjs +13 -2
  13. package/gsd-core/bin/lib/capability-registry.cjs +156 -74
  14. package/gsd-core/bin/lib/capability-state.cjs +14 -8
  15. package/gsd-core/bin/lib/capability-validator.cjs +17 -0
  16. package/gsd-core/bin/lib/config-loader.cjs +10 -0
  17. package/gsd-core/bin/lib/config.cjs +256 -58
  18. package/gsd-core/bin/lib/core-utils.cjs +34 -0
  19. package/gsd-core/bin/lib/decision-coverage-support.cjs +74 -0
  20. package/gsd-core/bin/lib/gate-context-drift.cjs +8 -3
  21. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +6 -1
  22. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +6 -1
  23. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
  24. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +63 -22
  25. package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
  26. package/gsd-core/bin/lib/git-info-exclude.cjs +57 -0
  27. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +18 -3
  28. package/gsd-core/bin/lib/health-diagnostic-types.cjs +4 -0
  29. package/gsd-core/bin/lib/health-diagnostic.cjs +52 -1
  30. package/gsd-core/bin/lib/host-integration.cjs +13 -6
  31. package/gsd-core/bin/lib/init.cjs +65 -1
  32. package/gsd-core/bin/lib/install-engine.cjs +246 -32
  33. package/gsd-core/bin/lib/install-profiles.cjs +9 -4
  34. package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
  35. package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
  36. package/gsd-core/bin/lib/markdown-sectionizer.cjs +113 -0
  37. package/gsd-core/bin/lib/milestone-lock.cjs +8 -1
  38. package/gsd-core/bin/lib/milestone-release-map.cjs +244 -0
  39. package/gsd-core/bin/lib/milestone-release-tags.cjs +440 -0
  40. package/gsd-core/bin/lib/milestone.cjs +43 -1
  41. package/gsd-core/bin/lib/model-catalog.cjs +8 -0
  42. package/gsd-core/bin/lib/normalize-test-command.cjs +67 -0
  43. package/gsd-core/bin/lib/phase-id.cjs +21 -4
  44. package/gsd-core/bin/lib/phase-lifecycle.cjs +37 -4
  45. package/gsd-core/bin/lib/phase.cjs +115 -26
  46. package/gsd-core/bin/lib/planning-snapshot.cjs +3 -1
  47. package/gsd-core/bin/lib/planning-workspace.cjs +18 -0
  48. package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
  49. package/gsd-core/bin/lib/read-only-git-env.cjs +58 -0
  50. package/gsd-core/bin/lib/review-lane-descriptor.cjs +35 -2
  51. package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
  52. package/gsd-core/bin/lib/roadmap-parser.cjs +12 -0
  53. package/gsd-core/bin/lib/roadmap-upgrade.cjs +39 -2
  54. package/gsd-core/bin/lib/roadmap.cjs +33 -25
  55. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +381 -14
  56. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
  57. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
  58. package/gsd-core/bin/lib/shell-command-projection.cjs +121 -16
  59. package/gsd-core/bin/lib/smart-entry.cjs +13 -0
  60. package/gsd-core/bin/lib/state-contract.cjs +29 -9
  61. package/gsd-core/bin/lib/state-document.cjs +12 -4
  62. package/gsd-core/bin/lib/state-transition.cjs +119 -24
  63. package/gsd-core/bin/lib/state.cjs +50 -28
  64. package/gsd-core/bin/lib/surface.cjs +9 -0
  65. package/gsd-core/bin/lib/task-command-router.cjs +5 -25
  66. package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
  67. package/gsd-core/bin/lib/verification.cjs +96 -9
  68. package/gsd-core/bin/lib/verify.cjs +2 -0
  69. package/gsd-core/bin/lib/worktree-safety.cjs +22 -1
  70. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  71. package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
  72. package/gsd-core/references/git-integration.md +11 -0
  73. package/gsd-core/references/loop-hook-dispatch.md +13 -6
  74. package/gsd-core/references/planning-config.md +2 -0
  75. package/gsd-core/references/tdd.md +2 -2
  76. package/gsd-core/templates/DEBUG.md +16 -0
  77. package/gsd-core/templates/config.json +2 -1
  78. package/gsd-core/templates/verification-report.md +1 -1
  79. package/gsd-core/workflows/audit-fix.md +2 -1
  80. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
  81. package/gsd-core/workflows/code-review.md +61 -7
  82. package/gsd-core/workflows/complete-milestone/steps/project-md-evolution-review.md +145 -0
  83. package/gsd-core/workflows/complete-milestone/steps/release-map.md +80 -0
  84. package/gsd-core/workflows/complete-milestone.md +37 -150
  85. package/gsd-core/workflows/diagnose-issues.md +29 -5
  86. package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
  87. package/gsd-core/workflows/discuss-phase-assumptions.md +33 -2
  88. package/gsd-core/workflows/discuss-phase-power.md +19 -2
  89. package/gsd-core/workflows/discuss-phase.md +6 -10
  90. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +13 -7
  91. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +7 -2
  92. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -1
  93. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +2 -2
  94. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
  95. package/gsd-core/workflows/execute-phase.md +9 -5
  96. package/gsd-core/workflows/health.md +2 -1
  97. package/gsd-core/workflows/import.md +1 -1
  98. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +2 -2
  99. package/gsd-core/workflows/plan-phase.md +13 -13
  100. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -1
  101. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +8 -1
  102. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +2 -1
  103. package/gsd-core/workflows/quick.md +1 -1
  104. package/gsd-core/workflows/review.md +16 -13
  105. package/gsd-core/workflows/section-manifest.json +5 -0
  106. package/gsd-core/workflows/settings.md +1 -0
  107. package/gsd-core/workflows/ship.md +3 -4
  108. package/gsd-core/workflows/spec-phase.md +3 -6
  109. package/gsd-core/workflows/verify-work.md +4 -3
  110. package/hooks/dist/gsd-agent-isolation-guard.js +11 -1
  111. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  112. package/hooks/dist/gsd-path-export.js +95 -0
  113. package/hooks/dist/gsd-secret-read-guard.js +172 -39
  114. package/hooks/dist/gsd-statusline.js +35 -2
  115. package/hooks/dist/gsd-windsurf-pre-write.js +28 -1
  116. package/hooks/dist/gsd-workflow-guard.js +1 -1
  117. package/hooks/dist/gsd-worktree-path-guard.js +28 -1
  118. package/hooks/dist/lib/isolation-sentinel.js +44 -0
  119. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  120. package/hooks/gsd-agent-isolation-guard.js +11 -1
  121. package/hooks/gsd-cursor-subagent-start.js +6 -2
  122. package/hooks/gsd-path-export.js +95 -0
  123. package/hooks/gsd-secret-read-guard.js +172 -39
  124. package/hooks/gsd-statusline.js +35 -2
  125. package/hooks/gsd-windsurf-pre-write.js +28 -1
  126. package/hooks/gsd-workflow-guard.js +1 -1
  127. package/hooks/gsd-worktree-path-guard.js +28 -1
  128. package/hooks/hooks.json +1 -0
  129. package/hooks/lib/isolation-sentinel.js +44 -0
  130. package/hooks/managed-hooks-registry.cjs +1 -0
  131. package/package.json +13 -6
  132. package/pi/gsd.cjs +1047 -10
  133. package/pi/package-agents/gsd-advisor-researcher.md +113 -0
  134. package/pi/package-agents/gsd-ai-researcher.md +111 -0
  135. package/pi/package-agents/gsd-assumptions-analyzer.md +110 -0
  136. package/pi/package-agents/gsd-code-fixer.md +769 -0
  137. package/pi/package-agents/gsd-code-reviewer.md +402 -0
  138. package/pi/package-agents/gsd-codebase-mapper.md +850 -0
  139. package/pi/package-agents/gsd-debug-session-manager.md +416 -0
  140. package/pi/package-agents/gsd-debugger.md +1274 -0
  141. package/pi/package-agents/gsd-doc-classifier.md +269 -0
  142. package/pi/package-agents/gsd-doc-synthesizer.md +259 -0
  143. package/pi/package-agents/gsd-doc-verifier.md +212 -0
  144. package/pi/package-agents/gsd-doc-writer.md +626 -0
  145. package/pi/package-agents/gsd-dom-verifier.md +164 -0
  146. package/pi/package-agents/gsd-domain-researcher.md +144 -0
  147. package/pi/package-agents/gsd-eval-auditor.md +185 -0
  148. package/pi/package-agents/gsd-eval-planner.md +149 -0
  149. package/pi/package-agents/gsd-executor.md +894 -0
  150. package/pi/package-agents/gsd-framework-selector.md +159 -0
  151. package/pi/package-agents/gsd-integration-checker.md +476 -0
  152. package/pi/package-agents/gsd-intel-updater.md +338 -0
  153. package/pi/package-agents/gsd-mempalace-curator.md +49 -0
  154. package/pi/package-agents/gsd-nyquist-auditor.md +200 -0
  155. package/pi/package-agents/gsd-pattern-mapper.md +340 -0
  156. package/pi/package-agents/gsd-phase-researcher.md +897 -0
  157. package/pi/package-agents/gsd-plan-checker.md +1077 -0
  158. package/pi/package-agents/gsd-planner.md +1003 -0
  159. package/pi/package-agents/gsd-project-researcher.md +611 -0
  160. package/pi/package-agents/gsd-research-synthesizer.md +259 -0
  161. package/pi/package-agents/gsd-roadmapper.md +779 -0
  162. package/pi/package-agents/gsd-security-auditor.md +171 -0
  163. package/pi/package-agents/gsd-ui-auditor.md +581 -0
  164. package/pi/package-agents/gsd-ui-checker.md +417 -0
  165. package/pi/package-agents/gsd-ui-researcher.md +469 -0
  166. package/pi/package-agents/gsd-user-profiler.md +174 -0
  167. package/pi/package-agents/gsd-verifier.md +963 -0
  168. package/pi/package-agents/manifest.json +39 -0
  169. package/pi/package-skills/gsd-add-tests/SKILL.md +39 -0
  170. package/pi/package-skills/gsd-ai-integration-phase/SKILL.md +36 -0
  171. package/pi/package-skills/gsd-audit-fix/SKILL.md +34 -0
  172. package/pi/package-skills/gsd-audit-milestone/SKILL.md +40 -0
  173. package/pi/package-skills/gsd-audit-uat/SKILL.md +26 -0
  174. package/pi/package-skills/gsd-autonomous/SKILL.md +52 -0
  175. package/pi/package-skills/gsd-capture/SKILL.md +68 -0
  176. package/pi/package-skills/gsd-cleanup/SKILL.md +25 -0
  177. package/pi/package-skills/gsd-code-review/SKILL.md +71 -0
  178. package/pi/package-skills/gsd-complete-milestone/SKILL.md +147 -0
  179. package/pi/package-skills/gsd-config/SKILL.md +60 -0
  180. package/pi/package-skills/gsd-debug/SKILL.md +64 -0
  181. package/pi/package-skills/gsd-discuss-phase/SKILL.md +74 -0
  182. package/pi/package-skills/gsd-docs-update/SKILL.md +50 -0
  183. package/pi/package-skills/gsd-eval-review/SKILL.md +35 -0
  184. package/pi/package-skills/gsd-execute-phase/SKILL.md +62 -0
  185. package/pi/package-skills/gsd-explore/SKILL.md +26 -0
  186. package/pi/package-skills/gsd-extract-learnings/SKILL.md +25 -0
  187. package/pi/package-skills/gsd-fast/SKILL.md +33 -0
  188. package/pi/package-skills/gsd-forensics/SKILL.md +59 -0
  189. package/pi/package-skills/gsd-graphify/SKILL.md +210 -0
  190. package/pi/package-skills/gsd-health/SKILL.md +36 -0
  191. package/pi/package-skills/gsd-help/SKILL.md +37 -0
  192. package/pi/package-skills/gsd-import/SKILL.md +46 -0
  193. package/pi/package-skills/gsd-inbox/SKILL.md +42 -0
  194. package/pi/package-skills/gsd-ingest-docs/SKILL.md +43 -0
  195. package/pi/package-skills/gsd-manager/SKILL.md +46 -0
  196. package/pi/package-skills/gsd-map-codebase/SKILL.md +85 -0
  197. package/pi/package-skills/gsd-mempalace-capture/SKILL.md +111 -0
  198. package/pi/package-skills/gsd-mempalace-recall/SKILL.md +111 -0
  199. package/pi/package-skills/gsd-milestone-summary/SKILL.md +54 -0
  200. package/pi/package-skills/gsd-mvp-phase/SKILL.md +45 -0
  201. package/pi/package-skills/gsd-new-milestone/SKILL.md +49 -0
  202. package/pi/package-skills/gsd-new-project/SKILL.md +49 -0
  203. package/pi/package-skills/gsd-next/SKILL.md +34 -0
  204. package/pi/package-skills/gsd-ns-context/SKILL.md +31 -0
  205. package/pi/package-skills/gsd-ns-ideate/SKILL.md +30 -0
  206. package/pi/package-skills/gsd-ns-manage/SKILL.md +42 -0
  207. package/pi/package-skills/gsd-ns-project/SKILL.md +34 -0
  208. package/pi/package-skills/gsd-ns-review/SKILL.md +35 -0
  209. package/pi/package-skills/gsd-ns-workflow/SKILL.md +44 -0
  210. package/pi/package-skills/gsd-onboard/SKILL.md +46 -0
  211. package/pi/package-skills/gsd-pause-work/SKILL.md +48 -0
  212. package/pi/package-skills/gsd-phase/SKILL.md +61 -0
  213. package/pi/package-skills/gsd-plan-phase/SKILL.md +62 -0
  214. package/pi/package-skills/gsd-plan-review-convergence/SKILL.md +63 -0
  215. package/pi/package-skills/gsd-pr-branch/SKILL.md +31 -0
  216. package/pi/package-skills/gsd-profile-user/SKILL.md +48 -0
  217. package/pi/package-skills/gsd-progress/SKILL.md +51 -0
  218. package/pi/package-skills/gsd-quick/SKILL.md +179 -0
  219. package/pi/package-skills/gsd-quick-batch/SKILL.md +114 -0
  220. package/pi/package-skills/gsd-resume-work/SKILL.md +31 -0
  221. package/pi/package-skills/gsd-review/SKILL.md +50 -0
  222. package/pi/package-skills/gsd-review-backlog/SKILL.md +65 -0
  223. package/pi/package-skills/gsd-secure-phase/SKILL.md +37 -0
  224. package/pi/package-skills/gsd-settings/SKILL.md +30 -0
  225. package/pi/package-skills/gsd-ship/SKILL.md +27 -0
  226. package/pi/package-skills/gsd-sketch/SKILL.md +56 -0
  227. package/pi/package-skills/gsd-spec-phase/SKILL.md +63 -0
  228. package/pi/package-skills/gsd-spike/SKILL.md +53 -0
  229. package/pi/package-skills/gsd-stats/SKILL.md +22 -0
  230. package/pi/package-skills/gsd-surface/SKILL.md +177 -0
  231. package/pi/package-skills/gsd-thread/SKILL.md +29 -0
  232. package/pi/package-skills/gsd-ui-phase/SKILL.md +35 -0
  233. package/pi/package-skills/gsd-ui-review/SKILL.md +35 -0
  234. package/pi/package-skills/gsd-ultraplan-phase/SKILL.md +38 -0
  235. package/pi/package-skills/gsd-undo/SKILL.md +38 -0
  236. package/pi/package-skills/gsd-update/SKILL.md +51 -0
  237. package/pi/package-skills/gsd-validate-phase/SKILL.md +37 -0
  238. package/pi/package-skills/gsd-verify-work/SKILL.md +41 -0
  239. package/pi/package-skills/gsd-workspace/SKILL.md +57 -0
  240. package/pi/package-skills/gsd-workstreams/SKILL.md +73 -0
  241. package/scripts/build-hooks.js +4 -0
  242. package/scripts/command-contract-helpers.cjs +1 -1
  243. package/scripts/docs-guard-registry.cjs +20 -7
  244. package/scripts/gen-health-docs.cjs +13 -1
  245. package/scripts/gen-pi-package-skills.cjs +217 -0
  246. package/scripts/lib/macos-conformance-tier.generated.cjs +11 -1
  247. package/scripts/lib/platform-conformance-tier.generated.cjs +26 -1
  248. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
  249. package/scripts/lint-retired-runtime-name.cjs +5 -0
  250. package/scripts/npm-audit-baseline.cjs +41 -7
  251. package/scripts/reviewed-or-maintainer-policy.cjs +181 -0
  252. package/scripts/select-docs-guards.cjs +1 -1
  253. package/scripts/self-test-report-policy.cjs +350 -0
  254. package/vscode/package.json +1 -1
  255. package/scripts/diagnose-validate-commit-in-tier.cjs +0 -270
  256. package/scripts/diagnose-validate-commit-phases.cjs +0 -348
  257. package/scripts/xtrace-phase-timing.cjs +0 -205
@@ -889,8 +889,40 @@ function compactModelName(name) {
889
889
  // are a fixed array, and the workspace dir is passed via -C. Fails silently
890
890
  // (segment absent) outside a repo, without git, or on timeout.
891
891
 
892
+ // #5048: bound for the `git status` spawn below. With GIT_OPTIONAL_LOCKS=0 the
893
+ // spawn no longer writes the refreshed index back, so what is left is reading
894
+ // the index and stat-ing the tracked paths. The bound keeps a hung network
895
+ // filesystem from stalling a render for long while leaving room for a cold stat
896
+ // cache on a large repo. The figure was chosen, not measured: time
897
+ // `git status --porcelain=v2 --branch` on a large tree with a cold cache before
898
+ // changing it. Exported so the test can pin the bound instead of restating the
899
+ // literal (which is how the constant and its assertion drifted before).
892
900
  const GIT_STATUS_TIMEOUT_MS = 1500;
893
901
 
902
+ /**
903
+ * #5048: env for the read-only git spawn in this file.
904
+ *
905
+ * By default `git status` refreshes the index and takes an *optional*
906
+ * `.git/index.lock` to write the refreshed copy back. That is a write the
907
+ * caller never asked for, and it turns a pure read into a contender for the
908
+ * lock a real `git add` / `git commit` needs — a read-only statusline render
909
+ * can then make a concurrent commit fail with
910
+ * `Unable to create '.git/index.lock': File exists`. The statusline spawns
911
+ * git on every render, so the lock is re-created continuously.
912
+ *
913
+ * The env block itself is owned by src/read-only-git-env.cts and shared with
914
+ * every other read-only seam (`execGit` in src/shell-command-projection.cts,
915
+ * `readGitSignals` in src/smart-entry.cts, `gitExec` in
916
+ * src/pristine-baseline.cts, the two blocking guards). This hook reads it per
917
+ * call rather than snapshotting at load, so a later `process.env` mutation is
918
+ * visible and the seams behave identically.
919
+ *
920
+ * Deliberately NOT applied to the `git rev-list` spawn in
921
+ * readStateHeadCommits(): that walks the commit graph and never opens the
922
+ * index, so the variable would be a no-op there.
923
+ */
924
+ const { readOnlyGitEnv } = require('../gsd-core/bin/lib/read-only-git-env.cjs');
925
+
894
926
  /**
895
927
  * Run `git status --porcelain=v2 --branch` in dir.
896
928
  * Returns raw stdout, or null when git is missing, dir isn't a repo, or the
@@ -902,7 +934,7 @@ function readGitStatus(dir) {
902
934
  // or untracked files; overflow still degrades safely to segment-absent via
903
935
  // the catch below.
904
936
  return childProcess.execFileSync('git', ['-C', dir, 'status', '--porcelain=v2', '--branch'],
905
- { encoding: 'utf8', timeout: GIT_STATUS_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true });
937
+ { encoding: 'utf8', timeout: GIT_STATUS_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true, env: readOnlyGitEnv() });
906
938
  } catch (e) {
907
939
  return null;
908
940
  }
@@ -995,7 +1027,7 @@ function isValidStateHeadStamp(raw) {
995
1027
  */
996
1028
  function readStateHeadCommits(root, stamp) {
997
1029
  try {
998
- return childProcess.execFileSync('git',
1030
+ return childProcess.execFileSync('git', // allow-index-refresh: rev-list walks the commit graph and never opens the index
999
1031
  ['-C', root, 'rev-list', '--left-right', '--count', `${stamp}...HEAD`],
1000
1032
  { encoding: 'utf8', timeout: STATE_FRESHNESS_GIT_TIMEOUT_MS,
1001
1033
  stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true });
@@ -1396,6 +1428,7 @@ module.exports = {
1396
1428
  isAutoCompactDisabled, AUTO_COMPACT_DISABLE_ENV_KEYS, AUTO_COMPACT_ENV_TRUTHY,
1397
1429
  resolveAutoCompactThreshold, contextMeter, SETTINGS_FILE_MAX_BYTES,
1398
1430
  parseAutoCompactWindowEnv, canonicalModelKey, isAutoCompactWindowValue,
1431
+ GIT_STATUS_TIMEOUT_MS,
1399
1432
  };
1400
1433
 
1401
1434
  /**
@@ -49,8 +49,35 @@ const { reportIfUndetermined, BLOCKING_GUARD_PROBE_TIMEOUT_MS } = require('./lib
49
49
  // timeout does. The guard keeps its fail-open posture either way.
50
50
  const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS, windowsHide: true };
51
51
 
52
+ // #5048: GIT_OPTIONAL_LOCKS=0 on every git call from this hook. `git rev-parse`
53
+ // never opens the index, so this is inert for today's callers; it is set anyway
54
+ // so the read-only invariant has no holes if a `status` / `diff` caller is ever
55
+ // added here. git refreshes the index and takes an *optional*
56
+ // `.git/index.lock` on some reads, and this hook runs on every tool call, so a
57
+ // read here can otherwise fail a concurrent `git add` / `git commit` with
58
+ // `Unable to create '.git/index.lock': File exists`.
59
+ //
60
+ // The env block is owned by src/read-only-git-env.cts and shared with every
61
+ // other read-only seam (built artifact, not a sibling hooks/-tree file — same
62
+ // rule hooks/lib/git-cmd.js follows for token-scanner). It is read per spawn,
63
+ // not snapshotted at load, so the two guards behave identically.
64
+ // Parity: tests/git-optional-locks-parity.test.cjs.
65
+ // A raw plugin-marketplace or git-clone install never ran `npm run build:lib`
66
+ // (#3582), so the compiled owner can be absent. This guard only runs
67
+ // `git rev-parse`, which never opens the index, so the unmodified environment
68
+ // loses nothing; requiring the artifact unguarded would throw at load and the
69
+ // containment guard would silently stop running.
70
+ let readOnlyGitEnv = () => process.env;
71
+ try {
72
+ const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
73
+ ensureRuntimeBuild();
74
+ ({ readOnlyGitEnv } = require('../gsd-core/bin/lib/read-only-git-env.cjs'));
75
+ } catch {
76
+ // artifact absent and not buildable: keep the inert fallback above
77
+ }
78
+
52
79
  function git(args, cwd) {
53
- return spawnSync('git', args, { ...SPAWNOPT, cwd });
80
+ return spawnSync('git', args, { ...SPAWNOPT, cwd, env: readOnlyGitEnv() });
54
81
  }
55
82
 
56
83
  // Walk up from `start` to find the nearest existing DIRECTORY (not merely an
@@ -81,7 +81,7 @@ function forceGitAddCwds(command, defaultCwd) {
81
81
  }
82
82
 
83
83
  function currentBranch(cwd) {
84
- const result = spawnSync('git', ['branch', '--show-current'], {
84
+ const result = spawnSync('git', ['branch', '--show-current'], { // allow-index-refresh: reads HEAD only, never opens the index
85
85
  cwd,
86
86
  encoding: 'utf8',
87
87
  stdio: ['ignore', 'pipe', 'ignore'],
@@ -34,8 +34,35 @@ const ON_CRASH = HOOK_ON_CRASH.ALLOW;
34
34
  // hooks/lib/git-probe.js, #5180). This guard runs at most 3 sequential probes.
35
35
  const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS, windowsHide: true };
36
36
 
37
+ // #5048: GIT_OPTIONAL_LOCKS=0 on every git call from this hook. `git rev-parse`
38
+ // never opens the index, so this is inert for today's callers; it is set anyway
39
+ // so the read-only invariant has no holes if a `status` / `diff` caller is ever
40
+ // added here. git refreshes the index and takes an *optional*
41
+ // `.git/index.lock` on some reads, and this hook runs on every tool call, so a
42
+ // read here can otherwise fail a concurrent `git add` / `git commit` with
43
+ // `Unable to create '.git/index.lock': File exists`.
44
+ //
45
+ // The env block is owned by src/read-only-git-env.cts and shared with every
46
+ // other read-only seam (built artifact, not a sibling hooks/-tree file — same
47
+ // rule hooks/lib/git-cmd.js follows for token-scanner). It is read per spawn,
48
+ // not snapshotted at load, so the two guards behave identically.
49
+ // Parity: tests/git-optional-locks-parity.test.cjs.
50
+ // A raw plugin-marketplace or git-clone install never ran `npm run build:lib`
51
+ // (#3582), so the compiled owner can be absent. This guard only runs
52
+ // `git rev-parse`, which never opens the index, so the unmodified environment
53
+ // loses nothing; requiring the artifact unguarded would throw at load and the
54
+ // containment guard would silently stop running.
55
+ let readOnlyGitEnv = () => process.env;
56
+ try {
57
+ const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
58
+ ensureRuntimeBuild();
59
+ ({ readOnlyGitEnv } = require('../gsd-core/bin/lib/read-only-git-env.cjs'));
60
+ } catch {
61
+ // artifact absent and not buildable: keep the inert fallback above
62
+ }
63
+
37
64
  function git(args, cwd) {
38
- return spawnSync('git', args, { ...SPAWNOPT, cwd });
65
+ return spawnSync('git', args, { ...SPAWNOPT, cwd, env: readOnlyGitEnv() });
39
66
  }
40
67
 
41
68
  // Walk up from `start` to find the nearest existing directory.
package/hooks/hooks.json CHANGED
@@ -4,6 +4,7 @@
4
4
  {
5
5
  "hooks": [
6
6
  { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-ensure-canonical-path.js\"", "timeout": 5 },
7
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-path-export.js\"", "timeout": 5 },
7
8
  { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-check-update.js\"" }
8
9
  ]
9
10
  }
@@ -91,6 +91,11 @@ const SENTINEL_RELATIVE_PATH = path.join('.gsd', 'dispatch-isolation-sentinel.js
91
91
  // later, unrelated phase.
92
92
  const SENTINEL_STALE_MS = 10 * 60 * 1000; // 10 minutes
93
93
 
94
+ // #5479: the longest a recorded sequential degrade keeps governing its own phase after the trust
95
+ // window (see agedNoneGovernsDispatch). A single executor run is minutes to a couple of hours; a
96
+ // day is a generous bound that still ends a stale or forged record's authority.
97
+ const AGED_NONE_MAX_MS = 24 * 60 * 60 * 1000;
98
+
94
99
  function sentinelPath(cwd) {
95
100
  return path.join(cwd, SENTINEL_RELATIVE_PATH);
96
101
  }
@@ -280,6 +285,43 @@ function sentinelAppliesToDispatch(sentinel, dispatchIds) {
280
285
  return true;
281
286
  }
282
287
 
288
+ /**
289
+ * #5479: does an AGED recorded sequential degrade still govern THIS dispatch?
290
+ *
291
+ * The trust window (`SENTINEL_STALE_MS`) exists so a leftover sentinel cannot authorize an unisolated
292
+ * executor for a LATER, unrelated phase (#3045 F2). It also made the guard forget a legitimate
293
+ * degrade (the #683/#3060 base-check, the project opt-out) the moment the previous executor ran
294
+ * longer than the window: the next flag-less dispatch of the SAME phase was denied because the
295
+ * registry fallback answers "this host CAN isolate" and cannot see the shell-side degrade.
296
+ *
297
+ * True only when ALL of these hold, so the aged path can only ever loosen toward `none` for the exact
298
+ * phase the dispatch itself names:
299
+ * - the sentinel is present, well-formed, and records `isolation: 'none'` (an aged
300
+ * `harness-worktree`/`orchestrator-worktree` record authorizes nothing);
301
+ * - its `written_at` is not in the future (a forged or clock-skewed record is never trusted);
302
+ * - it carries a phase, the dispatch names a phase, and they are equal (a dispatch whose phase
303
+ * cannot be established is "cannot compare", which keeps today's fallback, never an allow).
304
+ * A plan-scoped record speaks only for its own plan (or a plan-less dispatch): a `none` recorded for
305
+ * a submodule-degraded plan A must not authorize a genuinely isolated plan B of the same phase. The
306
+ * phase-level degrade (the base-check, the project opt-out) is the record with no plan.
307
+ * - it is at most `AGED_NONE_MAX_MS` old: a static record, forged or left by a long-finished run,
308
+ * does not govern indefinitely. The normal flow re-records at every phase start and plan gate,
309
+ * so a live phase is always far younger.
310
+ * A fresh sentinel is handled by the existing path and never reaches here.
311
+ *
312
+ * `clock` is the injectable `{ now(): number }` seam `readSentinel` uses.
313
+ */
314
+ function agedNoneGovernsDispatch(sentinel, dispatchIds, { clock = Date } = {}) {
315
+ if (!sentinel || sentinel.present !== true || sentinel.malformed) return false;
316
+ if (sentinel.isolation !== 'none') return false;
317
+ if (typeof sentinel.writtenAt !== 'number') return false;
318
+ const age = clock.now() - sentinel.writtenAt;
319
+ if (age < -5000 || age > AGED_NONE_MAX_MS) return false;
320
+ if (!sentinel.phase || !dispatchIds || !dispatchIds.phase) return false;
321
+ if (sentinel.plan && dispatchIds.plan && sentinel.plan !== dispatchIds.plan) return false;
322
+ return sentinel.phase === dispatchIds.phase;
323
+ }
324
+
283
325
  /**
284
326
  * #4594 F3: build the structured "a fresh sentinel was present but did not
285
327
  * apply to this dispatch" descriptor, mirroring the exact comparison
@@ -307,10 +349,12 @@ module.exports = {
307
349
  VALID_ISOLATION,
308
350
  SENTINEL_RELATIVE_PATH,
309
351
  SENTINEL_STALE_MS,
352
+ AGED_NONE_MAX_MS,
310
353
  sentinelPath,
311
354
  resolveSentinelRoot,
312
355
  readSentinel,
313
356
  extractDispatchIdentifiers,
314
357
  sentinelAppliesToDispatch,
358
+ agedNoneGovernsDispatch,
315
359
  buildSentinelDiscard,
316
360
  };
@@ -29,6 +29,7 @@ const MANAGED_HOOKS = [
29
29
  'gsd-cursor-subagent-stop.js',
30
30
  'gsd-ensure-canonical-path.js',
31
31
  'gsd-graphify-update.sh',
32
+ 'gsd-path-export.js',
32
33
  // #3662: portable node resolver (helper staged in hooks/; managed JS hook
33
34
  // commands route through it under --portable-hooks).
34
35
  'gsd-node-runner.sh',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengsd/gsd-core",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
5
5
  "main": ".opencode/plugins/gsd-core.js",
6
6
  "bin": {
@@ -46,8 +46,14 @@
46
46
  "context-engineering",
47
47
  "spec-driven-development",
48
48
  "codex",
49
- "codex-cli"
49
+ "codex-cli",
50
+ "pi-package"
50
51
  ],
52
+ "pi": {
53
+ "extensions": [
54
+ "./pi/gsd.cjs"
55
+ ]
56
+ },
51
57
  "author": "OpenGSD",
52
58
  "license": "MIT",
53
59
  "repository": {
@@ -98,7 +104,7 @@
98
104
  "hono": ">=4.13.5"
99
105
  },
100
106
  "optionalDependencies": {
101
- "fallow": "^2.70.0"
107
+ "fallow": "^3.34.0"
102
108
  },
103
109
  "scripts": {
104
110
  "sync:launcher": "node scripts/sync-runtime-launcher.cjs",
@@ -108,13 +114,14 @@
108
114
  "check:identity-drift": "node scripts/lint-package-identity-drift.cjs",
109
115
  "check:phase-id-drift": "node scripts/lint-phase-id-drift.cjs",
110
116
  "check:integrity": "node scripts/check-npm-integrity.cjs",
111
- "build": "npm run generate:identity && npm run build:lib && npm run gen:section-manifest && npm run gen:context-index && npm run gen:plugin-skills && npm run gen:loop-host-contract && npm run gen:capability-registry && npm run build:hooks",
117
+ "build": "npm run generate:identity && npm run build:lib && npm run gen:section-manifest && npm run gen:context-index && npm run gen:plugin-skills && npm run gen:loop-host-contract && npm run gen:capability-registry && npm run gen:pi-package-skills && npm run build:hooks",
112
118
  "build:hooks": "node scripts/build-hooks.js",
113
119
  "build:lib": "tsc -p tsconfig.build.json",
114
120
  "generate:identity": "node scripts/generate-package-identity.cjs",
115
121
  "gen:context-index": "node scripts/gen-context-index.cjs --write",
116
122
  "gen:loop-host-contract": "node scripts/gen-loop-host-contract.cjs --write",
117
123
  "gen:plugin-skills": "node scripts/gen-plugin-skills.cjs --write",
124
+ "gen:pi-package-skills": "node scripts/gen-pi-package-skills.cjs --write",
118
125
  "gen:capability-registry": "node scripts/gen-capability-registry.cjs --write",
119
126
  "gen:registry": "node scripts/gen-registry.cjs --write",
120
127
  "gen:install-tree": "node scripts/gen-install-tree-fixtures.cjs",
@@ -124,7 +131,7 @@
124
131
  "prepack": "npm run build:lib",
125
132
  "prepare": "npm run build:lib",
126
133
  "version": "node scripts/sync-manifest-versions.cjs --stage && node scripts/gen-capability-registry.cjs --write && git add gsd-core/bin/lib/capability-registry.cjs",
127
- "prepublishOnly": "npm run build:lib && npm run build:hooks",
134
+ "prepublishOnly": "npm run build:lib && npm run build:hooks && npm run gen:pi-package-skills",
128
135
  "pretest": "npm run build:lib && npm run lint:skill-deps",
129
136
  "pretest:coverage": "npm run build:lib && npm run lint:skill-deps",
130
137
  "lint": "eslint . --cache --cache-location node_modules/.cache/eslint/ --max-warnings 0",
@@ -147,7 +154,7 @@
147
154
  "lint:test-file-count": "node scripts/lint-test-file-count.cjs",
148
155
  "lint:pr-checks": "node scripts/lint-pr-check-project-dir.cjs",
149
156
  "lint:changeset": "node scripts/changeset/lint.cjs",
150
- "lint:generated-sync": "node scripts/gen-capability-registry.cjs --check && node scripts/gen-loop-host-contract.cjs --check && node scripts/gen-capability-matrix.cjs --check && node scripts/sync-manifest-versions.cjs --check && node scripts/gen-inventory-manifest.cjs --check && node scripts/generate-package-identity.cjs --check && node scripts/gen-plugin-skills.cjs --check && node scripts/gen-registry.cjs --check && node scripts/gen-adr-index.cjs --check && node scripts/gen-features.cjs --check && node scripts/check-glossary-refs.cjs --check && node scripts/lint-compiled-artifact-sync.cjs --check && node scripts/gen-context-index.cjs --check && node scripts/gen-section-manifest.cjs --check && node scripts/gen-health-docs.cjs --check && node scripts/gen-state-md-docs.cjs --check && node scripts/gen-scripts-cli-exit.cjs --check && node scripts/gen-hooks-cli-exit.cjs --check && node scripts/gen-exit-code-registry.cjs --check && node scripts/gen-exit-code-docs.cjs --check && node scripts/gen-platform-conformance-tier.cjs --check && node scripts/gen-platform-conformance-tier.cjs --target macos --check",
157
+ "lint:generated-sync": "node scripts/gen-capability-registry.cjs --check && node scripts/gen-pi-package-skills.cjs --check && node scripts/gen-loop-host-contract.cjs --check && node scripts/gen-capability-matrix.cjs --check && node scripts/sync-manifest-versions.cjs --check && node scripts/gen-inventory-manifest.cjs --check && node scripts/generate-package-identity.cjs --check && node scripts/gen-plugin-skills.cjs --check && node scripts/gen-registry.cjs --check && node scripts/gen-adr-index.cjs --check && node scripts/gen-features.cjs --check && node scripts/check-glossary-refs.cjs --check && node scripts/lint-compiled-artifact-sync.cjs --check && node scripts/gen-context-index.cjs --check && node scripts/gen-section-manifest.cjs --check && node scripts/gen-health-docs.cjs --check && node scripts/gen-state-md-docs.cjs --check && node scripts/gen-scripts-cli-exit.cjs --check && node scripts/gen-hooks-cli-exit.cjs --check && node scripts/gen-exit-code-registry.cjs --check && node scripts/gen-exit-code-docs.cjs --check && node scripts/gen-platform-conformance-tier.cjs --check && node scripts/gen-platform-conformance-tier.cjs --target macos --check",
151
158
  "lint:docs": "node scripts/lint-docs-required.cjs",
152
159
  "lint:qa-smells": "node scripts/qa-smell-ratchet.cjs",
153
160
  "lint:legacy-name": "node scripts/lint-legacy-dir-name.cjs",