diff --git a/docs/internal/project/v2-reset-execution-record.md b/docs/internal/project/v2-reset-execution-record.md index 830a9fe..78988ca 100644 --- a/docs/internal/project/v2-reset-execution-record.md +++ b/docs/internal/project/v2-reset-execution-record.md @@ -1,7 +1,7 @@ --- owner: Project Owner + Work Manager status: Active project record -last-updated: 2026-06-29 +last-updated: 2026-07-02 canonical-source: docs/internal/project/v2-reset-execution-record.md project-issue: 209 --- @@ -34,7 +34,9 @@ The product should let a user: 3. sync all or selected settings in the correct direction; 4. handle conflicts and missing apps/settings safely; 5. use a settings storage folder with or without Git; -6. use bundled recipes first and later optional recipe catalogs/taps; +6. use the preconfigured official catalog, shown as `dotfiles-manager/official` + with catalog state shown by version and last-updated time, then later + optional catalog updates and additional remote catalogs/taps; 7. set up a new computer by installing apps first and then applying settings. ## Current accepted product constraints @@ -79,28 +81,41 @@ operationally. Current active gate: -- #228 has draft implementation PR #255, but #228 is in recovery gate status, - not normal ready-to-finish implementation. -- #228 recovery is required because implementation began without a sufficiently - explicit Project Owner implementation-start gate for the final CLI UX package - and without a recorded decision that static storyboard/transcript evidence was - sufficient instead of a runnable/replayable CLI mock. -- PR #255 also has a separate CI coverage failure: `coverage-aggregation` failed - at `88.9% < 90%`; `final-required-check` failed because coverage failed. - -No-go actions until #228 recovery is recorded: - -- Do not fix #255 coverage or continue #228 implementation as if the UX gate were - already satisfied. -- Do not mark PR #255 ready, merge it, request #228 acceptance, or close #228. -- Do not start #229 remote catalog implementation using #228 as accepted. - -Allowed #228 recovery actions after #256: - -1. create a runnable/replayable CLI mock and compare PR #255 against it; -2. record Project Owner waiver/not-applicable decision for the mock and accept - the existing storyboard as sufficient design evidence for #228; or -3. recontract #228 before implementation continues. +- #228 is being recontracted after Project Owner review of the `list` surface. +- Project Owner decision on 2026-06-30: remove internal pseudo-app targets from + normal discovery and remove local catalog lifecycle from the normal #228 path. +- Project Owner managed change on 2026-07-02 after UX review: the #228 public/mock + surface should not demonstrate internal pseudo-app names or unavailable catalog + lifecycle commands. Both `catalog update` and `catalog add` are omitted from + #228 public/mock output until those behaviors are implemented by #229. +- #228 should now target the preconfigured official-catalog discovery baseline + without presenting the catalog as baked into the app. `catalog list` should + identify the official catalog as `dotfiles-manager/official`, define catalogs + as support-definition sources rather than settings storage, and show concise + state such as catalog version and updated time. First-run download, + official-catalog update, and additional remote catalogs are implemented by + #229 unless separately recontracted. +- Draft PR #255 implemented the previous local-catalog lifecycle scope and is now + superseded for #228 acceptance purposes. Its coverage failure is no longer the + next actionable blocker. + +No-go actions until #228 is recontracted and a new implementation-start gate is +recorded: + +- Do not fix #255 coverage or continue #255 implementation as if it still matched + #228. +- Do not mark PR #255 ready, merge it, request #228 acceptance, or close #228 + based on #255. +- Do not start #229 remote catalog implementation using old #228 superseded + behavior as accepted. + +Allowed #228 recovery actions: + +1. update #228 issue contract and durable design evidence for + official-catalog discovery with version/last-updated catalog state; +2. mark PR #255 as superseded/blocked by the recontract decision; +3. after the recontract is reviewed and accepted, start a fresh implementation + from current `main` against the new #228 contract. ## Standards maturity snapshot @@ -136,13 +151,13 @@ As of 2026-06-29: | #211 | Parent delivery area | Tier 2 overall | Complete | Accepted and closed 2026-06-26 after #221-#225, #212, and #213/#226 completed. | | #212 | Product-scope cleanup gate | Tier 1 | Complete | PR #243 removed public backup/restore workflow; issue closed 2026-06-25. | | #213 | Product-scope cleanup gate | Tier 1 | Complete | PR #247 removed v1 migration from the active v2 roadmap/user-facing happy path and closed 2026-06-26. | -| #214 | Parent delivery area | Discovery then Tier 2 for remote writes | Open parent | #227 completed the trust/origin model; use #228 before #229 where built-in/local discovery behavior is needed. Remote writes still require #229 write-gate implementation. | +| #214 | Parent delivery area | Discovery then Tier 2 for remote writes | Open parent | #227 completed the trust/origin model; use #228 for the official-catalog discovery baseline before #229 official-catalog download/update and remote catalog add/write-gate implementation. | | #215 | Parent delivery area | Tier 1/Tier 2 | Open parent | Use #230 before #231; Homebrew Bundle remains an example, not a dependency. | | #216 | Documentation delivery | Tier 1 | Open | Production docs depend on accepted behavior/examples from remaining gates. | | #226 | Delivery-design cleanup | Tier 1 | Complete | PR #247 hides retained legacy v1 commands from root help, labels direct help as legacy compatibility, and closed 2026-06-26. | | #227 | Discovery/design | Discovery/Tier 1 | Complete | PR #250 merged; issue accepted and closed 2026-06-26. | -| #228 | Delivery | Tier 1 | Recovery gate / draft PR #255 | Built-in/local catalog discovery implementation exists in draft PR #255, but process recovery is required before code/coverage continuation: choose runnable/replayable mock, explicit waiver, or recontract. | -| #229 | Delivery | Tier 2 | Open child of #214 | Implement remote catalog management with write gates against the accepted #227 model. | +| #228 | Delivery | Tier 1 | Recontracting | Project Owner removed internal pseudo-app targets, catalog lifecycle command placeholders, and user-facing built-in catalog wording from the normal path. Draft PR #255 is superseded by this direction; update contract/design evidence before fresh implementation. | +| #229 | Delivery | Tier 2 | Open child of #214 | Implement official-catalog update and additional remote catalog management with write gates against the accepted #227 model; remote catalogs are the intended normal extension path beyond the current official catalog. | | #230 | UX/design | Tier 1 | Open child of #215 | Specify new-computer UX and output. | | #231 | Delivery | Tier 2 | Open child of #215 | Implement apply-from-storage flow after sync model/UX is accepted. | | #238 | Cleanup / enabler | Tier 1 | Complete | PR #239 reconciled #209 and this execution record with live state; no runtime changes. | @@ -169,10 +184,10 @@ child-scope completion, not automatic parent closure. | #212 | Closed / Done | Product-scope gate | PR #243 merged 2026-06-25; issue closed | Checked | Public backup/restore workflow removed; lower-level internal recovery mechanics remain implementation details. | | #213 | Closed / Done | Product-scope gate | PR #247 merged; issue closed 2026-06-26 | Checked | No further action unless future v1 migration/deprecation work is explicitly reintroduced. | | #226 | Closed / Done | Child of #213 | PR #247 merged; issue closed 2026-06-26 | Checked under #213 | Retained legacy v1 commands are hidden from normal help and separated from v2 acceptance. | -| #214 | Open / Todo | Catalog parent | #227 closed; #228/#229 open | Keep parent open with children | Built-in/local discovery and remote write-gate implementation remain. | +| #214 | Open / Todo | Catalog parent | #227 closed; #228/#229 open | Keep parent open with children | Official-catalog discovery baseline (#228) and remote catalog update/add/write-gate implementation (#229) remain. | | #227 | Closed / Done | Catalog child | PR #250 merged; issue closed after Project Owner acceptance | Checked under #214 | Trust/origin/write-authority model complete for downstream implementation. | -| #228 | Open / draft PR | Catalog child | Draft PR #255 exists; CI coverage currently fails at 88.9% < 90%; process recovery gate identified | Recovery under #214 | Before code/coverage continuation, recover implementation-start/public-surface gate by mock, waiver, or recontract. | -| #229 | Open / Todo | Catalog child | Live issue open | Open under #214 | Implement remote catalog management with write gates against the accepted #227 model. | +| #228 | Open / recontracting | Catalog child | Draft PR #255 exists but is superseded by the 2026-06-30 and 2026-07-02 decisions to remove internal pseudo-app targets and catalog lifecycle placeholders from the normal path | Recontract under #214 | Update #228 contract/storyboard/mock, mark #255 superseded, then implement fresh from current `main`. | +| #229 | Open / Todo | Catalog child | Live issue open | Open under #214 | Implement official-catalog update and additional remote catalog management with write gates; this is the normal extension path beyond the current official catalog after local catalogs were removed from #228. | | #215 | Open / Todo | Bootstrap parent | Children #230-#231 open | Keep parent open with children | Bootstrap must reuse sync model; Homebrew Bundle is example only. | | #230 | Open / Todo | Bootstrap child | Live issue open | Open under #215 | Specify new-computer UX/output before implementation. | | #231 | Open / Todo | Bootstrap child | Live issue open | Open under #215 | Implement apply-from-storage after accepted UX/model. | @@ -217,7 +232,7 @@ Before starting any work item: | Branches from closed PRs may be reused accidentally | Enforce branch rule and start from `main` | Executor | Active | | Agents may cross lifecycle phases from broad prompts or artifact completion | Use lifecycle gate/passport readback, implementation-start gate, and explicit no-go actions from AGENTS/tailoring/templates | Work Manager / Executor | Mitigated / monitoring | | Meaningful CLI UX can be implemented from static transcripts without owner-approved runnable/replayable evidence | Default to runnable/replayable CLI usage evidence or record a Project Owner waiver/not-applicable decision | Work Manager / Project Owner | Mitigated / monitoring | -| #228 implementation may continue as if PR #255 were only a CI issue | Record #228 recovery status and require mock/waiver/recontract before coverage cleanup or merge | Work Manager | Active | +| #228 implementation may continue from superseded PR #255 | Mark #255 superseded, update #228 contract/design evidence, and require fresh implementation-start gate from current `main` | Work Manager | Active | ## Evidence index @@ -239,8 +254,11 @@ Before starting any work item: | Status/diff/sync parent gate accepted | Issue #211 closure comment after Project Owner selected option 1 to accept and close #211 | 2026-06-26 | Parent acceptance only; the combined v2 product remains open until catalog, bootstrap, and production documentation gates are complete or deferred. | | Catalog trust/origin model accepted | PR #250 squash merge `696e235d82856295c04c31941ca31e63e1935e67`; issue #227 closure record after Project Owner accepted closure | 2026-06-26 | Specifies design and handoff requirements only; #228/#229 still need implementation and runtime verification. | | External Project Execution Standard updated with lifecycle gates | `/Users/shpoont/Work/shpoont/project-execution-standards/project-execution-standard.md`, last-updated 2026-06-29 | 2026-06-29 | Local repo adaptation tracked by #256; external standard is linked, not copied. | -| #228 recovery gate identified | Draft PR #255 exists; CI coverage failure observed; Project Owner discussion identified missing explicit implementation-start/public-surface mock gate | 2026-06-29 | Recovery decision still pending: runnable/replayable mock, waiver, or recontract. | +| #228 recovery gate identified | Draft PR #255 exists; CI coverage failure observed; Project Owner discussion identified missing explicit implementation-start/public-surface mock gate | 2026-06-29 | Superseded by 2026-06-30 recontract decision; #255 is not the implementation target. | | Local lifecycle-gate process hardening completed | PR #257 / issue #256 adapted local AGENTS, tailoring, templates, and execution record to the updated standard | 2026-06-29 | Process-only change; does not validate #228 product behavior. | +| #228 recontract decision: remove internal pseudo-app targets, catalog lifecycle placeholders, and built-in catalog wording | Project Owner decided normal discovery should show real apps/tools only through the official catalog, with catalog version/updated state in `catalog list`; unavailable `catalog update` and `catalog add` output is omitted until #229 implements those behaviors | 2026-07-02 | Requires updated #228 contract/design evidence and fresh implementation-start gate before runtime work. | +| #228 GitHub contract recontracted | Issue #228 title/body updated, #228 managed-change comment recorded, PR #255 title/body/comment marked superseded, #229 received context comment, and #209 source-of-truth body updated | 2026-06-30 | Tracker update only; runtime implementation and #228 acceptance remain pending. | +| #228 official catalog refinement | Project Owner approved replacing user-facing built-in catalog wording with a preconfigured official catalog and later clarified that it must not be presented as included/baked into the app | 2026-06-30 | Design/tracker refinement only; #228 must not implement first-run download or update behavior until #229 behavior exists or the issue is recontracted. | ## Phase 1 acceptance state @@ -292,16 +310,22 @@ Phase 3 recontract/resequence: ## Current next gate -#256 local process hardening is complete. The current gate is #228 recovery: - -- create/review a runnable or replayable CLI mock and compare PR #255 against it; -- or record a Project Owner waiver/not-applicable decision that accepts the - existing storyboard/static transcript evidence for #228; -- or recontract #228 before implementation continues. - -Only after #228 recovery is recorded should the executor fix PR #255 coverage, -rerun validation, request review/acceptance, mark PR #255 ready, merge, or close -#228. +#228 is finishing recontract/design-evidence review after Project Owner and UX +review of the `list`/discovery surface: + +- remove internal/generated pseudo-app targets from normal discovery; +- omit unavailable catalog lifecycle commands from the #228 public/mock surface; +- define catalogs as app/tool support sources, not settings storage; +- show `dotfiles-manager/official` as active for discovery with concise catalog + version/updated metadata; +- use official-catalog update and additional remote catalogs as the future + extension path in #229; +- keep PR #255 superseded instead of fixing its coverage; +- merge PR #258 as the durable storyboard/mock evidence package before fresh + runtime implementation. + +Only after PR #258 is merged and an implementation-start gate is recorded should +an executor start fresh #228 runtime implementation from current `main`. Paired follow-up: diff --git a/docs/internal/scope/product-concept-v2.md b/docs/internal/scope/product-concept-v2.md index 146bfcc..1050076 100644 --- a/docs/internal/scope/product-concept-v2.md +++ b/docs/internal/scope/product-concept-v2.md @@ -37,7 +37,8 @@ The product should make it easy to: 3. sync all apps, one app, or selected settings/resources; 4. handle conflicts and missing apps/settings safely; 5. use the settings folder with or without Git; -6. use bundled recipes first and later optional recipe catalogs/taps; +6. use the preconfigured official catalog first through its bundled snapshot, + then later optional catalog updates and additional remote catalogs/taps; 7. set up a new computer by installing apps first and then applying settings. ## Public mental model @@ -78,7 +79,7 @@ Normal users should only need these nouns: | Diff | A readable or honest metadata-level explanation of differences. | | Sync | The action that copies selected settings in a chosen safe direction. | | Conflict | A case where both sides changed or the safe direction is ambiguous. | -| Catalog | A source of recipes. Bundled recipes come first; remote catalogs come later. | +| Catalog | A source of recipes. The official catalog snapshot comes first; catalog updates and additional remote catalogs come later. | Advanced/internal docs may also use: @@ -226,9 +227,10 @@ Native import/export is a recipe-declared capability or reviewed driver behavior not arbitrary user scripting. Opaque exports must be honest: show hash/metadata changes and limitations rather than fake semantic diffs. -Bundled recipes are the default path for common apps. Remote catalogs/taps are a -future expansion area and must wait for explicit origin, trust, update, disable, -and write-authority rules. +The preconfigured official catalog snapshot is the default path for common apps. +Catalog updates and additional remote catalogs/taps are a future expansion area +and must wait for explicit origin, trust, update, disable, and write-authority +rules. ## Explicit non-goals for active v2 diff --git a/docs/internal/specs/v2/00-vocabulary.md b/docs/internal/specs/v2/00-vocabulary.md index a34b5fb..99389fd 100644 --- a/docs/internal/specs/v2/00-vocabulary.md +++ b/docs/internal/specs/v2/00-vocabulary.md @@ -70,7 +70,6 @@ Examples: - `git`; - `starship`; -- `custom.files` for advanced/custom file management; - `example-tool` for illustrative-only examples. ### Setting @@ -190,14 +189,17 @@ is otherwise ambiguous. Conflicts require an explicit user decision or refusal. A source of recipes. -The bundled/default catalog is the source for common built-in app/tool support. -Optional local or remote catalogs are future expansion points. Catalogs are a -public noun only when users manage recipe sources; they are not a prerequisite -for ordinary bundled-app sync. +The dotfiles-manager official catalog is preconfigured by the app and is the +default source for common app/tool support. The installed app includes a bundled +snapshot of that official catalog, so first-run discovery works offline. +Optional catalog updates and additional remote catalogs are future expansion +points. Catalogs are a public noun only when users manage recipe sources; they +are not a prerequisite for ordinary official-catalog app sync. -Remote catalogs require the explicit origin, trust, update, disable/remove, -and write-authority rules in `17-catalog-trust-origin-model.md` before recipes -from them can write live settings. +Remote catalog update/add/remove behavior requires the explicit origin, trust, +update, disable/remove, and write-authority rules in +`17-catalog-trust-origin-model.md` before recipes from updated or additional +remote catalog data can write live settings. ## Scopes and profile language @@ -250,11 +252,12 @@ sensitivity policy, and optional native import/export capability. ### Catalog metadata -A source of recipes. The bundled/default catalog comes first. Remote catalog -runtime support is future work and must follow the origin, trust, update, -disable/remove, and write-authority model in -`17-catalog-trust-origin-model.md` before remote recipes can write live -settings. +A source of recipes. The dotfiles-manager official catalog comes first and is +available through the bundled snapshot included with the installed app. Remote +catalog runtime support is future work and must follow the origin, trust, +update, disable/remove, and write-authority model in +`17-catalog-trust-origin-model.md` before updated official data or additional +remote recipes can write live settings. ### Named location diff --git a/docs/internal/specs/v2/17-catalog-trust-origin-model.md b/docs/internal/specs/v2/17-catalog-trust-origin-model.md index 89acc16..2779e63 100644 --- a/docs/internal/specs/v2/17-catalog-trust-origin-model.md +++ b/docs/internal/specs/v2/17-catalog-trust-origin-model.md @@ -3,7 +3,7 @@ owner: Product + Core Engineering location: docs/internal/specs/v2/17-catalog-trust-origin-model.md document-type: v2-active-behavior-spec status: Active behavior spec -last-updated: 2026-06-26 +last-updated: 2026-06-30 canonical-source: docs/internal/specs/v2/17-catalog-trust-origin-model.md source-issue: 227 authority: Authoritative v2 catalog/tap trust, recipe origin, update, disabling/removal, provenance, collision, and write-authority model for #214/#227; #228 and #229 implementation must conform to this model. @@ -14,10 +14,12 @@ authority: Authoritative v2 catalog/tap trust, recipe origin, update, disabling/ ## Purpose This document defines the v2 model for recipe catalogs/taps before remote -catalog implementation starts. It accepts the existing bundled registry as the -first normalized catalog seed, defines how local and future remote recipe -sources are represented, and sets the trust/write-authority rules that must gate -any recipe that can change live settings. +catalog implementation starts. A 2026-06-30 managed change replaces the +previous default-catalog wording with a preconfigured dotfiles-manager official +catalog that ships with a bundled snapshot for first-run/offline discovery. It +also defines how future remote recipe sources are represented and sets the +trust/write-authority rules that must gate any recipe that can change live +settings. The central rule is: @@ -34,7 +36,7 @@ by itself to let recipes from that catalog write live app settings. In scope for issue #227: - catalog/source identity and state; -- bundled/default catalog definition; +- official catalog bundled-snapshot definition; - local and remote catalog behavior at model level; - effective recipe origin fields visible before writes; - trust, provenance, update, disabling/removal, collision, and write-authority @@ -58,13 +60,13 @@ Use four concepts and keep them distinct. | Concept | Meaning | Authority | | --- | --- | --- | | Catalog | Data that lists available recipe candidates. | No write authority by itself. | -| Catalog source / tap | A locally configured origin from which a catalog is discovered, such as bundled, local, or remote. | May allow discovery when enabled. | +| Catalog source / tap | A manager-known origin from which a catalog is discovered, such as the preconfigured official catalog or an additional remote catalog. | May allow discovery when enabled. | | Recipe origin | Manager-resolved provenance for one recipe candidate. | Must be visible before writes. | -| Write grant | Local-only authority allowing a specific recipe origin to write live settings. | Required for non-bundled writes. | +| Write grant | Local-only authority allowing a specific recipe origin to write live settings. | Required for writes from updated official data or additional remote catalogs. | -Normal users should not need the word `tap` for the bundled happy path. Public -copy should prefer `recipe source` and `permission to change live settings`. -Advanced/JSON output may expose catalog/source/tap fields. +Normal users should not need the word `tap` for the official-catalog happy path. +Public copy should prefer `catalog` and `permission to change live settings`. +Advanced/JSON output may expose recipe/source/tap fields. ## Source-of-truth and storage rules @@ -88,19 +90,19 @@ is a #228/#229 implementation detail, but the model requires these fields. | Field | Required | Meaning | | --- | --- | --- | -| `sourceId` | yes | Stable local identifier, for example `bundled`, `local`, or `remote:`. | -| `sourceKind` | yes | One of `bundled`, `local`, or `remote`. Unknown kinds fail closed. | +| `sourceId` | yes | Stable local identifier, for example `official` or `remote:`. | +| `sourceKind` | yes | One of `official` or `remote` for normal v2 catalog sources. `local` is historical/advanced-only unless a future issue reintroduces it. Unknown kinds fail closed. | | `catalogId` | yes | Stable catalog identity independent from display name. | | `displayName` | yes | User-facing source/catalog name. Not a security identifier. | -| `originUri` | yes | Release, file, or remote origin URI. | +| `originUri` | yes | Remote origin URI for the catalog; the official catalog may also have a release-bundled snapshot URI. | | `status` | yes | `candidate`, `enabled`, `disabled`, `blocked`, or `removed`. | -| `sourceAcceptance` | yes | Source-level discovery acceptance: `release-accepted`, `user-accepted`, `review-required`, `rejected`, or `invalid`. | -| `integrityState` | yes | `not-required`, `valid`, `missing`, `invalid`, `expired`, `revoked`, or `unsupported`. | -| `writeDefault` | yes | `allowed` or `denied`. Bundled is allowed; local and remote default denied. | +| `sourceAcceptance` | yes | Source-level discovery acceptance: `release-accepted`, `user-accepted`, `review-required`, `rejected`, or `invalid`. The bundled official snapshot is `release-accepted`. | +| `integrityState` | yes | `bundled-snapshot`, `valid`, `missing`, `invalid`, `expired`, `revoked`, or `unsupported`. | +| `writeDefault` | yes | `allowed` or `denied`. The release-accepted official bundled snapshot is allowed; additional or updated remote data defaults denied until write authority is explicit. | | `pinnedIdentity` | conditional | Release identity, canonical local path, or remote signing identity. Required when relevant to provenance/trust. | | `lastSeenManifestDigest` | conditional | Digest of the latest validated manifest. Required for remote once fetched. | | `lastFetchedAt` | conditional | Fetch timestamp for remote sources. | -| `updatePolicy` | yes | `release-only`, `manual`, or another explicitly accepted policy. | +| `updatePolicy` | yes | `bundled-snapshot`, `manual`, or another explicitly accepted policy. #229 owns remote update behavior. | | `disabledReason` / `blockedReason` | conditional | Metadata-only reason when the source is disabled or blocked. | A recipe or remote manifest must not be allowed to self-declare its effective @@ -115,42 +117,52 @@ is deliberately separate from recipe-level `reviewStatus` and write grants. A user can accept a remote catalog for discovery while every recipe from that catalog still remains blocked for live writes. -## Bundled/default catalog +## Official catalog and bundled snapshot -The existing bundled registry is accepted as the candidate seed for the v2 -bundled catalog. It should be normalized as: +The default source is the dotfiles-manager official catalog. It is conceptually a +preconfigured remote catalog, but #228 uses only the bundled snapshot shipped +with the installed app so first-run discovery works offline and does not require +network access. It should be normalized as: ```yaml -sourceId: bundled -sourceKind: bundled -catalogId: org.dotfiles-manager.bundled -displayName: dotfiles-manager built-in recipes -originUri: app-release://dotfiles-manager//recipes +sourceId: official +sourceKind: official +catalogId: org.dotfiles-manager.official +displayName: dotfiles-manager official catalog +originUri: dotfiles-manager-official-catalog://stable +snapshotUri: app-release://dotfiles-manager//official-catalog status: enabled sourceAcceptance: release-accepted -integrityState: not-required +integrityState: bundled-snapshot writeDefault: allowed -updatePolicy: release-only +updatePolicy: bundled-snapshot +removable: false ``` -Current bundled fields remain valid as the runtime seed: +Current bundled recipe files may remain the runtime seed, but user-facing output +and new #228/#229 contracts should call this the official catalog snapshot: ```text -source=bundled -recipeRef=recipe://bundled/ +catalog=official +recipeRef=recipe://official/ trustStatus=trusted ``` -The normative rule is that bundled trust comes from the dotfiles-manager release -process, not from recipe data claiming to be trusted. Bundled recipes still must -pass schema, capability, lifecycle, redaction, path, and command-boundary -validation before writes. +The normative rule is that trust in the bundled official snapshot comes from the +dotfiles-manager release process, not from recipe data claiming to be trusted. +Official snapshot recipes still must pass schema, capability, lifecycle, +redaction, path, and command-boundary validation before writes. -Bundled recipe updates happen only through manager releases. Installing or -updating the manager is the user's trust event for bundled recipes. +Updating the official catalog from its remote origin is #229 scope. #228 must not +fetch, update, or replace official catalog data from the network. ## Local catalog source +2026-06-30 managed change: local catalog lifecycle is not part of the normal v2 +user path and is removed from #228. The model below is retained only as +historical/advanced implementation context until a future issue explicitly +reintroduces local catalog authoring or debugging support. + Local recipes in the settings folder are modeled as a local catalog source: ```yaml @@ -282,7 +294,7 @@ Required effective fields: | `targetId` | App/tool being managed. | | `displayName` | User-facing target name. | | `recipeRef` | Stable resolved recipe reference. | -| `sourceKind` | `bundled`, `local`, or `remote`. | +| `sourceKind` | `official` or `remote` for the normal path. `local` is historical/advanced-only unless reintroduced. | | `sourceId` | Local catalog source identifier. | | `catalogId` | Catalog identity. | | `sourceDisplayName` | Human-readable recipe source. | @@ -297,14 +309,14 @@ Required effective fields: | `capability` | Declared capability. | | `declaredReadScopes` | User-readable summary of reads. | | `declaredWriteScopes` | User-readable summary of writes. | -| `selectedBy` | `bundled-default`, `user-selected`, `pinned`, or `collision-resolution`. | +| `selectedBy` | `official-default`, `user-selected`, `pinned`, or `collision-resolution`. | Current compatibility fields map into this object: | Current field | Effective origin mapping | | --- | --- | -| `source=bundled` | `sourceKind=bundled`, `sourceId=bundled`. | -| `recipeRef=recipe://bundled/` | Bundled recipe reference. | +| legacy `source=bundled` | Compatibility input that maps to `sourceKind=official`, `sourceId=official`. | +| legacy `recipeRef=recipe://bundled/` | Compatibility input that maps to `recipe://official/`. | | `trustStatus=trusted` | Compatibility display for `reviewStatus=release-reviewed` and `writeAuthority=allowed`. | | `source=local` | `sourceKind=local`, `sourceId=local`. | | `recipeRef=recipe://local/` | Local recipe reference. | @@ -322,8 +334,8 @@ is not enough to grant writes. ## Write grants -A write grant is local-only authority for a non-bundled recipe origin to change -live settings. It must live outside the settings folder and must contain no +A write grant is local-only authority for an updated official-catalog or +additional remote recipe origin to change live settings. It must live outside the settings folder and must contain no secret values. Minimum write-grant fields: @@ -358,7 +370,7 @@ and execution time: 2. the source is not empty, unknown, disabled, removed, blocked, invalid, expired, or revoked; 3. the recipe origin was resolved by the manager; -4. the recipe digest matches the resolved bundled/local/catalog content; +4. the recipe digest matches the resolved official-snapshot or remote catalog content; 5. the operation is within declared read/write scopes; 6. schema, platform, support, capability, lifecycle, redaction, path, symlink, and command-boundary checks pass; @@ -370,9 +382,10 @@ Source-specific rules: | Source | Write rule | | --- | --- | -| `bundled` | Allowed by release trust when all validation gates pass. | -| `local` | Denied until a local write grant/trust record matches the current digest and write surface. | +| `official` bundled snapshot | Allowed by release trust when all validation gates pass. | +| `official` updated from remote | Denied until #229 update, integrity, identity, and write-authority gates pass. | | `remote` | Denied until the source is enabled, identity-pinned, signature-valid, unexpired, and the exact recipe digest/write surface has a local write grant. | +| `local` historical/advanced-only | Out of the normal v2 path; denied unless a future issue explicitly reintroduces local catalog write grants. | | empty / unknown | Always denied. | | disabled / removed / blocked | Always denied. | | invalid / missing required signature | Always denied. | @@ -384,11 +397,15 @@ or treating `--yes` as trust approval. ## Update rules -### Bundled updates +### Official catalog snapshot and updates + +The bundled official catalog snapshot is delivered through manager releases. +Explain/security output should include snapshot version/digest provenance where +available, and snapshot write authority remains release-process authority. -Bundled recipe updates are delivered through manager releases. Explain/security -output should include bundled version/digest provenance where available, but -bundled write authority remains release-process authority. +Updating the official catalog from its remote origin belongs to #229 and must use +the remote update, integrity, and write-authority gates before updated recipe +data can write live settings. ### Local updates @@ -464,22 +481,23 @@ write authority. Default resolution: 1. user-pinned `recipeRef` when it remains resolvable and allowed; -2. bundled default for bundled-supported targets; -3. no automatic choice; show local/remote candidates that require explicit - selection. +2. official catalog snapshot default for supported targets; +3. no automatic choice; show additional remote candidates that require explicit + selection after #229 defines that behavior. Rules: -- a remote recipe must never silently override a bundled recipe; -- a local recipe must never silently override a bundled recipe; +- an additional remote recipe must never silently override an official-catalog snapshot recipe; +- a historical/advanced local recipe must never silently override an official-catalog snapshot recipe; - display names are not security identifiers; - duplicate recipe IDs inside one catalog are invalid unless #229 defines an explicit variant mechanism; - collisions are visible before writes and require explicit selection when the - bundled default is not used. + official default is not used. -This preserves the current implementation behavior where bundled lookup wins -before local lookup and turns that behavior into the accepted default model. +This preserves the accepted user-facing default that official catalog support is +the normal choice unless the user explicitly selects another source under future +#229 rules. ## Command-execution boundary @@ -536,17 +554,18 @@ files or run commands are not harmless just because the command is `status` or ## User-facing examples -### Bundled recipe before write +### Official catalog snapshot recipe before write -Normal users should see this as built-in app support, not as catalog/tap -management. +Normal users should see this as support from the official catalog, not as a +recipe/tap management concept. ```text Target: git -Recipe source: built in with dotfiles-manager -Recipe: recipe://bundled/git +Catalog: official +Catalog data: bundled snapshot +Recipe: recipe://official/git Writes live settings: ~/.gitconfig [user] email, [user] name -Permission: allowed by bundled release +Permission: allowed by dotfiles-manager release snapshot ``` ### Local recipe before write @@ -653,10 +672,8 @@ claiming implementation acceptance. The minimum matrix is: | Scenario | Required result | | --- | --- | -| Bundled default recipe | Source shown as built in; write authority allowed after normal safety checks. | -| Bundled + local collision | Bundled remains default; local candidate is visible but cannot override silently. | -| Local recipe without grant | Candidate/explain visible; live write blocked as review required. | -| Local recipe with matching grant | Live write allowed only for matching digest/write surface after normal confirmation. | +| Official catalog snapshot recipe | Source shown as official catalog bundled snapshot; write authority allowed after normal safety checks. | +| Updated official catalog recipe | Discovery/update behavior belongs to #229; live writes blocked unless remote update/trust/write-authority gates pass. | | Remote source enabled but recipe ungranted | Discovery allowed; live write blocked as recipe approval required. | | Remote recipe with matching grant | Live write allowed only for exact digest/write surface after normal confirmation. | | Remote recipe digest/write surface changed | Existing grant invalidated; live write blocked pending review. | @@ -667,19 +684,22 @@ claiming implementation acceptance. The minimum matrix is: ## Requirements for #228 -#228 must implement built-in/local catalog discovery against this model: +#228 must implement official-catalog bundled-snapshot discovery against this +model: -- expose the bundled registry as the normalized bundled catalog/source; -- preserve bundled-first precedence; -- show local recipes as local candidates, not bundled replacements; -- keep local write authority bound to the external local trust record; +- expose the release-bundled official catalog snapshot as the default catalog; +- show user-facing source as `official`, not `built-in`; +- avoid local catalog lifecycle and local recipe candidates in the normal path; - make source/origin fields visible in list/explain/planning output before writes; +- prove no network fetch/update is performed in #228; - fail closed for unknown source kinds. ## Requirements for #229 -#229 must not implement remote writes until it specifies and verifies: +#229 owns updating the preconfigured official catalog and adding/updating/ +disabling/removing additional remote catalogs. It must not implement remote +writes until it specifies and verifies: - remote source configuration and local state location; - manifest schema and signature verification; @@ -691,8 +711,9 @@ claiming implementation acceptance. The minimum matrix is: - command-execution boundary enforcement; - blackbox examples showing blocked-by-default remote writes. -Remote catalog discovery without writes may be implemented earlier only if it -cannot grant live write authority and all output makes that limitation explicit. +Remote catalog discovery or official-catalog updates without writes may be +implemented earlier only if they cannot grant live write authority and all output +makes that limitation explicit. ## Acceptance checklist for #227 @@ -701,6 +722,6 @@ cannot grant live write authority and all output makes that limitation explicit. - Recipe origin is visible before writes through the effective origin object and examples. - Trust/write-authority rules are defined before remote writes are allowed. -- The bundled registry is accepted as the normalized bundled/default catalog - seed. +- The release-bundled official catalog snapshot is accepted as the default + catalog seed for #228. - Remote catalogs are data-only and cannot become arbitrary command execution. diff --git a/docs/internal/ux/README.md b/docs/internal/ux/README.md index 7ef9f7f..ad09dd1 100644 --- a/docs/internal/ux/README.md +++ b/docs/internal/ux/README.md @@ -22,10 +22,12 @@ tests, and command documentation. storyboard for selecting several supported apps/settings through the current repeated `add ` flow, without implying unsupported multi-target add syntax. -- `v2-catalog-discovery-storyboard.md` — pre-implementation terminal - storyboard for #228 built-in/local catalog discovery using the flattened - normal-user `list`, `search`, and `explain` surface, with `catalog ...` and - advanced `recipe ...` boundaries. +- `v2-catalog-discovery-storyboard.md` — recontracted pre-implementation + terminal storyboard for #228 official-catalog app discovery from the bundled + snapshot using the flattened normal-user `list`, `search`, and `explain` + surface. It removes internal pseudo-app targets and catalog lifecycle commands + from the normal #228 path and reserves catalog updates/additional remote + catalogs for #229. - `v2-cli-discovery-normalization-storyboard.md` — pre-implementation storyboard for the #252 focused discovery slice that changes normal `list`, adds `search` and top-level `explain`, and preserves selected-settings list diff --git a/docs/internal/ux/mocks/v2-catalog-discovery/README.md b/docs/internal/ux/mocks/v2-catalog-discovery/README.md new file mode 100644 index 0000000..f46c245 --- /dev/null +++ b/docs/internal/ux/mocks/v2-catalog-discovery/README.md @@ -0,0 +1,49 @@ +# v2 catalog discovery runnable UX mock + +This directory contains runnable/replayable design evidence for the recontracted +#228 catalog-discovery scope. + +It supersedes the earlier PR #255 mock/evidence that modeled local catalog +lifecycle. The new target is normal discovery from dotfiles-manager official +catalog metadata: + +- real official-catalog apps/tools only in `list`; +- no internal pseudo-app targets in normal discovery; +- no local or remote catalog lifecycle commands in #228; +- no first-run download/update implementation in #228; +- official-catalog download/update and additional remote catalogs are future #229 work. + +The mock is not product code. + +## Safety + +- The mock never reads live app settings. +- The mock never writes live app settings. +- The mock never reads or writes the user's real settings storage folder. +- The mock never reads real user catalog contents. +- The mock never changes catalog state. +- The mock never uses the network. + +## Run the replayable demo + +From the repository root. Requires Python; set `PYTHON=/path/to/python` if +`python3` is not the desired interpreter. + +```bash +docs/internal/ux/mocks/v2-catalog-discovery/run-demo.sh +``` + +To mechanically verify the demo output against the checked-in golden transcript: + +```bash +docs/internal/ux/mocks/v2-catalog-discovery/run-demo.sh --check +``` + +## What the demo covers + +- official-catalog app discovery from deterministic mock catalog metadata; +- app-first search and explanation without teaching the `recipe` noun; +- unsupported app search that points to future catalog capabilities without + exposing unavailable update/add commands; +- internal pseudo-app targets excluded from normal discovery; +- official catalog listing with concise version and updated-time metadata. diff --git a/docs/internal/ux/mocks/v2-catalog-discovery/expected-demo.txt b/docs/internal/ux/mocks/v2-catalog-discovery/expected-demo.txt new file mode 100644 index 0000000..a3f7539 --- /dev/null +++ b/docs/internal/ux/mocks/v2-catalog-discovery/expected-demo.txt @@ -0,0 +1,59 @@ +# v2 catalog discovery runnable UX mock +# Work item: #228 recontracted official-catalog discovery baseline +# Safety: mock only; no live settings, stored settings, real catalog folders, secrets, or network resources are read or changed. + +$ dotfiles-manager list +Supported apps + + APP CATALOG STATE + git official not managed + nvim official not managed + ssh official not managed + starship official not managed + tmux official not managed + zsh official not managed + +Use `dotfiles-manager explain ` to see what can be managed. + +$ dotfiles-manager search git +Search results for "git" + + APP CATALOG STATE + git official not managed + +Use `dotfiles-manager explain git` to see what can be managed. + +$ dotfiles-manager search wezterm +No supported apps found for "wezterm". + +The current official catalog supports: + git, nvim, ssh, starship, tmux, zsh + +This version searches only the current official catalog. +Future versions may refresh official support data or add remote catalogs. +This version cannot do that yet. + +$ dotfiles-manager explain git +Git is supported. + +App ID: git +Catalog: official +State: not managed + +Can manage: + git:user.email Git user email + git:user.name Git user name + +Does not manage: + credential.helper + [credential] sections + include/includeIf expansion + +$ dotfiles-manager catalog list +Catalogs + +Catalogs define app/tool support; they do not store your settings. + + dotfiles-manager/official active for discovery + Catalog version: 9f2c7a1 + Catalog updated: 2026-06-30 18:00 UTC diff --git a/docs/internal/ux/mocks/v2-catalog-discovery/mock-dotfiles-manager.py b/docs/internal/ux/mocks/v2-catalog-discovery/mock-dotfiles-manager.py new file mode 100755 index 0000000..fe45729 --- /dev/null +++ b/docs/internal/ux/mocks/v2-catalog-discovery/mock-dotfiles-manager.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +"""Runnable UX mock for the recontracted #228 catalog discovery surface. + +This mock is intentionally separate from product code. It simulates the accepted +official-catalog discovery target from +docs/internal/ux/v2-catalog-discovery-storyboard.md. + +Safety: the mock never reads or writes live app settings, the user's real +settings storage folder, real catalog folders, secrets, or network resources. +""" +from __future__ import annotations + +import sys + +OFFICIAL_APPS = ["git", "nvim", "ssh", "starship", "tmux", "zsh"] +OFFICIAL_CATALOG_VERSION = "9f2c7a1" +OFFICIAL_CATALOG_UPDATED = "2026-06-30 18:00 UTC" + + +def out(text: str = "") -> None: + print(text) + + +def print_list() -> int: + out("Supported apps") + out("") + out(" APP CATALOG STATE") + for app in OFFICIAL_APPS: + out(f" {app:<8} official not managed") + out("") + out("Use `dotfiles-manager explain ` to see what can be managed.") + return 0 + + +def print_search(args: list[str]) -> int: + query = args[0] if args else "" + if query.lower() == "git": + out('Search results for "git"') + out("") + out(" APP CATALOG STATE") + out(" git official not managed") + out("") + out("Use `dotfiles-manager explain git` to see what can be managed.") + return 0 + out(f'No supported apps found for "{query}".') + out("") + out("The current official catalog supports:") + out(" git, nvim, ssh, starship, tmux, zsh") + out("") + out("This version searches only the current official catalog.") + out("Future versions may refresh official support data or add remote catalogs.") + out("This version cannot do that yet.") + return 0 + + +def print_explain(args: list[str]) -> int: + app = args[0] if args else "" + if app == "git": + out("Git is supported.") + out("") + out("App ID: git") + out("Catalog: official") + out("State: not managed") + out("") + out("Can manage:") + out(" git:user.email Git user email") + out(" git:user.name Git user name") + out("") + out("Does not manage:") + out(" credential.helper") + out(" [credential] sections") + out(" include/includeIf expansion") + return 0 + out(f"App not supported: {app}") + return 1 + + +def catalog_list() -> int: + out("Catalogs") + out("") + out("Catalogs define app/tool support; they do not store your settings.") + out("") + out(" dotfiles-manager/official active for discovery") + out(f" Catalog version: {OFFICIAL_CATALOG_VERSION}") + out(f" Catalog updated: {OFFICIAL_CATALOG_UPDATED}") + return 0 + + +def main(argv: list[str]) -> int: + if not argv: + out("dotfiles-manager UX mock: missing command") + return 1 + cmd, rest = argv[0], argv[1:] + if cmd == "list": + return print_list() + if cmd == "search": + return print_search(rest) + if cmd == "explain": + return print_explain(rest) + if cmd == "catalog": + if not rest: + out("catalog: missing subcommand") + return 1 + sub, subargs = rest[0], rest[1:] + if sub == "list": + return catalog_list() + out(f"dotfiles-manager UX mock: unsupported command {' '.join(argv)!r}") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/docs/internal/ux/mocks/v2-catalog-discovery/run-demo.sh b/docs/internal/ux/mocks/v2-catalog-discovery/run-demo.sh new file mode 100755 index 0000000..b7a3863 --- /dev/null +++ b/docs/internal/ux/mocks/v2-catalog-discovery/run-demo.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MOCK="${ROOT_DIR}/mock-dotfiles-manager.py" +EXPECTED="${ROOT_DIR}/expected-demo.txt" +PYTHON_BIN="${PYTHON:-python3}" +TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/dfm-v2-catalog-mock.XXXXXX")" +trap 'rm -rf "${TMP_DIR}"' EXIT + +run_command() { + local status=0 + printf '$ dotfiles-manager' + printf ' %q' "$@" + printf '\n' + "${PYTHON_BIN}" "${MOCK}" "$@" || status=$? + if [[ ${status} -ne 0 ]]; then + printf '[exit %d]\n' "${status}" + fi + printf '\n' +} + +generate() { + cat <<'HEADER' +# v2 catalog discovery runnable UX mock +# Work item: #228 recontracted official-catalog discovery baseline +# Safety: mock only; no live settings, stored settings, real catalog folders, secrets, or network resources are read or changed. + +HEADER + run_command list + run_command search git + run_command search wezterm + run_command explain git + run_command catalog list +} + +emit_normalized_demo() { + generate | "${PYTHON_BIN}" -c 'import sys; data = sys.stdin.read(); sys.stdout.write(data.rstrip("\n") + "\n")' +} + +if [[ "${1:-}" == "--check" ]]; then + actual="${TMP_DIR}/actual-demo.txt" + emit_normalized_demo > "${actual}" + diff -u "${EXPECTED}" "${actual}" + echo "OK: recontracted catalog discovery UX mock output matches expected-demo.txt" +else + emit_normalized_demo +fi diff --git a/docs/internal/ux/v2-catalog-discovery-storyboard.md b/docs/internal/ux/v2-catalog-discovery-storyboard.md index 568c3f8..bf4cda0 100644 --- a/docs/internal/ux/v2-catalog-discovery-storyboard.md +++ b/docs/internal/ux/v2-catalog-discovery-storyboard.md @@ -1,72 +1,113 @@ --- owner: Product + Core Engineering -status: Design evidence for #228 -last-updated: 2026-06-28 +status: Recontracted design evidence for #228 +last-updated: 2026-07-02 source-issue: 228 related-issues: - 214 - 227 - 229 - 252 +supersedes: + - older #228 local-catalog storyboard/transcripts + - PR #255 superseded local-catalog implementation scope --- # v2 catalog discovery storyboard -This storyboard is pre-implementation design evidence for #228. It supersedes -older #228 issue-comment transcripts that used `recipe list` as the normal -user discovery command. +This storyboard is pre-implementation design evidence for the recontracted #228 +work item. It supersedes earlier #228 storyboards that exposed internal +pseudo-app targets or catalog lifecycle commands in normal discovery. -The accepted user-facing direction is: +Project Owner decisions recorded on 2026-06-30: ```text -Users add catalogs. -Catalogs provide support for apps/tools. -Users manage apps/tools. -Recipe is an advanced authoring/debugging noun, not the normal discovery noun. +Normal discovery should show real supported apps/tools only. +Internal/generated pseudo-app targets are not normal app/tool entries. +Catalog lifecycle commands are removed from #228 and from the normal user path. +The default catalog should be the dotfiles-manager official catalog, not a +user-facing "built-in" catalog. +The official catalog is preconfigured by the app, but it is not presented as +baked into the app. +Catalog list should show useful state such as version and last-updated time. +Remote catalog download/update/add/remove behavior is handled by #229. ``` -## Boundaries - -#228 should implement the built-in/local catalog discovery foundation and the -normal discovery/explanation surface that depends on it: - -- `list` — show supported apps/tools and whether each is managed; -- `search ` — find supported apps/tools across enabled catalogs; -- `explain ` — explain app support, provenance, candidates, and write - authority without requiring the user to understand recipe files; -- `catalog ...` — manage built-in/local catalog sources; -- advanced `recipe ...` commands may remain for authors/debugging, but they are - not the happy-path discovery surface. +## Accepted user-facing direction -Broader command cleanup belongs to #252 when it is outside #228's catalog -foundation scope, especially: +```text +Users manage apps/tools. +Apps/tools are supported by recipes/support definitions. +The dotfiles-manager official catalog is preconfigured. +Normal catalog output shows the catalog name, version, and last-updated time. +Additional support comes later through official-catalog download/update behavior +and additional remote catalogs (#229). +Recipe remains an advanced authoring/debugging noun, not the normal discovery noun. +``` -- final compatibility/deprecation behavior for existing `add`, `recipe list`, - `recipe explain`, and `app create/validate/test` commands; -- `manage`/`unmanage` command implementation if it is not already needed for the - #228 catalog/unavailable-source scenarios; -- full normal help reordering beyond the #228 discovery and catalog lifecycle - changes. +## Boundaries -Remote GitHub catalogs are not implemented by #228. The future shape -`catalog add shpoont/custom-recipes` is reserved for #229. +#228 should implement the official-catalog discovery baseline and the normal +app/tool-oriented discovery/explanation surface that depends on it: + +- `list` — show real supported apps/tools from the current official catalog + metadata and whether each is managed; +- `search ` — find supported apps/tools from the current official + catalog metadata; +- `explain ` — explain official-catalog support, managed settings, + and important exclusions; +- `catalog list` — show the official catalog with useful state: version and + last-updated time. + +Out of scope for #228 after recontracting: + +- internal/generated pseudo-app targets in the normal `list`/`search` happy path; +- public arbitrary-file management under a pseudo-app name; +- local or remote catalog add/list/disable/enable/remove lifecycle; +- local-only app candidates, local/official collisions, or local unavailable + source states; +- implementing first-run download or update of the official catalog; +- adding, updating, disabling, removing, signing, caching, or integrity-checking + additional remote catalogs; +- remote write grants or live-write authority; +- full `manage`/`unmanage` command implementation unless separately + recontracted; +- broader compatibility/deprecation behavior for existing `recipe ...` and + `app ...` authoring/debugging commands (#252 / future focused cleanup). + +Remote catalog commands such as `catalog add /` are the planned +normal extension path, but they are omitted from the #228 public/mock surface +until #229 implements catalog lifecycle behavior. `catalog update` is likewise +omitted until update behavior is implemented. ## UX principles - The normal path is app-first/action-first, not recipe-first. - `list`, `search`, and `explain` are safe read-only discovery commands. -- Catalogs are an advanced expansion mechanism, similar to Homebrew taps. -- Built-in support is always available, deterministic/offline, and not removable. -- Local catalogs are local-only; adding one does not fetch from the network. -- Adding/disabling/removing a catalog never deletes live app settings or stored - settings. -- Local recipes are discoverable after machine validation, but they do not - silently override built-in support. -- Before any operation that can write live settings, the effective source/origin - and write authority must be visible. +- Do not repeat "nothing was read/changed" safety footers in normal discovery + output; they add noise for little user value. Reserve explicit safety notices + and confirmations for commands that inspect or change real settings. +- `explain ` describes support and must not read or print live setting + values. +- Normal users should see the default source as the **official catalog**, not as + a "built-in catalog". +- Do not present the official catalog as baked into or included with the app. +- `catalog list` should avoid source/cache/network/removable detail in normal + output; show version and last-updated time. +- Downloading/updating the official catalog and adding additional remote catalogs + are #229 responsibilities, not #228 responsibilities. +- Normal `list` should not include internal/generated pseudo-app mechanisms. +- If a user wants support for an app that is not in the current official + catalog, the future path is refreshed official support data or an additional + remote catalog, not a local catalog tip in default output. +- Local recipe authoring/debugging may exist as an advanced/internal capability, + but it is not #228 normal-user product scope. +- Before any operation that can write live settings, the effective recipe/source + and write authority must still be visible in the sync/status surface owned by + the relevant implementation issue. - The word `recipe` can appear in advanced/debug details, JSON, validation - errors, and authoring docs, but normal users should be able to succeed without - learning it. + errors, and authoring docs, but normal users should be able to discover support + without learning it. ## Command vocabulary used by these transcripts @@ -81,12 +122,8 @@ dotfiles-manager save [...] dotfiles-manager apply [...] dotfiles-manager catalog list -dotfiles-manager catalog add --name -dotfiles-manager catalog disable -dotfiles-manager catalog enable -dotfiles-manager catalog remove -dotfiles-manager recipe ... # advanced authoring/debugging only +dotfiles-manager recipe ... # advanced authoring/debugging only ``` `save` and `apply` remain directional sync aliases: @@ -97,10 +134,10 @@ dotfiles-manager recipe ... # advanced authoring/debugging only `manage` means enroll an app/tool so it participates in status/diff/sync by default. `unmanage` means stop tracking it by default. Neither command should silently overwrite live settings, uninstall apps, delete live settings, or delete -stored settings. #252 owns full command cleanup for those verbs unless #228 -needs a minimal form to explain unavailable local-catalog selections. +stored settings. #252 owns full command cleanup for those verbs unless a focused +work item explicitly changes that. -## Transcript 1: first run/offline built-in discovery +## Transcript 1: list official-catalog apps Command: @@ -113,24 +150,20 @@ Expected output: ```text Supported apps - APP SOURCE STATE - custom.files built-in not managed - git built-in not managed - nvim built-in not managed - ssh built-in not managed - starship built-in not managed - tmux built-in not managed - zsh built-in not managed - -Catalogs: - built-in enabled ships with dotfiles-manager + APP CATALOG STATE + git official not managed + nvim official not managed + ssh official not managed + starship official not managed + tmux official not managed + zsh official not managed -No live settings were read or changed. -No stored settings were changed. +Use `dotfiles-manager explain ` to see what can be managed. ``` -User takeaway: the tool has built-in app support and works offline before any -catalog setup. +User takeaway: the tool has real app/tool support from the current official +catalog metadata. Internal/generated pseudo-app targets are intentionally absent +from normal `list`. ## Transcript 2: search supported apps @@ -145,170 +178,39 @@ Expected output: ```text Search results for "git" - APP SOURCE STATE - git built-in not managed + APP CATALOG STATE + git official not managed Use `dotfiles-manager explain git` to see what can be managed. -No live settings were read or changed. ``` User takeaway: search is app/tool-oriented and does not require recipe terms. -## Transcript 3: explain built-in support +## Transcript 3: search an unsupported app Command: ```bash -dotfiles-manager explain git +dotfiles-manager search wezterm ``` Expected output: ```text -Git is supported. +No supported apps found for "wezterm". -App ID: git -Source: built-in support from dotfiles-manager -State: not managed +The current official catalog supports: + git, nvim, ssh, starship, tmux, zsh -Can manage: - git:user.email Git user email - git:user.name Git user name - -Does not manage: - credential.helper - [credential] sections - include/includeIf expansion - -Why this source is used: - Built-in support is the default for Git and is trusted by the - dotfiles-manager release. - -No live values were printed. -No live settings were changed. +This version searches only the current official catalog. +Future versions may refresh official support data or add remote catalogs. +This version cannot do that yet. ``` -Advanced/verbose output may include `recipe://bundled/git`, catalog IDs, -digests, and source IDs. Default output keeps those details secondary. +User takeaway: there is a future extension path, but the default output does not +push a local catalog workaround. -## Transcript 4: list catalogs before local catalogs exist - -Command: - -```bash -dotfiles-manager catalog list -``` - -Expected output: - -```text -Catalogs - - built-in Built in enabled - Source: ships with dotfiles-manager - Updates: with dotfiles-manager releases - Network: not used - Removable: no - -Local catalogs: none -Remote catalogs: not supported yet - -No live settings were read or changed. -``` - -User takeaway: catalogs are understandable, but not required for the built-in -happy path. - -## Transcript 5: add a valid local catalog - -Precondition: `~/dotfiles-manager-recipes` is a local folder containing recipe -files for `example-tool` and `git`. - -Command: - -```bash -dotfiles-manager catalog add ~/dotfiles-manager-recipes --name personal -``` - -Expected output: - -```text -Added local catalog: personal - -Source: - ~/dotfiles-manager-recipes - -Validated support: - example-tool local support - git local candidate; built-in support remains the default - -Network: not used -No live settings were read or changed. -No stored settings were changed. -``` - -User takeaway: adding a local catalog is safe, local-only, and validated before -it becomes active. - -## Transcript 6: reject invalid local catalog - -Command: - -```bash -dotfiles-manager catalog add ~/broken-recipes --name broken -``` - -Expected output: - -```text -Catalog not added: broken - -Reason: - 1 support definition failed validation. - -Invalid support: - broken-tool - - unknown field "dangerousCommand" - -No live settings were read or changed. -No stored settings were changed. -``` - -User takeaway: invalid catalog content fails closed before it affects discovery -or writes. - -## Transcript 7: list with built-in and local support - -Command: - -```bash -dotfiles-manager list -``` - -Expected output: - -```text -Supported apps - - APP SOURCE STATE - custom.files built-in not managed - example-tool personal not managed - git built-in not managed - also in personal; built-in remains default - nvim built-in not managed - ssh built-in not managed - starship built-in not managed - tmux built-in not managed - zsh built-in not managed - -Use `dotfiles-manager explain ` to see support details and candidates. -No live settings were read or changed. -``` - -User takeaway: local support appears as app support. Collisions are visible and -safe. - -## Transcript 8: explain built-in/local collision +## Transcript 4: explain official-catalog support Command: @@ -319,274 +221,49 @@ dotfiles-manager explain git Expected output: ```text -Git is supported by multiple sources. - -Default source: - built-in support from dotfiles-manager - -Other available source: - local catalog: personal - Status: candidate only - -Why built-in is used: - Built-in support remains the default unless you explicitly choose another - source. Local support cannot silently replace built-in support. - -Can manage from the default source: - git:user.email Git user email - git:user.name Git user name - -No live values were printed. -No live settings were changed. -``` - -Verbose/debug output may include the effective recipe origins: - -```text -Default: recipe://bundled/git -Candidate: recipe://local/git -``` - -## Transcript 9: explain local-only support - -Command: - -```bash -dotfiles-manager explain example-tool -``` - -Expected output: - -```text -Example Tool is supported by a local catalog. +Git is supported. -App ID: example-tool -Source: local catalog personal +App ID: git +Catalog: official State: not managed Can manage: - example-tool:config Config file - Live location: $HOME/.config/example-tool/config.yaml - -Before live settings can be changed: - dotfiles-manager will show this source and the paths it wants to manage. - Local support requires write approval before it can change live settings. - -No live values were printed. -No live settings were changed. -``` - -User takeaway: the local source is visible in plain language before any write. - -## Transcript 10: before-write origin summary for local support - -Command: - -```bash -dotfiles-manager sync --dry-run example-tool -``` - -Expected output when local write authority is not yet granted: - -```text -Preview sync for example-tool - -Source: - local catalog personal - -This support wants to manage: - example-tool:config - Live location: $HOME/.config/example-tool/config.yaml - Stored settings: desired/user//targets/example-tool/... - -Result: - Blocked before write. - -Reason: - Local support requires write approval before dotfiles-manager can change live - settings for this app. - -Next step: - Review and approve this local support before allowing writes. - If this version does not include an approval command yet, this app remains - blocked for live writes. - -No live settings were changed. -No stored settings were changed. -``` - -#228 should show this blocked state and preserve the source/provenance details. -The concrete approval/review command is outside #228 unless the active issue is -explicitly recontracted to include it. - -If the exact local write grant already exists, the command may proceed to the -normal dry-run sync preview, but it must still show the local source before any -confirmed write. - -## Transcript 11: disable a local catalog - -Command: - -```bash -dotfiles-manager catalog disable personal -``` - -Expected output: - -```text -Disabled local catalog: personal - -No longer available from this catalog: - example-tool - git local candidate - -Nothing was deleted. -Live app settings were not changed. -Stored settings were not changed. - -If a managed app depends on this catalog, it will show as source unavailable -until you enable the catalog, add another source, or stop managing that app. -``` - -Command: - -```bash -dotfiles-manager list -``` - -Expected output excerpt: - -```text -Supported apps - - APP SOURCE STATE - custom.files built-in not managed - git built-in not managed - nvim built-in not managed - ssh built-in not managed - starship built-in not managed - tmux built-in not managed - zsh built-in not managed - -Disabled local catalogs: - personal 2 hidden apps/candidates -``` - -## Transcript 12: unavailable managed app after disabling/removing source - -Precondition: `example-tool` was managed before the `personal` catalog was -disabled or removed. - -Command: - -```bash -dotfiles-manager status example-tool -``` - -Expected output: - -```text -example-tool: blocked - -Reason: - This app is managed with support from local catalog "personal", but that - catalog is disabled or removed. - -No live app settings were read or changed. -Stored settings were not changed. -Stored settings still exist, if they existed before. - -To continue: - Enable the catalog: - dotfiles-manager catalog enable personal - - Or add another catalog that supports example-tool. - - Or remove this app from the managed set. -``` - -#228 should not invent a separate stop-managing command. The normal -`unmanage ` command and exact compatibility behavior are tracked by #252. - -User takeaway: disabling/removing a catalog is reversible and data-preserving. - -## Transcript 13: enable a local catalog + git:user.email Git user email + git:user.name Git user name -Command: +Does not manage: + credential.helper + [credential] sections + include/includeIf expansion -```bash -dotfiles-manager catalog enable personal ``` -Expected output: +Advanced/verbose output may include `recipe://official/git`, catalog IDs, +origin URIs, snapshot digests, and source IDs. Default output keeps those details +secondary. -```text -Enabled local catalog: personal - -Validated support: - example-tool local support - git local candidate; built-in support remains the default - -No live settings were read or changed. -No stored settings were changed. -``` - -## Transcript 14: remove a local catalog +## Transcript 5: list catalogs Command: ```bash -dotfiles-manager catalog remove personal +dotfiles-manager catalog list ``` Expected output: ```text -Removed local catalog: personal - -Forgotten by dotfiles-manager: - ~/dotfiles-manager-recipes - -Nothing was deleted from that folder. -Live app settings were not changed. -Stored settings were not changed. - -Apps that depended on this catalog are now source unavailable until you re-add -this catalog, choose another source, or stop managing those apps. -``` - -Important distinction: - -- `disable` keeps the source record and can be re-enabled by name; -- `remove` forgets the source record but does not delete the catalog folder or - settings data. - -## Transcript 15: remote catalog syntax reserved for #229 - -Command: - -```bash -dotfiles-manager catalog add shpoont/custom-recipes -``` - -Expected #228 output: - -```text -Catalog not added: shpoont/custom-recipes - -Reason: - Remote GitHub catalogs are not supported in this version of dotfiles-manager. +Catalogs -For now, use a local catalog folder: - dotfiles-manager catalog add ./custom-recipes --name personal +Catalogs define app/tool support; they do not store your settings. -Remote catalog trust, updates, and write gates are planned separately. -No live settings were read or changed. -No stored settings were changed. + dotfiles-manager/official active for discovery + Catalog version: 9f2c7a1 + Catalog updated: 2026-06-30 18:00 UTC ``` -User takeaway: the future Homebrew-like syntax is reserved, but #228 remains -local/offline. +User takeaway: users can see which official catalog data they are using without +reading source/cache/network/removable implementation details. ## JSON and advanced-output notes @@ -598,6 +275,7 @@ implementation fields needed by tests and advanced users, including: - `catalogId`; - `sourceDisplayName`; - `originUri`; +- `snapshotDigest`; - `recipeRef`; - `recipeDigest`; - `reviewStatus`; @@ -609,12 +287,21 @@ unsafe write surfaces must fail closed. ## Acceptance notes for #228 implementation -A #228 implementation PR should use this storyboard as design evidence and then -provide real-result verification with: +A #228 implementation PR should use this storyboard and the runnable mock in +`docs/internal/ux/mocks/v2-catalog-discovery/` as design evidence, then provide +real-result verification with: - targeted catalog/discovery tests and `go test ./...` where practical; -- temp-home/fixture command output for built-in first-run/offline discovery; -- local catalog add/list/disable/enable/remove evidence; -- built-in/local collision evidence; -- unavailable-source status evidence; -- proof that remote catalog/network behavior was not introduced. +- temp-home/fixture command output for official-catalog metadata discovery; +- proof that internal/generated pseudo-app targets are not shown as normal + apps/tools in `list` or `search`; +- proof that local catalog lifecycle is not implemented or promoted in #228; +- proof that remote catalog add/update/write behavior was not introduced by + #228; +- proof that #228 uses deterministic official catalog metadata and does not + implement first-run download or update behavior; +- proof that #255's prior local-catalog implementation scope is not treated as + accepted #228 behavior; +- proof that runtime output and tests are reconciled with the refined design: + normal discovery should not keep old repeated "nothing was read/changed" + safety footers. diff --git a/docs/internal/ux/v2-cli-discovery-normalization-storyboard.md b/docs/internal/ux/v2-cli-discovery-normalization-storyboard.md index 99dab96..5af2157 100644 --- a/docs/internal/ux/v2-cli-discovery-normalization-storyboard.md +++ b/docs/internal/ux/v2-cli-discovery-normalization-storyboard.md @@ -1,7 +1,7 @@ --- owner: Product + Core Engineering -status: Design evidence for #252 focused slice -last-updated: 2026-06-28 +status: Design evidence for #252 focused slice; #228 recontract note applied +last-updated: 2026-07-02 source-issue: 252 related-issues: - 228 @@ -16,6 +16,17 @@ This storyboard is pre-implementation design evidence for the first #252 slice: make normal discovery app/tool-oriented and flattened so #228 catalog discovery can use the accepted command model. +2026-06-30 note: #228 was later recontracted to remove internal pseudo-app +targets from normal discovery, remove catalog lifecycle commands from the +normal #228 path, and use the dotfiles-manager official catalog instead of a +user-facing built-in catalog. The expected normal `list` examples below reflect +that managed change. + +2026-06-30 UX refinement: normal read-only discovery output should not repeat +"nothing was read/changed" footers. They are low-value noise for `list`, +`search`, and `explain`; safety notices and confirmations belong with commands +that inspect or change real settings. + The frozen slice is intentionally narrower than all of #252. It covers: - `dotfiles-manager list` as normal supported-app discovery; @@ -32,7 +43,7 @@ remote catalogs, or changes to sync execution semantics. Normal users should think: ```text -dotfiles-manager supports apps/tools. +dotfiles-manager supports apps/tools through its official catalog. I can list or search supported apps. I can explain one app before deciding whether to manage it. Recipes are implementation details for authors and debugging. @@ -99,20 +110,16 @@ Expected output: ```text Supported apps - APP SOURCE STATE - custom.files built-in not managed - git built-in not managed - nvim built-in not managed - ssh built-in not managed - starship built-in not managed - tmux built-in not managed - zsh built-in not managed + APP CATALOG STATE + git official not managed + nvim official not managed + ssh official not managed + starship official not managed + tmux official not managed + zsh official not managed Use `dotfiles-manager explain ` to see what can be managed. -Use `dotfiles-manager list --settings` to list selected managed settings. -No live settings were read or changed. -No stored settings were changed. ``` Design note: because the settings folder is not initialized, every app is shown @@ -133,23 +140,19 @@ Expected output: ```text Supported apps - APP SOURCE STATE - custom.files built-in not managed - git built-in managed - nvim built-in not managed - ssh built-in not managed - starship built-in not managed - tmux built-in not managed - zsh built-in not managed + APP CATALOG STATE + git official managed + nvim official not managed + ssh official not managed + starship official not managed + tmux official not managed + zsh official not managed Managed apps: git 2 selected settings Use `dotfiles-manager status git` to inspect drift for Git. -Use `dotfiles-manager list --settings` to see selected settings. -No live settings were read or changed. -No stored settings were changed. ``` Design note: detecting `managed` may read the settings folder profile metadata @@ -200,12 +203,10 @@ Expected output: ```text Search results for "git" - APP SOURCE STATE - git built-in not managed + APP CATALOG STATE + git official not managed Use `dotfiles-manager explain git` to see what can be managed. -No live settings were read or changed. -No stored settings were changed. ``` Command: @@ -219,16 +220,18 @@ Expected output: ```text No supported apps found for "shell". -Try: - dotfiles-manager list +The current official catalog supports: + git, nvim, ssh, starship, tmux, zsh -No live settings were read or changed. -No stored settings were changed. +This version searches only the current official catalog. +Future versions may refresh official support data or add remote catalogs. +This version cannot do that yet. ``` Design note: this slice searches app IDs, display names, aliases, and summaries -from existing bundled support metadata. Local-catalog candidates, collisions, -and richer catalog provenance remain #228 work. +from the current official catalog metadata. No-match text follows the #228 +official-catalog mock so implementation has one normal discovery target. +Additional remote catalog management remains #229 work. ## Transcript 5: explain supported app @@ -244,7 +247,7 @@ Expected output: Git is supported. App ID: git -Source: built-in support from dotfiles-manager +Catalog: official State: not managed Can manage: @@ -256,19 +259,13 @@ Does not manage: [credential] sections include/includeIf expansion -Why this source is used: - Built-in support is the default for Git and is trusted by the - dotfiles-manager release. -No live values were printed. -No live settings were changed. -No stored settings were changed. ``` Design note: top-level `explain` may internally reuse existing `recipe explain` metadata, but default text must be app/tool-oriented rather than recipe-oriented. -Local-catalog candidate/collision explanation remains #228 work; this slice may -ship built-in support metadata only. +Additional remote catalog candidate/collision explanation remains #229 work; +this slice uses current official catalog metadata only. ## Transcript 6: explain unknown app @@ -287,8 +284,6 @@ Try: dotfiles-manager search missing dotfiles-manager list -No live settings were read or changed. -No stored settings were changed. ``` Expected behavior: @@ -354,16 +349,16 @@ cover at least the fields below. "schemaVersion": 1, "command": "list", "runId": "app-list", - "summary": {"status": "ok", "apps": 7, "managed": 1}, + "summary": {"status": "ok", "apps": 6, "managed": 1}, "apps": [ { "id": "git", "displayName": "Git", "aliases": ["gitconfig"], - "source": "built-in", + "catalog": "official", "state": "managed", "selectedSettings": 2, - "recipeRef": "recipe://bundled/git", + "recipeRef": "recipe://official/git", "trustStatus": "trusted", "supportLevel": "experimental", "capability": "read-write", @@ -377,7 +372,8 @@ cover at least the fields below. `state` is `managed` when at least one selected setting exists for the app in the active profile stack; otherwise it is `not-managed`. `list --json` exits `0` -when no settings folder exists and reports all built-in apps as `not-managed`. +when no settings folder exists and reports all official-catalog apps as +`not-managed`. ### `search --json` @@ -390,7 +386,7 @@ when no settings folder exists and reports all built-in apps as `not-managed`. "query": "git", "summary": {"status": "ok", "apps": 1, "managed": 0, "matches": 1}, "apps": [ - {"id": "git", "displayName": "Git", "source": "built-in"} + {"id": "git", "displayName": "Git", "catalog": "official"} ], "diagnostics": [] } @@ -412,11 +408,11 @@ A no-match search exits `0`, uses `summary.status: ok`, and returns "app": { "id": "git", "displayName": "Git", - "source": "built-in", - "sourceDescription": "built-in support from dotfiles-manager", + "catalog": "official", + "catalogVersion": "9f2c7a1", "state": "not-managed", "selectedSettings": 0, - "recipeRef": "recipe://bundled/git", + "recipeRef": "recipe://official/git", "trustStatus": "trusted", "supportLevel": "experimental", "capability": "read-write",