Repository navigation
Conversation
- `scopes:` in techpack.yaml (absent = both); validate errors on bad values, sync strips them with a warning - Configurator rejects adding a pack to an unsupported scope before any change; --all skips such packs, the picker hides them - Doctor warns on a pack already installed in an unsupported scope; validate warns on project-only features without `scopes: [project]`
- `scopes:` field, validation rule and project-only-features heuristic in the schema reference - When to declare `scopes: [project]` in the pack authoring guide - Scope-mismatch behavior for sync and doctor in the CLI reference and CLAUDE.md
- `supportedScopes` has no protocol default, so a wrapper that forgets to forward it no longer compiles - `--all` and the global-block filter share one skip notice; the scope lives once on SyncScope - Project-only placeholder keys come from project sync's own built-in key list
There was a problem hiding this comment.
🟡 Changes recommended
Scope validation can be bypassed before destructive pruning, and doctor can miss unsupported global installations while removing their supported project copies.
2 open issues
What changed in this PR
Adds pack-declared scope support to the shared configuration engine, preventing new installations into unsupported scopes while preserving existing installations. This also provides a foundation for global bootstrap support.
Changes:
- Adds manifest scope declarations, validation, runtime sanitization, and scope-aware pack selection.
- Adds doctor warnings and validation heuristics for scope incompatibilities.
- Adds lifecycle coverage and documents scope behavior.
| File | Description |
|---|---|
| Tests/MCSTests/TestHelpers.swift | Adds scope support to shared test doubles. |
| Tests/MCSTests/ResourceRefCounterTests.swift | Updates the pack stub for scope support. |
| Tests/MCSTests/PackHeuristicsTests.swift | Tests project-only placeholder warnings. |
| Tests/MCSTests/LifecycleIntegrationTests.swift | Covers scope rejection, filtering, and legacy installations. |
| Tests/MCSTests/ExternalPackManifestTests.swift | Tests scope defaults, validation, and sanitization. |
| Tests/MCSTests/DestinationCollisionResolverTests.swift | Updates the wrapper test double. |
| Tests/MCSTests/CrossPackPromptResolverTests.swift | Updates the prompt test double. |
| Tests/MCSTests/CLAUDELocalFreshnessCheckTests.swift | Updates freshness-check test doubles. |
| Sources/mcs/TechPack/TechPack.swift | Defines pack scopes and requires scope declarations. |
| Sources/mcs/Sync/SyncScope.swift | Represents the target scope explicitly. |
| Sources/mcs/Sync/ProjectSyncStrategy.swift | Centralizes project-only placeholder keys. |
| Sources/mcs/Sync/PackScopeError.swift | Reports unsupported pack selections. |
| Sources/mcs/Sync/DestinationCollisionResolver.swift | Forwards wrapped packs’ supported scopes. |
| Sources/mcs/Sync/ConfiguratorSupport.swift | Adds scope filtering and shared skip reporting. |
| Sources/mcs/Sync/Configurator.swift | Enforces scope support and filters picker options. |
| Sources/mcs/ExternalPack/PackHeuristics.swift | Warns about project-only features in global-capable packs. |
| Sources/mcs/ExternalPack/ExternalPackManifest.swift | Adds scope parsing, validation, and sanitization. |
| Sources/mcs/ExternalPack/ExternalPackLoader.swift | Applies runtime scope sanitization. |
| Sources/mcs/ExternalPack/ExternalPackAdapter.swift | Exposes manifest-supported scopes. |
| Sources/mcs/Doctor/PackScopeCheck.swift | Defines unsupported-installation diagnostics. |
| Sources/mcs/Doctor/DoctorRunner.swift | Collects scope-support checks. |
| Sources/mcs/Commands/SyncCommand.swift | Filters unsupported additions for --all. |
| docs/techpack-schema.md | Documents the scope field and validation rules. |
| docs/creating-tech-packs.md | Explains when authors should restrict scopes. |
| docs/cli.md | Documents sync rejection and doctor warnings. |
| CLAUDE.md | Updates architecture and scope-design guidance. |
Review effort: Balanced
Comment on lines
+248
to
+250
| if !pack.supportedScopes.contains(scope.packScope) { | ||
| allChecks.append((check: PackScopeCheck(packID: pack.identifier, scope: scope.packScope), syncTarget: nil)) | ||
| } |
7 tasks
- A `scopes:` list with no valid value supports no scope instead of both; publish-time validation stays strict - Doctor reports unsupported installs from each scope's state, and won't drop a supported project copy to keep an unsupported global one - Named packs hit the scope check before the global-block filter; errors and the picker say where a pack can go
- Merge nested ifs and repeated guards the newer nestedIf/repeatedGuard rules flag across the repo
…nt alike - Hoist the multi-line call out of each merged guard; behavior is unchanged
Comment on lines
+339
to
+343
| let rejected = ConfiguratorSupport.scopeUnsupported( | ||
| requested, scope: configurator.packScope, previouslyConfigured: previouslyConfigured | ||
| ) | ||
| if !rejected.isEmpty { | ||
| throw PackScopeError(packs: rejected, scope: configurator.packScope) |
Collaborator
Author
There was a problem hiding this comment.
- `scopes` is a `let` like every other manifest root field, so a rebuild that forgets it no longer widens the pack to both scopes - Loader test: runtime sanitizing keeps declared scopes, strict validation rejects an unknown one
- Adding a project-only pack globally throws before any hook file or settings entry is written
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
A pack that only works in a project could be installed globally, where its configure script never runs and
__REPO_NAME__stays unresolved in~/.claude/CLAUDE.md. Packs can now declare which scopes they support, and sync refuses to add them anywhere else. #411 (scope-routed bootstrap) builds on this.Closes #457
Release note
scopes: [project]orscopes: [global]intechpack.yaml;mcs syncrefuses to add a pack to a scope it doesn't support, and--allskips itmcs doctorwarns about a pack installed in a scope it no longer supportsmcs pack validatewarns when a pack uses project-only features but allows global scopeChanges
scopes:manifest field; absent means both scopesmcs updateanddoctor --fixkeep re-applying a pack installed before it declaredscopes:--packand--dry-runexit non-zero on a mismatch;--allskips with a warning; the picker hides those packsscopes:values failmcs pack validatebut are dropped with a warning at sync timeTest plan
swift testpasses locallyswiftformat --lint .andswiftlintpass without violationsmcs sync,mcs doctor)scopes: [project]:mcs sync --global --pack <id>exits non-zero and~/.claudeis unchanged__REPO_NAME__in a template and noscopes::mcs pack validatewarnsChecklist for engine changes
LifecycleIntegrationTestsorDoctorRunnerIntegrationTests)CLAUDE.md,docs/,techpack.yamlschema inExternalPackManifest.swift)