fix(3566): emit canonical [features].hooks; recognize legacy codex_hooks as alias

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 commit is contained in:
Tom Boucher
2026-05-15 14:03:32 -04:00
parent 1a4e6df6b3
commit 55b3f45ad0
5 changed files with 268 additions and 14 deletions

View File

@@ -0,0 +1,6 @@
---
type: Fixed
pr: 0
---
**Codex installer now emits canonical `[features].hooks` (not legacy `codex_hooks`)** — Codex's own source marks `codex_hooks` as a `legacy_key` ([codex-rs/features/src/legacy.rs](https://github.com/openai/codex/blob/main/codex-rs/features/src/legacy.rs)). The GSD installer was writing the deprecated key on every fresh install and reinstall, leaving deprecated config behind on Codex CLI ≥ 0.130.0. The installer now writes the canonical `[features].hooks = true` (section, root-dotted, and block-fallback forms), recognizes legacy `codex_hooks` as equivalent during reinstall, migrates it forward in-place, and strips either form on uninstall. User-owned preexisting `[features].hooks = true` lines are preserved untouched (per the #2760 defensive principle). Closes #3566.