* fix(#2777): remove stale codex local home metadata * chore(#2777): add changeset for codex local layout metadata --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
5
.changeset/steady-eagles-cheer.md
Normal file
5
.changeset/steady-eagles-cheer.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 2831
|
||||
---
|
||||
**Codex local capability metadata now matches project-scoped installs** — Remove the inert user-home override from the local skills descriptor, document the global/local skill roots, and reject user-home overrides across all local artifact-layout entries. (#2777)
|
||||
@@ -38,8 +38,7 @@
|
||||
"prefix": "gsd-",
|
||||
"nesting": "flat",
|
||||
"recursive": false,
|
||||
"converter": "convertClaudeCommandToCodexSkill",
|
||||
"home": ".agents"
|
||||
"converter": "convertClaudeCommandToCodexSkill"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -108,7 +108,7 @@ Sources consulted:
|
||||
|
||||
**GSD integration status — Phase D dogfood complete (#2088, ADR-1239).** Codex installs through the `declarative` embedding adapter (`createDeclarativeAdapter` → `installRuntimeArtifacts`); the hardcoded `runtime === 'codex'`/`isCodex` projection is folded into descriptor-driven `runtime.hostBehaviors`, and install/uninstall output is byte-parity-gated at the time (`tests/fixtures/golden-install-parity/codex.json`; superseded by the differential attribution check, #2724). Three capability upgrades land, each with a test driving the user-reachable surface:
|
||||
|
||||
- **Skill root** — skills install to the canonical `$HOME/.agents/skills` (Codex core-skills `loader.rs` user-scope root), not the deprecated `$CODEX_HOME/skills` fallback. Declared via the skills-kind `home: ".agents"` override; pre-move installs are migrated (stale `~/.codex/skills/gsd-*` cleaned on both install and uninstall).
|
||||
- **Skill root** — global skills install to the canonical `$HOME/.agents/skills` (Codex core-skills `loader.rs` user-scope root), not the deprecated `$CODEX_HOME/skills` fallback; local skills install to `<project>/.codex/skills`. The global path is declared via the global skills-kind `home: ".agents"` override, while the local kind intentionally has no home override. Pre-move global installs are migrated (stale `~/.codex/skills/gsd-*` cleaned on both install and uninstall); local installs do not remove `$HOME/.agents/skills` because those skills may be intentionally global.
|
||||
- **Hook events** — GSD registers all documented `hooks.json` lifecycle events beyond `SessionStart`: `SubagentStart`, `Stop`, `PostToolUse` (#772), plus the six added in #2088 — `PreToolUse`, `PermissionRequest`, `PreCompact`, `PostCompact`, `SubagentStop`, `UserPromptSubmit` — all routed through `gsd-context-monitor.js`. (The descriptor `extendedHookEvents` field reflects the schema-valid cross-runtime subset `SubagentStop`/`Stop`/`PreCompact`; Codex's full event set is codex-hooks-json-native, registered directly in `hooks.json`.)
|
||||
- **Dispatch tuning** — `[agents] max_depth = 1` is written explicitly into the managed `config.toml` block, pinning the `dispatch.maxDepth: 1` axis instead of relying on codex-cli's implicit default. Because `maxDepth === 1`, `degradationFor` flattens GSD-hosted wave dispatch to single-level even though `dispatch.nested`/`background`/`backgroundDispatch` are all `true`. The block is a bare `[agents]` AgentsToml scalar table; it does **not** carry per-role `[agents.gsd-*]` sub-tables — those pointed `config_file` back at the standalone `agents/gsd-*.toml` files Codex already auto-discovers, so emitting them was a duplicate role registration (Codex logged "Ignoring malformed agent role definition: duplicate agent role name" once per agent) removed in #2406. `validateCodexConfigSchema` permits a known-scalar-only `[agents]` while still rejecting `[[agents]]` and unknown-key forms.
|
||||
|
||||
|
||||
@@ -1036,8 +1036,7 @@ const capabilities = {
|
||||
"prefix": "gsd-",
|
||||
"nesting": "flat",
|
||||
"recursive": false,
|
||||
"converter": "convertClaudeCommandToCodexSkill",
|
||||
"home": ".agents"
|
||||
"converter": "convertClaudeCommandToCodexSkill"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -5389,8 +5388,7 @@ const runtimes = {
|
||||
"prefix": "gsd-",
|
||||
"nesting": "flat",
|
||||
"recursive": false,
|
||||
"converter": "convertClaudeCommandToCodexSkill",
|
||||
"home": ".agents"
|
||||
"converter": "convertClaudeCommandToCodexSkill"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1075,7 +1075,18 @@ function validateArtifactLayout(capId, layout) {
|
||||
errors.push(ctx + '.' + scope + ' must be an array');
|
||||
} else {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
errors.push(...validateArtifactKindEntry(capId, arr[i], 'artifactLayout.' + scope + '[' + i + ']'));
|
||||
const entry = arr[i];
|
||||
errors.push(...validateArtifactKindEntry(capId, entry, 'artifactLayout.' + scope + '[' + i + ']'));
|
||||
if (
|
||||
scope === 'local' &&
|
||||
typeof entry === 'object' && entry !== null && !Array.isArray(entry) &&
|
||||
Object.prototype.hasOwnProperty.call(entry, 'home')
|
||||
) {
|
||||
errors.push(
|
||||
ctx + '.local[' + i + '].home is not allowed; ' +
|
||||
'local artifact layout entries must remain project-scoped',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3720,6 +3720,26 @@ describe('ADR-1016 phase 5a: validateArtifactLayout unit tests', () => {
|
||||
});
|
||||
assert.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test('#2777: home override is accepted globally and rejected locally', () => {
|
||||
const entry = {
|
||||
kind: 'skills',
|
||||
destSubpath: 'skills',
|
||||
prefix: 'gsd-',
|
||||
nesting: 'flat',
|
||||
recursive: false,
|
||||
converter: null,
|
||||
home: '.agents',
|
||||
};
|
||||
|
||||
assert.deepEqual(validateArtifactLayout('test', { global: [entry], local: [] }), []);
|
||||
|
||||
const errors = validateArtifactLayout('test', { global: [], local: [entry] });
|
||||
assert.ok(
|
||||
errors.some((error) => error.includes('artifactLayout.local[0].home') && error.includes('project-scoped')),
|
||||
'Expected local home override rejection, got: ' + JSON.stringify(errors),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 24d-extra. FIX 3: tightened validateRuntimeBody / validateConfigHome ──────
|
||||
|
||||
@@ -23,6 +23,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const { resolveRuntimeArtifactLayout, findInstallSourceRoot } = require('../gsd-core/bin/lib/runtime-artifact-layout.cjs');
|
||||
const capabilityRegistry = require('../gsd-core/bin/lib/capability-registry.cjs');
|
||||
const installProfiles = require('../gsd-core/bin/lib/install-profiles.cjs');
|
||||
const { install } = require('../bin/install.js');
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
@@ -121,6 +122,19 @@ describe('resolveRuntimeArtifactLayout — codex', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('keeps every built-in local artifact layout project-scoped (#2777)', () => {
|
||||
for (const [runtime, descriptor] of Object.entries(capabilityRegistry.runtimes)) {
|
||||
const localEntries = descriptor.runtime?.artifactLayout?.local ?? [];
|
||||
for (const entry of localEntries) {
|
||||
assert.strictEqual(
|
||||
Object.prototype.hasOwnProperty.call(entry, 'home'),
|
||||
false,
|
||||
`${runtime} local artifact layout entry '${entry.kind}' must not declare home`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('resolveRuntimeArtifactLayout — copilot', () => {
|
||||
test('returns correct layout for copilot', () => {
|
||||
const layout = resolveRuntimeArtifactLayout('copilot', FAKE_DIR);
|
||||
|
||||
Reference in New Issue
Block a user