Skip to content

Commit b9c3e26

Browse files
committed
feat(skills): workspace W>G resolve with reserved core protection
Add hooks/_runtime/skill-resolution.cjs as the single Owner for workspace-namespace skills over global ~/.agents/skills, block reserved skill ids from W override, bind BundleDecision selected identity via resolutionPlan, carve workspace skills out of managed/control-plane paths, expose devcodex skill resolve, and gate with test:skill-resolve plus validation-manifest nodes on fast/full routes.
1 parent 1eae34e commit b9c3e26

13 files changed

Lines changed: 1161 additions & 33 deletions

‎README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,13 @@ GlobalOnlyHostConfigModeV1 与 GlobalOnlyWorkspaceCleanModeV1 将宿主配置、
203203
204204
<workspace>/
205205
└── .devcodex/ ← workspace-namespace 的 Profile、记忆、报告与运行态
206+
└── workspace/
207+
├── profile/ ← 事实 base
208+
└── skills/ ← 可选自定义 Skill(W;非 reserved 时 W>G,见 skill-resolution)
206209
```
207210

211+
自定义 Skill 只写 `<workspace>/.devcodex/workspace/skills/<id>/SKILL.md`(一次配置,经 DevCodex resolve 五宿主共用);**不要**改 `~/.agents/skills` 或五宿主 home 树作扩展。强制核 skill(如 `compliance`/`cp-gate`)禁止 W 覆盖。诊断:`devcodex skill resolve <id> --json`;探针:`npm run test:skill-resolve`。
212+
208213
bare `devcodex init/update` 只管理 `.devcodex`。若当前路径没有可复用的祖先 workspace marker,且不存在旧版项目运行态,首次执行会原子创建 `.devcodex/layout.json` 并采用 `workspace-namespace`;若祖先已存在有效 marker,则沿用祖先 owner;本地 marker 非法时返回 `WORKSPACE_LAYOUT_INVALID`,旧 `.devcodex/profile`、`.memory` 等运行态存在时返回 `WORKSPACE_LAYOUT_MIGRATION_REQUIRED`,不会静默切换目录,需先执行 `devcodex migrate-layout plan/apply`。`devcodex init --claude`、`devcodex init --codex`、`--host`、`--gemini`、`--grok`、裸 `devcodex uninstall` 以及 `uninstall --host grok` 首批均返回结构化错误 `CLI_HOST_CONFIG_GLOBAL_ONLY`。安装与更新错误会指向 npm 全局命令;卸载错误会明确首批不支持自动删除用户级配置。现有工作区宿主文件不会被自动迁移、覆盖或删除;删除前仍需用户明确确认。
209214

210215
Grok 全局刷新会先识别所有同名 plugin identity:受管 canonical、已知 legacy 与受管恢复来源会经官方 `grok plugin uninstall/install` 收敛为一个 canonical identity;任意未知同名来源会在写入前阻断。收敛前保留配置、registry 证据及 source/installed 字节快照,不直接改写 Grok registry,也不自动删除旧工作区 source。

‎changelogs/unreleased.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
## 当前未发布实现候选
77

8+
- **工作区 Skill 双层 resolve(S2 / W>G)**:新增 `hooks/_runtime/skill-resolution.cjs`(唯一 Owner):`workspace-namespace` 下 `.devcodex/workspace/skills` 优先于 `~/.agents/skills`,**reserved** skill id 禁止 W 覆盖;同次 digest/content 契约;`profile_skill_plan` / `BundleDecisionV2` 附加 `resolutionPlan` 并改写 selected source identity;Hook 对 workspace-skill 做 managed/control-plane carve-out;CLI `devcodex skill resolve`;`npm run test:skill-resolve`(含 hook 路径矩阵;并入 `test:control-plane`);`validation-manifest` 节点 `skill-resolve` + `skill-resolution-hook-paths` 进入 fast/full/profile-deploy/package-release;apply 目标禁入 W 树。需求:`双层Skill用户优先与意图加载` CP2 v1.2 S2 / CP3。
89
- **SourceExistenceVerificationGate(规范吸纳防复发)**:`spec-absorption` 强制在输出「可吸纳」前对 source-root 做同名/近邻存在性验证;台账 `open/pending absorption` 不得单独成 absorb。`AbsorptionCandidateMatrixV1` 增加 `sourceExistence`;planner 阻断 `unverified` 与 `present/equivalent-covered` 的 absorb;并列「可关账清单」。负向:ledger-status-only。验证:`node scripts/plan-absorption-candidates.js --self-test`、`npm run test:residual-absorption-controls`、V81 needles。
910
- **ExecutableAbsorption + ProbeNecessity(假吸纳禁止)**:吸纳必须可执行——`enforcementLevel` 不得为 checklist-only/none;每条 absorb 必填 `probeNecessity`(required/conditional/not-required/forbidden、alwaysOnImpact、复杂度与误报)。只写 Skill 正文或「以后再补探针」不得 ready/absorbed。分类器 `classifyExecutableAbsorptionSample`。验证:planner self-test + residual + V81。
1011
- **可执行吸纳主包 A 实施(R1~R4 + T2)**:`ProgressReportFastPath`(skill-gap/compliance/SC16);`TaskPhaseProjectionGate`(cp-gate/user-visible-output);`ClosedArtifactNoReviveGate`(cp-gate);`FormalRerunLightClassify` + `FindingObjectLayer`(consumer-validation)。探针:`scripts/lib/executable-absorption-gates.js` + `npm run test:executable-absorption-gates`(并入 host-parity / control-plane)。

‎hooks/_runtime/lifecycle.cjs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,15 @@ function toControlPlaneRelPath(target) {
861861

862862
function isControlPlaneSourcePath(target, state) {
863863
if (!target || isDevCodexManagedPath(target, state)) return false
864+
// Workspace custom skills must never be treated as package control-plane skills/
865+
try {
866+
const { isWorkspaceSkillPath } = require('./skill-resolution.cjs')
867+
if (isWorkspaceSkillPath(resolveRelativeToContext(target), { cwd: CONTEXT_ROOT || WORKSPACE_ROOT })) {
868+
return false
869+
}
870+
} catch {
871+
/* skill-resolution optional during partial deploys */
872+
}
864873
const rel = toControlPlaneRelPath(target)
865874
return CONTROL_PLANE_SOURCE_RE.test(rel)
866875
}
@@ -943,6 +952,15 @@ function isActiveDevCodexNamespacePath(target, state) {
943952

944953
function isDevCodexManagedPath(target, state) {
945954
if (!target) return false
955+
// Carve-out: workspace skills are user-editable extensions, not managed G/runtime state
956+
try {
957+
const { isWorkspaceSkillPath } = require('./skill-resolution.cjs')
958+
if (isWorkspaceSkillPath(resolveRelativeToContext(target), { cwd: CONTEXT_ROOT || WORKSPACE_ROOT })) {
959+
return false
960+
}
961+
} catch {
962+
/* optional */
963+
}
946964
const rel = toWorkspaceRelativePath(target)
947965
if (DEVCODEX_DEPLOYMENT_PATH_RE.test(rel)) return true
948966
return isActiveDevCodexNamespacePath(target, state)

0 commit comments

Comments
 (0)