Skip to content

Implement pin-aware sync gate for Codex Conductor extension pinning#23

Draft
JonahBraun wants to merge 11 commits into
mainfrom
feature/conductor-pin-sync
Draft

Implement pin-aware sync gate for Codex Conductor extension pinning#23
JonahBraun wants to merge 11 commits into
mainfrom
feature/conductor-pin-sync

Conversation

@JonahBraun
Copy link
Copy Markdown
Contributor

Prevent sync when pinned extension versions don't match the running versions, and signal sync completion so the Codex Conductor can detect mid-session pin changes.

See architecture docs:

Changes

  • Signal syncCompletedAt timestamp to workspaceState after successful sync for Conductor observability
  • Add PinnedExtensions types, type guard, and mismatch detection utilities
  • Add checkPinnedExtensionsForSync() with 2-hour notification cooldown
  • Integrate pin checks into checkMetadataVersionsForSync() — pins override requiredExtensions
  • Add handleRemotePinValidation() in SCMManager to write remote pins to workspaceState and gate sync
  • Skip requiredExtensions version checks for pinned extension IDs
  • Style: consistent braces on single-line if statements

Test plan (current stable Codex — no Conductor)

  • Verify sync completes normally when no pins are present in metadata.json (regression — existing behavior unchanged)
  • Verify requiredExtensions version checks still work as before (regression)
  • Run pnpm test — new pinning.test.ts covers mismatch detection and notification behavior

Test plan (Codex beta with Conductor)

  • Pin a project via admin CLI and verify sync is blocked with "Reload Codex" notification when pinned version doesn't match running version
  • Confirm "Reload Codex" triggers window reload and Conductor switches to pinned profile
  • Verify pinned extensions bypass requiredExtensions ratchet checks
  • Confirm notification cooldown: auto-sync suppresses repeat notifications within 2 hours
  • Confirm syncCompletedAt is written to workspaceState after successful sync; Conductor re-evaluates pins
  • Confirm remotePinnedExtensions is written to workspaceState during remote metadata check, even when sync aborts before merge
  • Walk through Scenario 2 (sync receives new pin): Frontier writes remote pins, Conductor installs VSIX and switches profile
  • Walk through Scenario 3 (pin removed): Frontier clears remotePinnedExtensions, Conductor switches back to Default profile

Consistent brace style across extensionVersionChecker, SCMManager, and
versionChecker tests. Replace unsafe `as any` cast in
getCurrentExtensionVersion with narrower type assertion.
Write syncCompletedAt timestamp to workspaceState after a successful
sync. The Codex Conductor observes this via IStorageService to detect
mid-session changes to pinnedExtensions in metadata.json.
Foundation layer for extension pinning sync gate:
- PinnedExtensionEntry interface and PinnedExtensions type
- isPinnedExtensions() type guard for validation
- readLocalPinnedExtensions() reads pins from metadata.json
- findPinMismatches() compares running vs pinned versions
- extensionDisplayName() formats extension IDs for display
- Non-modal notification with "Reload Codex" action
Add checkPinnedExtensionsForSync() — checks pin mismatches, respects
the 2-hour notification cooldown, and returns { canSync, pinnedIds }.

Modify checkMetadataVersionsForSync() to call pin check first. Pinned
extensions override requiredExtensions: if an extension is pinned, its
requiredExtensions check is skipped so the pin version takes precedence.
Add handleRemotePinValidation() which writes remote pins to
workspaceState for Conductor visibility, then delegates to the shared
checkPinnedExtensionsForSync utility.

In syncChanges(), call remote pin validation before merge. Skip
requiredExtensions version checks for pinned extension IDs so the
pin version takes precedence over the ratchet.
Cover findPinMismatches and checkPinnedExtensionsForSync:
- Detects mismatched versions between running and pinned
- Returns empty when all versions match
- Shows non-modal notification and blocks sync on mismatch
- Reloads window when user clicks "Reload Codex"
@JonahBraun JonahBraun marked this pull request as draft March 25, 2026 23:32
…xtensions

Update version checking logic to:
- Use exact string equality for pinned version mismatches, ensuring prerelease
  affixes (like -pr123) are correctly enforced.
- Implement checkRequiredVersion to explicitly handle malformed required
  versions (fail-open) and missing installed versions (fail-closed).
- Ignore prerelease affixes during legacy requiredExtensions checks to
  prevent accidental blocking of development builds.
- Add integration tests for malformed metadata and missing version scenarios.
…reload UX

Frontier's "Reload Codex" button called workbench.action.reloadWindow which
bypasses the Conductor's authoritative reload (forceProfile), risking a
profile mismatch loop. The notification is now info-only. The Conductor
handles all reload UX via hostService.reload({ forceProfile }).
checkMetadataVersionsForSync was passing undefined for remotePins,
falling through to stale metadata.json on disk. After a conductor
profile switch, disk still has the old pin while storage has the new
one — blocking sync indefinitely.

The post-fetch check in handleRemotePinValidation also read from the
raw remote git blob, which is stale when a prior sync was blocked.
Both now use the conductor's getEffectivePinnedExtensions command,
which reads from IStorageService (always up-to-date). The post-fetch
path writes fresh remote pins to workspaceState first so the conductor
sees them before validating.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant