* refactor(#1734): extract install engine from bin/install.js (ADR-1239 Phase B deep move)
Relocate the runtime-artifact install cluster out of the 12,490-line
bin/install.js into a dedicated src/install-engine.cts -> install-engine.cjs:
installRuntimeArtifacts, uninstallRuntimeArtifacts, installOpencodeFamilySkills,
and their cluster helpers (_copyStaged, snapshot/restore, legacy migration,
GSD-entry pruning, preserve/restoreUserArtifacts, OpenCode-family converters,
USER_OWNED_ARTIFACTS).
- bin/install.js imports the engine and re-exports the moved symbols for
back-compat; getCommitAttribution STAYS in install.js (impure config I/O +
argv explicitConfigDir global) and is injected via a resolveAttribution param.
- 17 test files migrated to import the moved symbols from the engine.
- Bookkeeping: eslint built-artifact ignore, .gitignore, INVENTORY manifest+row,
CONTEXT.md Install Engine Module glossary seam.
Behaviour-preserving: install output is byte-identical for all 16 runtimes
(golden-parity harness #1730) — the only delta is the new install-engine.cjs
file shipping in the installed gsd-core/bin/lib/ tree.
Closes#1734
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#1734): backfill changeset PR number (#1735)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: review-bot <review-bot@gsd>
* feat(#1724): complete install write-confinement (copyWithPathReplacement, installCodexConfig)
ADR-1239 Phase B (parent #1679). PR #1706 (2a) confined the layout-driven
plan path and _copyStaged's inline guard; this completes the destSubpath
write-confinement acceptance criterion for the two remaining write sites
and canonicalizes _copyStaged.
- copyWithPathReplacement: new required confinementRoot param; a fail-closed
gate (assertDestWithinConfigHome + hasExistingSymlinkBetween) runs BEFORE
the rmSync/mkdirSync; root threaded through recursion + all 4 call sites
(stageRoot for pristine staging, targetDir for the 3 install sites); writes
go through the validated absolute path. Exported for behavioral testing.
- installCodexConfig: confines config.toml, agents/, and per-agent
agents/<name>.toml (name from agent frontmatter) via the canonical gate +
symlink-escape guard (parity with the other two functions).
- _copyStaged: fail-closed when configDir omitted (all callers pass it);
delegates strict-subpath to the canonical gate, keeps its symlink guard,
writes through the validated absolute path.
Reuses the existing assertDestWithinConfigHome (handles absolute dests via
path.resolve) and hasExistingSymlinkBetween — no new module. Behavioral
regression tests (escape/dest==root/fail-closed/symlink/name-injection),
red-first proven; cross-platform symlink tests use t.skip not bare return.
Closes#1724
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#1724): backfill changeset PR number (#1725)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#1679): confine install writes within configHome
ADR-1239 Phase B write-confinement: a pure assertDestWithinConfigHome(configDir, destSubpath) rejects a destSubpath that escapes configHome (path traversal / NUL byte) at plan-build time on BOTH the install and uninstall plan paths; surface.applySurface and installOpencodeFamilySkills route through it, and _copyStaged carries a defense-in-depth containment check. Security-load-bearing for the Phase C third-party-descriptor loader.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#1704): add changeset for destSubpath write-confinement
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#1704): fix windows path-portability in confinement test
The N1 'accepts a true child subpath' assertion compared against path.join (no drive resolution) while the helper uses path.resolve — on Windows that mismatches the C: drive prefix. Compute the expected via path.resolve to mirror the helper. Windows-CI-only failure (local gsd-test is Mac+Linux).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>