Commit cf306c9
fix(skills): route [[skill]] + plugin skills natively for Codex/Gemini (ADR-012) (#67)
* fix(skills): route [[skill]] + plugin skills natively for Codex/Gemini (ADR-012)
PR #65 fixed only the SNAPSHOT path (agents_skills/, gemini_skills/). The two
other ways a skill reaches an agent — [[skill]] by-reference in variant.toml and
a [nasde.plugin]'s own skills/ — still went through stage_skill_dir's hardcoded
/app/.claude/skills/ target. Correct for Claude (cwd discovery root), but for a
Codex/Gemini variant the skill landed where the CLI never scans, so it was never
natively registered — the same silent "skill-as-document" failure, just via a
different path. Uncaught today (no non-Claude variant used [[skill]]/plugin) but
a latent trap for the core skill-migration use case.
Both paths now resolve to skill DIRECTORIES (plugin_registration.collect_
referenced_skill_dirs / collect_plugin_skill_dirs) and, for Codex/Gemini, route
through Harbor's native config.agent.skills — unioned with the snapshot dirs
inside _refresh_agent_skills under one _nasde_derived_skills marker (stale-drop
and hand-authored preservation work across both sources). Claude keeps its
unchanged sandbox_files path; a Claude agent drops extra_skill_dirs at merge to
avoid double-injecting. The frontmatter warning now fires on this path too, and
a new warning flags two derived dirs sharing a basename (Harbor resolve_skills
is last-wins, so the loser would silently vanish).
New example variants codex-nasde-dev-with-arch and gemini-nasde-dev-with-arch
exercise [[skill]] by-reference on a non-Claude agent (and add the long-missing
Codex variant to nasde-dev-skill).
Config verified: the codex variant's generated harbor_config carries
skills=[nasde-dev] (native) with the [[skill]] dir, not just the Claude sandbox
entry. Full suite 393 passing; ruff/format/mypy clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(skills): normalize Rich-wrapped warning output (Windows CI)
The frontmatter and basename-collision warning assertions matched a literal
substring against capsys output, but Rich wraps the long tmp-path line on
Windows' narrow CI console, splitting 'missing YAML frontmatter' across a
newline. Normalize with ' '.join(out.split()) like the existing collision
tests. Verified with COLUMNS=80 locally.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(skills): resolve [[skill]]/plugin dirs so dedup matches snapshot (review)
Code-review finding: `_collect_native_skill_dirs` returns `str(dir.resolve())`
(resolved), but the new native channel did NOT resolve — `collect_plugin_skill_
dirs` returned `staged.staged_dir / name` and `_resolve_skill_source`'s ref
branch returned `worktree / relative`, both unresolved. In `_refresh_agent_
skills` the union/dedup, `_nasde_derived_skills` stale-drop, handwritten
preservation and basename-collision warning all key on string equality, so the
same logical dir in two string forms (a symlinked /tmp on macOS, or a ref
worktree) would fail to dedup, break stale-drop across runs, and fire a spurious
collision warning. Both collect_* now `.resolve()` their dirs to match the
snapshot path. Adds canonical-path regression tests.
Also corrected a misleading comment claiming create_ref_worktree caches/dedups
ref worktrees — it does not; the lifecycle is safe because all worktrees live in
_active_worktrees until cleanup_worktrees() in the finally after the job.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(skills): use dict.fromkeys for order-preserving dedup (review)
Replace the hand-rolled _dedup_preserving_order helper with the stdlib idiom
list(dict.fromkeys(...)) — same behavior, one line, more discoverable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(skills): integration coverage for [nasde.plugin] skills routing
The [[skill]] path is verified e2e on a live agent, but there is no example
with a [nasde.plugin], so the plugin path had only unit coverage on a synthetic
StagedPlugin. Add two integration tests that drive the REAL ensure_task_plugin
staging through _prepare_task_environments → collect_plugin_skill_dirs →
harbor_config: a plugin's own skills/ lands in native config.agent.skills
(resolved) for a Codex variant, and in /app/.claude/skills sandbox_files for a
Claude variant. Closes the 'does collect_plugin_skill_dirs read the right staged
location' gap without Docker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(adr-012): note plugin path is integration-tested, not e2e (honest coverage)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(skills): OS-agnostic basename assert in plugin test (Windows CI)
assert s.endswith('/plug-skill') hardcoded a forward slash and failed on
Windows where the resolved host path uses backslashes. Use Path(s).name ==
'plug-skill' instead. (The /app/.claude/... sandbox assertion is a container
posix path and stays as-is.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Szymon Janikowski <szymon.janikowski@itlibrium.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent 76016bd commit cf306c9
12 files changed
Lines changed: 585 additions & 47 deletions
File tree
- .claude/skills/nasde-benchmark-creator
- docs/adr
- examples/nasde-dev-skill/variants
- codex-nasde-dev-with-arch
- gemini-nasde-dev-with-arch
- src/nasde_toolkit
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
336 | 336 | | |
337 | 337 | | |
338 | 338 | | |
339 | | - | |
340 | | - | |
341 | | - | |
342 | | - | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
343 | 347 | | |
344 | 348 | | |
345 | 349 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
107 | 124 | | |
108 | 125 | | |
109 | 126 | | |
| |||
522 | 539 | | |
523 | 540 | | |
524 | 541 | | |
| 542 | + | |
525 | 543 | | |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
97 | 127 | | |
98 | 128 | | |
99 | 129 | | |
| |||
123 | 153 | | |
124 | 154 | | |
125 | 155 | | |
126 | | - | |
127 | | - | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
128 | 161 | | |
129 | 162 | | |
130 | 163 | | |
131 | 164 | | |
132 | 165 | | |
133 | 166 | | |
134 | 167 | | |
135 | | - | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
136 | 173 | | |
137 | 174 | | |
138 | 175 | | |
139 | | - | |
140 | | - | |
| 176 | + | |
| 177 | + | |
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
19 | 25 | | |
20 | 26 | | |
21 | 27 | | |
| |||
119 | 125 | | |
120 | 126 | | |
121 | 127 | | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
122 | 176 | | |
123 | 177 | | |
124 | 178 | | |
| |||
0 commit comments