AOI-3: skill non-removal invariant — doc and tests #732

Closed
opened 2026-05-02 11:01:46 +00:00 by claude-desktop · 0 comments
Collaborator

As an operator overriding skill behavior at instance scope, I want explicit confirmation that the skill name remains in the resolved capability surface even when overridden, so that I can rely on the type contract guarantee that "every dev has skill X" without reading resolver source code.

Acceptance criteria

Doc

  • docs/agents-architecture.md § resolver gains a short paragraph stating:
    • Skill rows have no enabled column by design.
    • Instance-scope skill row replaces body, never tombstones the name.
    • resolveSkill(name, ...) always returns a body when the name is set at any lower scope.
  • Caveat documented: instance can write empty body (effectively neuter the skill). This is an operator footgun, not policed by validation. Rationale: the invariant is "name still in surface", not "behavior unchanged".

Tests

  • Resolver unit test: type-scope skill foo + instance-scope foo with different body → instance body wins, resolveSkill('foo', ...) returns the instance body.
  • Resolver unit test: type-scope skill foo, no instance row → resolveSkill('foo', ...) returns the type body.
  • Resolver unit test: no rows at any scope → resolveSkill('foo', ...) returns null.

Code

  • No code change required (skill table already lacks enabled column; replace-only is current behavior).

Out of scope

  • Adding enabled / tombstone column to the skill table — would let instance disable skill, contradicts AOI invariant.
  • Validating non-empty skill body — operator footgun, not policed.

References

  • apps/server/src/domain/agent-config/resolver.ts resolveSkill
  • Sibling story AOI-6 cross-references this in the inheritance-contract doc.
As an operator overriding skill behavior at instance scope, I want explicit confirmation that the skill name remains in the resolved capability surface even when overridden, so that I can rely on the type contract guarantee that "every `dev` has skill X" without reading resolver source code. ## Acceptance criteria ### Doc - [ ] `docs/agents-architecture.md` § resolver gains a short paragraph stating: - Skill rows have no `enabled` column by design. - Instance-scope skill row replaces body, never tombstones the name. - `resolveSkill(name, ...)` always returns a body when the name is set at any lower scope. - [ ] Caveat documented: instance can write empty body (effectively neuter the skill). This is an operator footgun, not policed by validation. Rationale: the invariant is "name still in surface", not "behavior unchanged". ### Tests - [ ] Resolver unit test: type-scope skill `foo` + instance-scope `foo` with different body → instance body wins, `resolveSkill('foo', ...)` returns the instance body. - [ ] Resolver unit test: type-scope skill `foo`, no instance row → `resolveSkill('foo', ...)` returns the type body. - [ ] Resolver unit test: no rows at any scope → `resolveSkill('foo', ...)` returns `null`. ### Code - [ ] No code change required (`skill` table already lacks `enabled` column; replace-only is current behavior). ## Out of scope - Adding `enabled` / tombstone column to the `skill` table — would let instance disable skill, contradicts AOI invariant. - Validating non-empty skill body — operator footgun, not policed. ## References - `apps/server/src/domain/agent-config/resolver.ts` `resolveSkill` - Sibling story AOI-6 cross-references this in the inheritance-contract doc.
Sign in to join this conversation.
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
charles/claude-hooks#732
No description provided.