@skill-map/spec 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Spec changelog
2
2
 
3
+ ## 0.33.0
4
+
5
+ ### Minor Changes
6
+
7
+ - da26519: Provider-aware confidence bump for resolved invocation links. Three changes ship together:
8
+
9
+ 1. **`core/markdown-link` references emit at confidence `1.0`** (previously `0.95`). The `[text](path)` syntax is unambiguous, there is no degree of inference left to discount; whether the path resolves is a separate concern owned by the `core/broken-ref` analyzer.
10
+
11
+ 2. **New `IProviderKind.identifiers`** declaring how to derive a kind's canonical invocation handle(s). Closed set: `'frontmatter.name'`, `'filename-basename'`, `'dirname'`. Multiple sources accumulate (built-in Anthropic skills declare `['frontmatter.name', 'dirname']` so a skill without an explicit `name:` still resolves via the directory between `.claude/skills/` and `/SKILL.md`, matching https://code.claude.com/docs/en/skills.md).
12
+
13
+ 3. **New `IProvider.resolution: Record<linkKind, targetKind[]>`** declaring the strict per-link-kind matrix the post-walk transform consults. `claude` ships `{ mentions: ['agent'], invokes: ['command', 'skill'] }`; `gemini` ships `{ mentions: ['agent'], invokes: ['skill'] }`; `openai` `{ mentions: ['agent'] }`; `agent-skills` `{ invokes: ['skill'] }`. A `/foo` slash matching an agent named `foo` does NOT bump because `invokes` excludes `agent` (the link-conflict / kind-mismatch analyzers handle that case separately).
14
+
15
+ The kernel renames `liftMentionConfidence` → `liftResolvedLinkConfidence` and generalises the rule to cover `mentions`, `invokes`, and `references` uniformly. Path-match (target equals a node path) still applies universally; name-match goes through the source Provider's resolution map. `broken-ref`'s scope (kind-agnostic "name exists somewhere") stays unchanged.
16
+
17
+ Spec wording: new `§Provider · kind identifiers` and `§Provider · resolution rules` sections in `spec/architecture.md`.
18
+
19
+ ## User-facing
20
+
21
+ **Invocation links to resolved targets now render at full confidence.** `@reviewer` mentions and `/explore` slash commands that match an existing agent / skill / command in your project show up at confidence `1.0` (previously `0.5` / `0.8`), so the graph no longer dims them.
22
+
23
+ ## 0.32.1
24
+
25
+ ### Patch Changes
26
+
27
+ - 4af662b: Loosen the active-provider lens gate to lens-only: per-provider extractors run on every visited node when the active lens is in the extractor's declared `precondition.provider` allowlist, regardless of which provider classified the node.
28
+
29
+ The previous gate (shipped in 0.34.0) double-checked `nodeProvider AND activeProvider`. That broke a real surface: a `@handle` in `CLAUDE.md` or `notes/todo.md` (files the `claude` provider disclaims to `core/markdown` because markdown is provider-agnostic) never got parsed under the `claude` lens, because the node's provider was `core`, not `claude`. The runtime grammar the lens represents applies across every markdown surface, not only the files the provider's `classify()` owns, so the lens is the single discriminator. Cross-lens isolation is preserved by the lens half alone: under `gemini`, claude extractors are silent on every node (including `.claude/*`), because the lens authorisation is missing.
30
+
31
+ Spec wording in `spec/architecture.md` §Universal extractors and per-provider extractors updated to match. `matchesProviderPrecondition` signature simplified to `(ex, activeProvider)`; the `provider` field is removed from `computeCacheDecision` opts. Unit tests rewritten with the lens-only matrix.
32
+
33
+ ## User-facing
34
+
35
+ **`@handle` and `/command` now resolve outside `.claude/`.** Under the Claude lens, mentions and invokes in `CLAUDE.md`, `notes/*.md`, and any markdown across the project are picked up as Claude edges. Switching lens hides them so the graph mirrors the active runtime.
36
+
3
37
  ## 0.32.0
4
38
 
5
39
  ### Minor Changes
package/architecture.md CHANGED
@@ -61,7 +61,9 @@ A provider plugin MAY declare it reads source files belonging to ANOTHER provide
61
61
 
62
62
  ### Universal extractors and per-provider extractors
63
63
 
64
- The lens does NOT gate the universal extractors that ship under `core/` (markdown links, external URLs, sidecar annotations). Those run regardless of the active provider because their semantics are provider-agnostic. Provider-specific extractors (Claude's `@`-directive parser, Gemini's three-surface `@`-parsers, Cursor's picker-derived references, the future Codex AGENTS.md walker) declare `precondition: { provider: '<id>' }` on their manifest; the orchestrator only invokes them when the node's provider matches AND the provider is the active lens.
64
+ The lens does NOT gate the universal extractors that ship under `core/` (markdown links, external URLs, sidecar annotations). Those run regardless of the active provider because their semantics are provider-agnostic. Provider-specific extractors (Claude's `@`-directive parser, Gemini's three-surface `@`-parsers, Cursor's picker-derived references, the future Codex AGENTS.md walker) declare `precondition: { provider: '<id>' }` on their manifest; the orchestrator invokes them on every node visited during the scan as long as the **active lens** is in the declared provider list, regardless of which provider's `classify()` claimed the node.
65
+
66
+ The gate is the active lens, not the node's provider. A `@handle` token in `CLAUDE.md` or `notes/todo.md` (files the `claude` provider disclaims to `core/markdown`) still gets parsed by `claude/at-directive` under the `claude` lens, because the runtime grammar is what the lens represents and the runtime reads markdown across the whole project, not only the files it owns. The earlier double-check ("node's provider matches AND the lens") silently dropped that surface; dropping the node side restores it. Cross-lens isolation is preserved by the lens half alone: under `gemini`, claude extractors are silent on every node (including `.claude/*`), because the lens authorisation is missing. When `activeProvider` is `null` (no setting, no filesystem marker), provider-gated extractors are skipped uniformly.
65
67
 
66
68
  ---
67
69
 
@@ -254,6 +256,38 @@ The dispatch contract has two consequences implementations MUST honour:
254
256
 
255
257
  The fallback exists because the format-named generic kind `markdown` is provider-agnostic: no vendor owns the universal markdown format. Keeping the fallback as a Provider (rather than a kernel-level special case) preserves the boot invariant that no extension is privileged, when a future vendor Provider (Codex, Cursor, Roo) lands, it slots into the iteration order before `core/markdown` and the fallback semantics stay invariant.
256
258
 
259
+ ### Provider · kind identifiers
260
+
261
+ Each entry in a Provider's `kinds` catalog MAY declare an optional `identifiers: TIdentifierSource[]` listing, in priority order, how the kernel derives the kind's canonical invocation handle(s) for the post-walk confidence-lift transform. Absent / empty = the kind is not name-resolvable (path-based resolution still applies independently).
262
+
263
+ The closed set of sources:
264
+
265
+ | `TIdentifierSource` | Reads | Typical kinds |
266
+ |---|---|---|
267
+ | `'frontmatter.name'` | `node.frontmatter.name` | every invocable kind whose schema declares `name` as required (agents, commands, skills); the canonical source when the author set it. |
268
+ | `'filename-basename'` | `basename(path)` with the extension stripped | Anthropic / Gemini agents and commands, OpenAI Codex sub-agents — references at `<dir>/<name>.<ext>` resolve `@<name>` even when frontmatter is partial. |
269
+ | `'dirname'` | `basename(dirname(path))` | Anthropic / Gemini / agent-skills skills — Anthropic explicitly documents that the directory between `skills/` and `/SKILL.md` is the invocation handle, with `frontmatter.name` as an optional override (https://code.claude.com/docs/en/skills.md). |
270
+
271
+ Sources MAY appear together; the resolver visits each declared source per node, normalises every yielded value with the §Extractor · trigger normalization pipeline, and contributes a presence entry to the cross-kind name index. Multiple sources that produce the same normalised name collapse into one bucket entry (the dual-source `['frontmatter.name', 'filename-basename']` on a `.claude/agents/foo.md` with `name: foo` yields a single `foo` entry, not two).
272
+
273
+ Implementations MUST treat an absent `identifiers` field exactly like `[]`: the kind contributes nothing to the name index and is reachable only via the path-match rule of §Provider · resolution rules.
274
+
275
+ ### Provider · resolution rules
276
+
277
+ Each Provider MAY declare an optional `resolution: Record<linkKind, targetKind[]>` map listing, for each `link.kind` an Extractor in this Provider's bundle emits, the set of target `node.kind` values that count as a valid resolution for the post-walk confidence-lift transform. Absent = no link.kind bumps under this Provider via the name path (path-match always fires).
278
+
279
+ The transform runs after `dedupeLinks` and before the analyzer pipeline. For each link below `confidence: 1.0`:
280
+
281
+ 1. **Path match (universal)**: if `link.target` equals some node's `path`, confidence is bumped to `1.0`. Applies to every link.kind, ignores the `resolution` map. Drives resolved markdown / at-directive references and `core/mcp-tools` synthetic edges.
282
+
283
+ 2. **Name match (links carrying a `trigger.normalizedTrigger`)**: strip the leading `@` / `/` sigil, look up the resulting handle in the cross-kind name index built from every node's declared `identifiers` (see §Provider · kind identifiers). The lookup keys on the LINK SOURCE node's Provider id: `resolution = providers[sourceNode.provider].resolution`. If `resolution[link.kind]` exists AND any candidate node's kind appears in it, confidence is bumped to `1.0`. Otherwise the link stays at its extractor-emitted confidence.
284
+
285
+ The matrix is **per-link-kind, per-Provider**, strict: a `claude` Provider that declares `resolution: { mentions: ['agent'], invokes: ['command', 'skill'] }` does NOT bump a `/foo` slash matching an agent named `foo` (slash → agent is a kind mismatch surfaced by `link-conflict` / `kind-mismatch` analyzers, not silently treated as a resolution). The strictness is the load-bearing difference from the kind-agnostic `core/broken-ref` analyzer: `broken-ref`'s scope is "the name exists somewhere" (a name-only resolution is enough to clear the broken flag), the post-walk bump is "the name exists AS A VALID resolution for this link.kind". The `not-broken` + `not-bumped` combination is a documented edge case: the trigger resolves to a real node but the link's kind cannot legitimately point there.
286
+
287
+ The lookup uses the SOURCE node's Provider id deliberately: a Provider rules over the link.kinds its Extractors emit, regardless of where the resolution candidates physically live in the graph. A `claude` agent that mentions a `gemini` agent (cross-provider mention) still follows claude's `resolution.mentions = ['agent']` rule. When the source node belongs to a Provider without `resolution` (e.g. a `CLAUDE.md` classified by `core/markdown`), the name path short-circuits, the path-match rule still applies.
288
+
289
+ **Distinct from the Signal IR `resolverRules` (§Resolver phase).** `resolverRules` rank candidates INSIDE a Signal (Phase 3+, no Provider declares it today); `resolution` runs against the merged Link graph post-walk and is the contract Extractors EMITTING Links rely on. The two surfaces share no mechanism and intentionally do not compose; when a Signal IR materialises into a Link, the `resolution` matrix runs unchanged against the resulting Link.
290
+
257
291
  ### Extractor · output callbacks
258
292
 
259
293
  The `Extractor` runtime contract is `extract(ctx) → void`. The extractor emits its work through three callbacks the kernel binds onto `ctx`:
package/index.json CHANGED
@@ -174,13 +174,13 @@
174
174
  }
175
175
  ]
176
176
  },
177
- "specPackageVersion": "0.32.0",
177
+ "specPackageVersion": "0.33.0",
178
178
  "integrity": {
179
179
  "algorithm": "sha256",
180
180
  "files": {
181
- "CHANGELOG.md": "07a19322ad6fb1b77976776a5c046ee6fe717d74ed341cae2e49e851a2756c7b",
181
+ "CHANGELOG.md": "28f06b38d7bb628b3e6a7f9e0d6f989e2388744692fefb4a34577154a011e387",
182
182
  "README.md": "54c4649fa9742bf2f74423ea78788a7474ce09649cbe1e72a270b606cf16a0a5",
183
- "architecture.md": "d7c01c6de2fb49959056bf01847fc290fafe26e094a8660690a9bb404de5694d",
183
+ "architecture.md": "fe82c147fe6a2cb80289c7957ce735497f6f7d75e708791bffb46978e9b3efdf",
184
184
  "cli-contract.md": "9c3e07a614e9504243b1661f3e76f680484646d46a35f4c2817486e501c78c6e",
185
185
  "conformance/README.md": "6871dde25b5770ed945284c9e0f749e0768ec3f5ba4966bdb215985789e43887",
186
186
  "conformance/cases/kernel-empty-boot.json": "2a5be9c93143d07a16d998df09dcc8fa4ea2d2f9a0bff6417573ed5a770352c1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skill-map/spec",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "JSON Schemas, prose contracts, and conformance suite for the skill-map specification.",
5
5
  "license": "MIT",
6
6
  "type": "module",