Closes#3566
Codex itself marks codex_hooks as a legacy_key in
codex-rs/features/src/legacy.rs. The canonical current Codex feature flag
under [features] is hooks. The GSD installer was still writing codex_hooks
on every fresh install / reinstall, leaving deprecated config behind on
Codex CLI >= 0.130.0.
Introduces a canonical/legacy split in bin/install.js:
CODEX_HOOKS_FEATURE_KEY = 'hooks'
CODEX_HOOKS_FEATURE_LEGACY_KEYS = ['codex_hooks']
isCodexHooksFeatureKey(key) // recognizes canonical OR any legacy alias
Threaded through:
- ensureCodexHooksFeature(): emits canonical hooks; recognizes legacy
codex_hooks; migrates legacy -> canonical in section, root-dotted, and
block-fallback insertion paths.
- hasEnabledCodexHooksFeature(): accepts either canonical or legacy.
- stripCodexHooksFeatureAssignments(): strips either canonical or legacy
during uninstall when GSD owns the line.
- rewriteTomlKeyLines(): now always uses the caller-supplied key instead
of the parsed-record keyRaw. The old `match.keyRaw || key` fallback was
the proximate reason the migration silently no-op'd — callers asking
to rewrite a section line to `hooks` got back the legacy `codex_hooks`
line because the parsed record carried keyRaw="codex_hooks".
The GSD_CODEX_HOOKS_OWNERSHIP_PREFIX audit-marker string is intentionally
unchanged so existing installs' ownership lines continue to round-trip.
Tests:
- New tests/bug-3566-codex-hooks-feature-canonical-key.test.cjs (6 cases):
fresh install writes hooks; section-form legacy migrated; root-dotted
legacy migrated; user-owned hooks preserved; uninstall removes
GSD-owned canonical; uninstall preserves user-owned hooks.
- Pre-existing legacy-pinning behaviour-change updates land in this PR
via the rewriteTomlKeyLines + ensureCodexHooksFeature edits; the
bug-2760-codex-install-defensive and bug-3427-3433 suites pass on the
new shape without further test edits because they assert behaviour
(not the literal key name).
Docs:
- docs/ARCHITECTURE.md row for Codex notes [features].hooks (canonical,
legacy codex_hooks recognized and migrated forward).
- docs/installer-migrations.md row updated to reflect canonical key
and the new Codex 0.130.0 features.hooks compatibility sentinel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This directory holds per-PR CHANGELOG fragments. Every PR with user-facing changes drops one (or more) <random-name>.md files here describing its CHANGELOG entry. Fragments are consolidated into the top-level CHANGELOG.md at release time.
Why
Two PRs that both edit the ### Fixed block of CHANGELOG.md always conflict on merge — git can't pick a serialization order without human input. Two PRs that each add a fresh .changeset/<unique-name>.md never conflict because they don't share lines.
Reads every fragment, groups bullets by type:, replaces ## [Unreleased] with a new ## [vX.Y.Z] - YYYY-MM-DD block, opens a fresh ## [Unreleased] above, deletes consumed fragments. Idempotent.