feat(seeder): context-file skills index for providers without native skill discovery (ADR-0035) #86

Merged
dominik.polakovics merged 1 commit from afk/79 into main 2026-07-10 01:10:44 +02:00

Seeds the SAME embedded skills bundle for every provider and, for a provider whose CLI has no native skill discovery, appends a generated skills index to its context file so the playbooks are discoverable through the one channel every agent reads. Also documents the lab-owned context-file-name hard rule.

Closes #79

What changed

  • SeedMeta.NativeSkillDiscovery bool (internal/provider/provider.go) — claude-code declares true (pinned in seedmeta_test.go; the providertest Fake mirrors it, NoLinkFake's zero-value false already models the skills-less shape).
  • Skills index render (internal/seeder/contextfile.go) — when a provider declares false with a non-empty SkillsDir, renderContextFile appends a ## Seeded skills section: one bullet per bundled skill from its frontmatter (name + description, which already carries the "use when…" trigger), pointing at <SkillsDir>/<name>/SKILL.md under THAT provider's declared dir. Appended in Go after the untouched template render, so a native-discovery provider's output can't drift structurally.
  • Frontmatter parsed once, fails loudly (internal/seeder/skillsindex.go) — strict parse of every bundled skill at package init (name must equal the bundle dir, description non-empty, folded scalars flattened to one line); a malformed bundle panics at init, caught by any test run and binary boot — never a silent partial index at seed time. go.yaml.in/yaml/v2 promoted indirect → direct (the bundle really contains a folded-scalar description).
  • Byte-identity pinned — pre-change renders of both tracker bindings captured from HEAD as goldens (internal/seeder/testdata/contextfile-claude-*.golden); tests assert the claude render and the on-disk seeded CLAUDE.local.md equal them byte-for-byte.
  • Hard rule documentedContextFileName doc comment + ADR-0035: the name must be lab-owned and never repo-tracked (.local-suffixed convention); making the CLI read it is the adapter's SeedWorkspace job (ADR-0008 bar); conformance enforcement is #80. CONTEXT.md's skills-bundle glossary entry updated.

Acceptance (issue #79)

  • Fake non-native provider (AGENTS.local.md + .codex/skills) seeds the full bundle and its context file lists every bundled skill with correct per-provider paths, worktree still clean under git statusTestSeedWorkspace_nonNativeAppendsSkillsIndex.
  • claude-code byte-identical: TestRenderContextFile_claudeGoldenByteIdentity (+ on-disk variant), goldens captured pre-change.
  • Empty SkillsDir → no index regardless of flag — TestRenderContextFile_noIndexWithoutSkillsDir.
  • Malformed/missing frontmatter fails at test time — init panic + 7 failure-mode cases in skillsindex_test.go.
  • Single go:embed source untouched; no curation list.

Verification

gofmt clean, go build ./..., go vet ./..., go test ./... -count=1 all green except internal/tmuxx integration tests, which fail identically at pristine HEAD in this sandbox (no usable tmux server) — pre-existing and unrelated.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JEHz335VrpFWsstLFoTePB

Seeds the SAME embedded skills bundle for every provider and, for a provider whose CLI has no native skill discovery, appends a generated skills index to its context file so the playbooks are discoverable through the one channel every agent reads. Also documents the lab-owned context-file-name hard rule. Closes #79 ## What changed - **`SeedMeta.NativeSkillDiscovery bool`** (`internal/provider/provider.go`) — claude-code declares `true` (pinned in `seedmeta_test.go`; the providertest `Fake` mirrors it, `NoLinkFake`'s zero-value `false` already models the skills-less shape). - **Skills index render** (`internal/seeder/contextfile.go`) — when a provider declares `false` with a non-empty `SkillsDir`, `renderContextFile` appends a `## Seeded skills` section: one bullet per bundled skill from its frontmatter (`name` + `description`, which already carries the "use when…" trigger), pointing at `<SkillsDir>/<name>/SKILL.md` under THAT provider's declared dir. Appended in Go after the untouched template render, so a native-discovery provider's output can't drift structurally. - **Frontmatter parsed once, fails loudly** (`internal/seeder/skillsindex.go`) — strict parse of every bundled skill at package init (name must equal the bundle dir, description non-empty, folded scalars flattened to one line); a malformed bundle panics at init, caught by any test run and binary boot — never a silent partial index at seed time. `go.yaml.in/yaml/v2` promoted indirect → direct (the bundle really contains a folded-scalar description). - **Byte-identity pinned** — pre-change renders of both tracker bindings captured from HEAD as goldens (`internal/seeder/testdata/contextfile-claude-*.golden`); tests assert the claude render and the on-disk seeded `CLAUDE.local.md` equal them byte-for-byte. - **Hard rule documented** — `ContextFileName` doc comment + ADR-0035: the name must be lab-owned and never repo-tracked (`.local`-suffixed convention); making the CLI read it is the adapter's `SeedWorkspace` job (ADR-0008 bar); conformance enforcement is #80. CONTEXT.md's skills-bundle glossary entry updated. ## Acceptance (issue #79) - Fake non-native provider (`AGENTS.local.md` + `.codex/skills`) seeds the full bundle and its context file lists every bundled skill with correct per-provider paths, worktree still clean under `git status` — `TestSeedWorkspace_nonNativeAppendsSkillsIndex`. - claude-code byte-identical: `TestRenderContextFile_claudeGoldenByteIdentity` (+ on-disk variant), goldens captured pre-change. - Empty `SkillsDir` → no index regardless of flag — `TestRenderContextFile_noIndexWithoutSkillsDir`. - Malformed/missing frontmatter fails at test time — init panic + 7 failure-mode cases in `skillsindex_test.go`. - Single `go:embed` source untouched; no curation list. ## Verification `gofmt` clean, `go build ./...`, `go vet ./...`, `go test ./... -count=1` all green except `internal/tmuxx` integration tests, which fail identically at pristine HEAD in this sandbox (no usable tmux server) — pre-existing and unrelated. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JEHz335VrpFWsstLFoTePB
feat(seeder): context-file skills index for providers without native skill discovery (ADR-0035)
All checks were successful
ci / native (pull_request) Successful in 5m37s
ci-nix / flake-check (pull_request) Successful in 16m31s
ddd715e967
The embedded skills bundle was seeded into every provider's declared
SkillsDir but only ever discovered through Claude Code's .claude/skills
convention — a context-file-only CLI (Codex, #2) got the bytes on disk and
no way to learn they exist, so none of the workflow playbooks that carry
AFK quality.

SeedMeta gains NativeSkillDiscovery (claude-code: true). When a provider
declares false with a non-empty SkillsDir, renderContextFile appends a
generated skills index: one line per bundled skill, rendered from its YAML
frontmatter (name + description — the descriptions already carry the "use
when…" triggers), pointing at <SkillsDir>/<name>/SKILL.md. The frontmatter
is parsed once at seeder package init and panics on a malformed bundle
(build/test-time failure, never a silent partial index at seed time);
go.yaml.in/yaml/v2 is promoted to a direct dependency for the folded-scalar
descriptions the bundle actually contains.

The bundle stays a single go:embed source — same content for every
provider, no curation list; only discovery differs. Claude-code's seeding
stays byte-identical: the section is appended in Go after the untouched
template render, and pre-change renders of both tracker bindings are pinned
as goldens (internal/seeder/testdata/contextfile-claude-*.golden).

SeedMeta.ContextFileName now documents the hard rule: the name must be
lab-owned and never repo-tracked (the .local-suffixed convention) — a
tracked name (codex's native AGENTS.md) can't be hidden by
.git/info/exclude, is clobbered on re-seed, dirties every diff, and breaks
incogni. Making the CLI read the lab-owned file is the adapter's job in its
SeedWorkspace (ADR-0008 bar); the conformance suite (#80) will assert the
convention. ADR-0035 records all of it; the CONTEXT.md skills-bundle
glossary entry now reflects the generic reality.

Closes #79

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JEHz335VrpFWsstLFoTePB
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab!86
No description provided.