You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support project-scoped profile/workflow config via --scope project #914
The issue correctly identifies the key files. Here are the exact locations:
Scope gate (the placeholder to remove):
src/commands/config.ts:214-220 — preAction hook rejects non-global scope with "Error: Project-local config is not yet implemented". This is the explicit placeholder for this feature.
Global config storage:
src/core/global-config.ts:35-56 — getGlobalConfigDir() resolves to $XDG_CONFIG_HOME/openspec/ or ~/.config/openspec/
/opsx-archive can offer 'Sync now' even when openspec-sync-specs is not installed (core profile) #913 (open) — /opsx-archive offers "Sync now" even when openspec-sync-specs is not installed (core profile). This is a concrete example of cross-project coupling: changing the global profile to add sync for one project would affect all other projects. Project-scoped config would let individual repos opt into sync without global side effects.
Introduce --profile expanded to init compatibly with 1.1.x #779 (open) — Requests --profile expanded for init. The pain point is that the global profile is volatile and not writable in some environments. Project-scoped config would solve this by storing profile selection in-repo.
The project config file already exists and is read by readProjectConfig() at src/core/project-config.ts:66-161. The natural place to store project-scoped profile/delivery/workflows is to extend this file:
This could live in a new file like src/core/effective-config.ts or be added to src/core/global-config.ts.
3. Update src/core/update.ts:96-106
Replace the direct getGlobalConfig() call with the new effective config resolver. The update command already receives resolvedProjectPath (line 82), so it has access to the project root for reading project config.
4. Update config command (src/commands/config.ts)
Remove the preAction scope gate at lines 214-220
For --scope project: read/write to openspec/config.yaml instead of ~/.config/openspec/config.json
config list should show effective (merged) config with source annotations (e.g., (project), (global), (default))
config profile --scope project should write to project config
This function currently takes only GlobalConfig. It would need to accept the effective merged config or take both global and project configs.
Key consideration: The config set/unset commands use src/core/config-schema.ts:44-68 for key validation, which currently only allows featureFlags, profile, delivery, workflows. For project scope, the same keys should be valid, but they need to be written to the YAML project config instead of the JSON global config.
Testing Considerations
Verify that openspec update picks up project config when present and falls back to global when absent
Verify that config list correctly shows source annotations
Verify that modifying project config does not affect global config
Verify that projects without project-scoped config are completely unaffected (backwards compatibility)
Edge case: what happens when project config has profile: core but also has workflows: [sync, propose]? The getProfileWorkflows() function at src/core/profiles.ts:42-50 ignores custom workflows for the core profile — this interaction needs to be defined clearly.
We're hitting this exact problem. We have a shared repo where we want a specific subset of workflows (9 of the 11), but since config is global-only, each developer has to configure their machine individually. This makes onboarding friction and causes drift when someone has a different profile set globally.
Would you be open to a community PR for this? Happy to contribute but wanted to check before investing the effort.
Activity
21st-connector commentedon Apr 3, 2026
🤖 Automation triggered: Enrich Github Issue
I'm starting to work on this automatically.
View live progress
Powered by 1Code
21st-connector commentedon Apr 3, 2026
Issue Analysis
Codebase References
The issue correctly identifies the key files. Here are the exact locations:
Scope gate (the placeholder to remove):
src/commands/config.ts:214-220—preActionhook rejects non-global scope with"Error: Project-local config is not yet implemented". This is the explicit placeholder for this feature.Global config storage:
src/core/global-config.ts:35-56—getGlobalConfigDir()resolves to$XDG_CONFIG_HOME/openspec/or~/.config/openspec/src/core/global-config.ts:92-94—getGlobalConfigPath()returns[configDir]/config.jsonsrc/core/global-config.ts:101-139—getGlobalConfig()loads and deep-merges with defaultssrc/core/global-config.ts:145-155—saveGlobalConfig()writes the full configConfig schema & validation:
src/core/config-schema.ts:7-25— Zod schema definingGlobalConfigshape (profile,delivery,workflows,featureFlags), uses.passthrough()for forward compatibilitysrc/core/config-schema.ts:44-68—validateConfigKeyPath()restricts valid key paths to known top-level keysUpdate workflow resolution (reads global-only today):
src/core/update.ts:96-106— ReadsgetGlobalConfig(), resolves profile/delivery/workflows, derivesdesiredWorkflowsand delivery flagssrc/core/update.ts:139-144—getToolsNeedingProfileSync()compares desired state against installed artifactsProfile system:
src/core/profiles.ts:14—CORE_WORKFLOWS = ['propose', 'explore', 'apply', 'archive']src/core/profiles.ts:19-31—ALL_WORKFLOWS(11 total)src/core/profiles.ts:42-50—getProfileWorkflows()resolves workflows by profile typeProfile drift detection:
src/core/profile-sync-drift.ts:14-26—WORKFLOW_TO_SKILL_DIRmappingsrc/core/profile-sync-drift.ts:166-176—getToolsNeedingProfileSync()src/core/profile-sync-drift.ts:218-240—hasProjectConfigDrift()— checks all tools for driftExisting project config (for context/rules only, not profile):
src/core/project-config.ts:19-41—ProjectConfiginterface:schema,context,rulessrc/core/project-config.ts:66-161—readProjectConfig()readsopenspec/config.yamlConfig command subcommands affected:
config list—src/commands/config.ts:231-268config get—src/commands/config.ts:271-288config set—src/commands/config.ts:291-330config unset—src/commands/config.ts:332-346config profile—src/commands/config.ts:452-644(interactive profile wizard)Related Issues & Prior Art
Directly referenced:
/opsx-archiveoffers "Sync now" even whenopenspec-sync-specsis not installed (core profile). This is a concrete example of cross-project coupling: changing the global profile to addsyncfor one project would affect all other projects. Project-scoped config would let individual repos opt intosyncwithout global side effects.--profile expandedto init compatibly with 1.1.x #779 (open) — Requests--profile expandedfor init. The pain point is that the global profile is volatile and not writable in some environments. Project-scoped config would solve this by storing profile selection in-repo.Related closed issues/PRs (precedence model):
--useroption toopenspec schema initfor user-level schemas #756 (closed) — Added--useroption for user-level schemas, addressing the same global-vs-project tension for schemas.openspec init --globalfor tool global directory installation #753 (closed, not merged) — Attemptedopenspec init --globalfor tool global directory installation — different direction but same underlying tension.Foundational PRs:
src/core/global-config.ts)core/customprofile system and delivery configopenspec updatebased on profile drift detectionOther related open issues:
config.yamlsupport for custom workflow rules (inverse direction: global rules that apply to all projects)config.yamlNot a duplicate. No existing issue or PR addresses project-scoped profile/delivery/workflow config specifically.
Existing Precedence Pattern
The codebase already implements a multi-level precedence model for schemas (
docs/opsx.md:110-114,docs/customization.md:83-90):--schema <name>).openspec.yaml)openspec/config.yaml)spec-driven)The proposed profile/workflow precedence model mirrors this existing pattern, which is a good sign for consistency.
Implementation Approach
Based on codebase analysis, here is a suggested approach:
1. Extend
openspec/config.yaml(project-config.ts)The project config file already exists and is read by
readProjectConfig()atsrc/core/project-config.ts:66-161. The natural place to store project-scoped profile/delivery/workflows is to extend this file:This requires:
ProjectConfiginterface (src/core/project-config.ts:19-41) with optionalprofile,delivery,workflowsfieldsreadProjectConfig()to validate these new fields2. Create an effective config resolver
A new function (e.g.,
resolveEffectiveConfig()) that merges configs with the proposed precedence:openspec/config.yamlprofile/delivery/workflows)~/.config/openspec/config.json)core,both)This could live in a new file like
src/core/effective-config.tsor be added tosrc/core/global-config.ts.3. Update
src/core/update.ts:96-106Replace the direct
getGlobalConfig()call with the new effective config resolver. The update command already receivesresolvedProjectPath(line 82), so it has access to the project root for reading project config.4. Update config command (
src/commands/config.ts)preActionscope gate at lines 214-220--scope project: read/write toopenspec/config.yamlinstead of~/.config/openspec/config.jsonconfig listshould show effective (merged) config with source annotations (e.g.,(project),(global),(default))config profile --scope projectshould write to project config5. Update
resolveCurrentProfileState()(src/commands/config.ts:99-108)This function currently takes only
GlobalConfig. It would need to accept the effective merged config or take both global and project configs.Key consideration: The
config set/unsetcommands usesrc/core/config-schema.ts:44-68for key validation, which currently only allowsfeatureFlags,profile,delivery,workflows. For project scope, the same keys should be valid, but they need to be written to the YAML project config instead of the JSON global config.Testing Considerations
openspec updatepicks up project config when present and falls back to global when absentconfig listcorrectly shows source annotationsprofile: custom+workflows: [sync]overrides globalprofile: coreprofile: corebut also hasworkflows: [sync, propose]? ThegetProfileWorkflows()function atsrc/core/profiles.ts:42-50ignores custom workflows for thecoreprofile — this interaction needs to be defined clearly.jamessawle commentedon Apr 24, 2026
We're hitting this exact problem. We have a shared repo where we want a specific subset of workflows (9 of the 11), but since config is global-only, each developer has to configure their machine individually. This makes onboarding friction and causes drift when someone has a different profile set globally.
Would you be open to a community PR for this? Happy to contribute but wanted to check before investing the effort.