# Scratchpad — Extension Version Immutability and Version Viewer - Plan slug: `extension-version-immutability-and-viewer` - Created: `2026-02-10` ## What This Is Working notes for implementing extension publish duplicate handling, strict version immutability, and a read-only versions viewer. ## Decisions - (2026-02-10) Scope locked to three outcomes only: friendly duplicate-version errors, read-only versions viewer, strict immutability. - (2026-02-10) Strict immutability means version reuse is rejected even when content hash is identical. - (2026-02-10) Viewer is read-only in Extension Details; no invalidation/delete controls in this phase. ## Discoveries / Constraints - (2026-02-10) CLI and UI publish flows converge on `extFinalizeUpload` and `upsertVersionFromManifest`. - (2026-02-10) Current registry v2 behavior allows version reuse and can attach multiple bundle hashes to one version. - (2026-02-10) Existing UI only surfaces current installed version; there is no full versions list UI. - (2026-02-10) Runner static ext-ui path is hash-addressed and heavily cached; strict immutability reduces future reuse ambiguity but does not clean up historical rows. ## Commands / Runbooks - (2026-02-10) Scaffolded plan: `python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/scaffold_plan.py "Extension Version Immutability and Version Viewer" --slug extension-version-immutability-and-viewer` - (2026-02-10) Validate plan artifacts: `python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/validate_plan.py ee/docs/plans/2026-02-10-extension-version-immutability-and-viewer` ## Links / References - Publish flow entry: `sdk/alga-client-sdk/src/lib/publish.ts` - UI installer flow: `ee/server/src/components/settings/extensions/InstallerPanel.tsx` - Finalize action: `ee/server/src/lib/actions/extBundleActions.ts` - Registry v2 upsert semantics: `ee/server/src/lib/extensions/registry-v2.ts` - Versions list helper (existing internal): `ee/server/src/lib/extensions/registry-v2-repo-knex.ts` - Extension settings list/details UI: `ee/server/src/components/settings/extensions/Extensions.tsx`, `ee/server/src/components/settings/extensions/ExtensionDetails.tsx` - Runner ext-ui caching/validation: `ee/runner/src/http/ext_ui.rs`, `ee/runner/src/registry/client.rs`, `ee/runner/src/engine/loader.rs` ## Open Questions - Should viewer expose only latest hash per version, or also a legacy “multiple bundles” indicator for versions that already have >1 hash? - Exact duplicate-version error code string to align with existing API error naming conventions. ## Implementation Progress - (2026-02-10) Completed F001: Define duplicate-version API contract for finalize: HTTP 409 + stable error code + friendly message - (2026-02-10) Completed F002: Update registry-v2 finalize version upsert path to enforce strict immutability (reject existing version regardless of hash) - (2026-02-10) Completed F003: Remove/disable duplicate-version branch that currently appends a new bundle hash to an existing version - (2026-02-10) Completed F004: Map DB unique-constraint races for (registry_id, version) to the same friendly duplicate-version 409 response - (2026-02-10) Completed F005: Ensure ext-bundles/finalize route and server action return consistent duplicate-version error payloads for UI and CLI callers - (2026-02-10) Completed F006: Update installer/publish UI flow to surface duplicate-version message directly to users - (2026-02-10) Completed F007: Add registry query/action to list all versions for a registry extension in tenant context - (2026-02-10) Completed F008: Include in version list payload: version string, created/published timestamp, latest content hash metadata - (2026-02-10) Completed F009: Include installed marker in version list payload by joining tenant_extension_install.version_id - (2026-02-10) Completed F010: Handle legacy version rows with multiple bundles by deterministically selecting latest bundle metadata in viewer payload - (2026-02-10) Completed F011: Render read-only Versions section in Extension Details page - (2026-02-10) Completed F012: Versions table shows columns Version, Published, Content hash, Installed - (2026-02-10) Completed F013: Versions section includes explicit empty state when no versions exist - (2026-02-10) Completed F014: Enforce existing permission checks for version listing (extension read scope) - (2026-02-10) Completed F015: Add tests for duplicate-version friendly response in both direct duplicate and race-condition paths - (2026-02-10) Completed F016: Add tests for strict immutability guaranteeing no new bundle rows are created when version already exists - (2026-02-10) Completed F017: Add tests for versions viewer backend query covering installed marker and legacy multi-bundle versions - (2026-02-10) Completed F018: Add UI tests for Extension Details versions section rendering, sorting, and empty state - (2026-02-10) Completed T001: Finalize returns HTTP 409 with stable duplicate-version error code when manifest version already exists for extension - (2026-02-10) Completed T002: Duplicate-version 409 payload includes user-friendly message with conflicting version value - (2026-02-10) Completed T003: Finalize rejects duplicate version when incoming content hash is identical to existing bundle hash - (2026-02-10) Completed T004: Finalize rejects duplicate version when incoming content hash differs from existing bundle hash - (2026-02-10) Completed T005: Version-upsert path does not create a new extension_bundle row for duplicate version attempts - (2026-02-10) Completed T006: Concurrent finalize requests for same publisher/name/version map losing request to friendly duplicate-version 409 - (2026-02-10) Completed T007: DB unique violation for (registry_id, version) is normalized to duplicate-version code and message - (2026-02-10) Completed T008: Finalize HTTP route returns same duplicate-version payload shape as server action result - (2026-02-10) Completed T009: Installer UI shows duplicate-version friendly message from finalize response without generic fallback text - (2026-02-10) Completed T010: CLI publish surfaces duplicate-version response message from finalize path - (2026-02-10) Completed T011: Version list backend returns all versions for given registry extension sorted newest first - (2026-02-10) Completed T012: Version list backend includes version string and publish timestamp fields for each row - (2026-02-10) Completed T013: Version list backend includes deterministic latest content hash metadata per version - (2026-02-10) Completed T014: Version list marks installed=true only for row matching tenant_extension_install.version_id - (2026-02-10) Completed T015: Version list handles extension with no install row by returning all rows installed=false - (2026-02-10) Completed T016: Legacy data case: one version with multiple bundle rows still returns one deterministic viewer row - (2026-02-10) Completed T017: Extension Details page renders Versions section heading and table when version data exists - (2026-02-10) Completed T018: Versions table renders expected columns Version, Published, Content hash, Installed - (2026-02-10) Completed T019: Versions table rows are sorted newest-first by publish timestamp - (2026-02-10) Completed T020: Versions section renders empty-state copy when backend returns no rows - (2026-02-10) Completed T021: Unauthorized caller cannot access version-list backend action/route without extension read permission - (2026-02-10) Completed T022: Authorized extension-read caller can access version-list backend action/route - (2026-02-10) Completed T023: Strict immutability leaves existing historical duplicate-bundle data untouched (no destructive migration) - (2026-02-10) Completed T024: Finalize happy path still succeeds for brand-new version values