Skip to content

docs(decisions): 0255 — skill namespaces keep v1 and fabrika apart (#4829) - #5277

Merged
usirin merged 4 commits into
mainfrom
usirin/adr-0253-skill-namespace-coexistence-4829-87FC3A96
Aug 10, 2026
Merged

docs(decisions): 0255 — skill namespaces keep v1 and fabrika apart (#4829)#5277
usirin merged 4 commits into
mainfrom
usirin/adr-0253-skill-namespace-coexistence-4829-87FC3A96

Conversation

@usirin

@usirin usirin commented Aug 10, 2026

Copy link
Copy Markdown
Member

The repo carries two skill sets right now — the old pipeline (v1) and its fabrika rebuild — and a
worry had been recorded that a rebuilt skill and its v1 original were fighting over the same name.
They are not. I measured it: Claude Code names a plugin's skills plugin:skill, and a plain skill's
name is just its directory name — so the plain report is always v1's and the rebuild is only ever
fabrika:report. Both show up in a real session, side by side, under different names.

That changes the answer. There is no name fight to fix, so this PR keeps the current setup, writes
down the rule (plain name means v1, fabrika: means the rebuild), and names the single edit that
retires the whole old set later. What does genuinely overlap is the descriptions — which is what
the model actually picks a skill by — and that was already the recorded mitigation.

Fixes #4829

What this changes

  • New ADR 0255 (.decisions/0255-skill-namespaces-keep-v1-and-fabrika-apart.md) — takes the
    issue's branch 2 (keep the loading path, correct the record), states the namespace rule, explains
    why the symlink is not cut now, and sequences the cutover.
  • claude-plugins/fabrika/skills/report/contract.md — the "dormant only by configuration"
    paragraph is corrected in place and points at the ADR.
  • claude-plugins/fabrika/skills/triage/contract.md — its already-correct correction is
    sharpened to say the overlap is in the description, not the name, and cites the ADR instead of the
    open issue.

Acceptance criteria

Notes for review

Deviations

  • Out-of-scope changeSaid: The .claude/skills symlink loads v1 skills regardless of the plugin toggle, so v1 and fabrika skills are model-invoked under one name today #4829's acceptance criteria name exactly one contract to
    reconcile, claude-plugins/fabrika/skills/report/contract.md. Did: also edited
    claude-plugins/fabrika/skills/triage/contract.md (two sentences). Why: that file had already
    independently falsified the report contract's premise and cited this issue as the open question;
    leaving it citing an open issue after the issue is settled, and leaving its "the stated mitigation
    is inert" line standing when the ADR concludes the mitigation is the right one, would put the
    corpus at odds with the ADR the same PR lands. Disposition: for the reviewer to judge — the
    edit is confined to the paragraph that names The .claude/skills symlink loads v1 skills regardless of the plugin toggle, so v1 and fabrika skills are model-invoked under one name today #4829 and adds no new claim beyond the ADR's.
  • (repair round 1) Head moved on a PASS, not a FAILSaid: repair mode acts on a
    current-head gate FAIL. Did: pushed a round while both namespaces stood at PASS/advisory,
    which staleness-invalidates the markers bound to 951a1fb. Why: the review-doc verdict
    recorded a non-blocking advisory that measurement 1 attributed the message "names must not
    contain ':' (reserved for plugin namespacing)"
    to the skill frontmatter validator, when both
    of its two occurrences in the shipped CLI are on the agent path (parseAgentFromMarkdown and
    the agent parse-error helper). An ADR that lands carrying a wrong attribution is far more
    expensive to correct later than now, and the PR is §CP and not yet approved. Disposition: the
    new head needs a fresh review-doc + review-skill pass before approval.
  • (repair round 1) Narrower than the finding, plus one accepted optional noteSaid: the
    advisory suggested a one-sentence swap of the cited mechanism. Did: that swap, re-grounded on
    two skill-side strings re-verified first-party against the same binary, plus the reviewer's
    second, optional note — softened "no bare name is ever contested" by one clause naming the
    shipped opt-in fallback frontmatter field. Why: the fallback qualifier cost one clause and
    no skill in this repo declares it, so the conclusion is untouched. Disposition: the ADR's
    conclusion, §1 naming rule, branch-2 decision and Banned list are unchanged; the branch was also
    rebased onto latest origin/main (clean).

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

No preview deploy

  • No preview deploy for this PR — its diff touches no deploy-relevant path, so no preview stack was minted and e2e is not applicable. (c6fb167)
  • web — Stage pr-5277 torn down.

@usirin

usirin commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

review-doc: advisory — blocking-set PR (§CP — approval-gated)

Verified fresh against issue #4829's acceptance criteria, read-only from the PR head via a per-run ref.

Reviewed-head: @ 951a1fb

§CP classification: control-plane, decided on the content axis, not the path axis.
cp-classify classify returned content-undetermined (no CODEOWNERS path matched — CODEOWNERS,
read live from main, carries no /claude-plugins/fabrika/ row and no .decisions/ row).
guard-content-probe classify then returned guard-touching (§CP, ADR 0164)
[guard-vocabulary-match]
on .decisions/0255-…. So the advisory form above, per §ADVISORY.

Class fan: class-probe classify --namespaces at this head names 2 namespaces —
review-doc, review-skill. Both are dispatched this pass, one marker per comment. No
UI-affecting path, so review-design is correctly not required.

Acceptance criteria

Grounding check — the premise-is-false claim, verified against the shipped CLI

The ADR's whole branch-2 conclusion rests on "the premise is false: v1 and fabrika are never invoked
under one name." I verified this against Claude Code 2.1.226 directly, not from the PR body. The
conclusion is correct, on three independent pieces of shipped evidence:

  1. The Skill tool's own instruction text states verbatim: "Plugin skills use plugin:skill."
  2. The skill-name validator states verbatim that "Skill names match the skill's directory name (or
    'plugin:skill' for plugin-qualified skills)"
    — a filesystem skill's name is directory-derived,
    and the colon form is reserved for the plugin-qualified spelling.
  3. The plugin-skill dedupe is scoped exactly as the ADR says. The shipped log line reads "Skipping
    plugin skill '…' — … is a user-level skill already surfaced by the skills directory loader"
    ,
    confirming the check is user-level-scoped and does not reach a project's .claude/skills.

The live setup also corroborates the ADR's framing independently of the PR: .claude/settings.json
carries {"fabrika@kampus": true, "kampus-pipeline@kampus": false}, .claude/skills and
.claude/agents are symlinks into the v1 tree, and the both-sets overlap is exactly the four names
the ADR claims — adr, plan-epic, report, triage — which I confirmed by intersecting the two
skill directories.

Advisory (non-blocking) — one citation in §"What was measured" is mis-scoped

Measurement 1 says "the frontmatter validator rejects a non-plugin skill whose name contains
a colon — the message is verbatim 'names must not contain ':' (reserved for plugin namespacing)'."

That message is real and verbatim in the shipped binary, but it occurs at exactly two code sites and
both are on the agent path, not the skill path: the agent-frontmatter parse-error helper
(Invalid "name": …) and parseAgentFromMarkdown (Agent file … has invalid name …). I found no
skill-side occurrence.

This does not falsify the finding — the skill-side guarantee is real, it is just a different and
arguably cleaner mechanism (evidence 1–2 above: directory-derived names, colon reserved for the
plugin-qualified form). So the conclusion, the naming rule in §1, and the branch-2 decision all
stand, and no acceptance criterion is unmet. But the sentence as written sends a future auditor
looking for a skill validator that does not carry that rule, which is the exact "confident wrong
answer" failure §1 of this ADR warns about. Worth a one-sentence correction swapping the cited
mechanism for evidence 1–2. Recorded here rather than blocking.

Second, smaller note: the shipped skill frontmatter carries an @internal fallback field whose
own description says a skill with it set "yields to a same-suffix plugin or MCP skill
(<plugin>:<name> / <server>:<name>) when one is loaded"
— i.e. there is a shipped mechanism
by which a bare name can be shadowed by a same-suffix plugin skill. It is opt-in and off by default,
and no skill in this repo declares it (checked), so the ADR's conclusion is unaffected in practice.
It is a qualifier the absolute phrasing "no bare name is ever contested" does not carry.

ADR numbering — renumber verified clean at this head

Hygiene

Verified read-only from a per-run ref at 951a1fb6d38f99c2b59f0fccf8c2ec7d1fc511cc; no working
tree was moved.

@usirin

usirin commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

review-skill: advisory — blocking-set PR (§CP — approval-gated)

Behavioral-artifact gate over the two changed fabrika contracts. Verified read-only from the PR head
via a per-run ref.

Reviewed-head: @ 951a1fb

§CP classification: control-plane by content, not path. CODEOWNERS (read live from main)
has no /claude-plugins/fabrika/ row, so the two contract files are not path-owned;
guard-content-probe classified the sibling .decisions/0255-… file as guard-touching (ADR 0164),
which puts the PR in the blocking set. Hence the advisory form.

Why this gate is required: class-probe classify --namespaces at this head names review-doc
and review-skill. The skills class fires on HAS_SKILLS_RE matching
claude-plugins/fabrika/skills/**. Both markers are posted this pass, one per comment.

Scope of this gate

Two files, both contract.md — authoring contracts, not SKILL.md. No skill frontmatter, name,
description, or trigger surface changed in this PR, so nothing here can alter what a skill is
invoked for. That keeps the skill-hygiene surface narrow: what is under review is whether the prose
these contracts commit fabrika's authors to is now true.

  • [PASS] report/contract.md — the falsified premise is corrected, not just re-pointed. The old
    paragraph asserted the collision was "dormant only by configuration" because
    "kampus-pipeline@kampus": false. The replacement states the mechanism correctly: the symlink
    loads v1 project-level so the toggle never reaches it, both sets are live, the names never
    collide, and the real overlap is the description — which is what a model actually invokes off.
    This matches what I verified against the shipped CLI (see the review-doc verdict for the
    grounding detail). It correctly hands the "does the differentiation actually fire" question to the
    eval set per ADR 0249 rather than asserting it, and preserves the frozen-v1-baseline stance of
    ADR 0238. Relative ADR link resolves.

  • [PASS] triage/contract.md — corrected on both counts, and it was already the more accurate of
    the two.
    Note in the ADR's favour: this file's pre-existing text on main already stated that
    a live roster carries both adr/fabrika:adr and report/fabrika:report. That is a committed
    observation predating this PR, so the ADR's roster claim has corroboration independent of the PR
    body. The edit makes the two changes the author disclosed: #4829 is no longer described as an
    open question (now "Settled by ADR 0255"), and the mitigation is no longer called "inert" (now
    "the right one, and it has to be load-bearing here"). Both were flatly contradicted by the ADR
    landing in the same commit.

  • [PASS] No behavioral drift. Neither edit changes a verb, an invariant, a gate, or a
    fail-closed rule. The triage edit keeps the "model-invoked deliberately" requirement and the
    two non-optional consequences that follow it intact — it only rewrites the justification
    paragraph above them. Diff sizes (11+/7− and 8+/4−) are consistent with a prose-only reconcile.

Ruling on the disclosed class-7 deviation — the triage/contract.md edit is IN SCOPE

The author flagged that #4829's acceptance criteria name only report/contract.md, and that they
edited triage/contract.md too. Accepted as in-scope. Three reasons:

  1. It is the same obligation the criteria already impose, applied to the one other file carrying
    the same falsified claim. AC3 exists because report/contract.md recorded a premise the ADR
    disproves. triage/contract.md recorded the same disproved premise plus a second claim the ADR
    contradicts ("its stated mitigation is inert" vs. the ADR's "the premise was wrong; the
    mitigation was right"). Reconciling one and not the other satisfies the letter of AC3 while
    leaving the repo self-contradicting.
  2. The ADR's own Banned list forbids "Recording anywhere that the plugin toggle gates what the
    symlink loads, or that the bare name is contested." Leaving the stale triage paragraph would
    land the ADR and a violation of its own banned list in the same commit.
  3. The edit is minimal and bounded — one paragraph, no new claims, no verb or invariant touched.

This is the correct handling of a class-7 deviation: disclosed rather than silent, and narrowly
scoped to what the primary change makes false. No further scope was taken; the routing-pin half
(#4761/#4762) and the retirement work (#5276) were correctly left filed rather than done here.

Hygiene

Verified read-only from a per-run ref at 951a1fb6d38f99c2b59f0fccf8c2ec7d1fc511cc; no working
tree was moved.

usirin and others added 4 commits August 9, 2026 22:09
…namespaces (#4829)

Settles #4829. Measured, not assumed: the CLI namespaces plugin skills as
`<plugin>:<skill>` and refuses a colon in any other skill's name, and a live
phoenix session's roster carries both the bare v1 set and the `fabrika:*` set.
So the bare name is unambiguously v1's and there is no name collision to cut
the `.claude/skills` symlink over — what overlaps is the description, which is
what a model invokes off.

Takes the issue's branch 2: keep the loading path, correct the record, name the
retirement lever. v1 retires as a set when `.claude/skills` and `.claude/agents`
are deleted alongside the ADR 0077 suppression, in one change. Covers
`.claude/agents` (same lever, no collision — fabrika ships no agents) and
confirms `.claude/.pipeline` is unaffected.

Reconciles the "dormant only by configuration" paragraph in the fabrika report
contract and sharpens the triage contract's already-correct correction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three in-flight lanes each derived 0253 off the same origin/main max (0252).
Ordered by PR creation time, #5271 (04:18:29Z) keeps 0253, #5273 (04:18:57Z)
takes 0254, and this PR (04:25:21Z) takes 0255.

Renames the file, updates the frontmatter `id` and the H1 self-reference, and
repoints the two fabrika contract links. No decision content changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…th, not the skill validator

The reviewer's non-blocking advisory on PR #5277: measurement 1 cited
"names must not contain ':' (reserved for plugin namespacing)" as the SKILL
frontmatter validator's message. The string is real and verbatim in the
shipped CLI (2.1.226), but it occurs at exactly two sites and both are on the
AGENT path — parseAgentFromMarkdown and the agent parse-error helper. There is
no skill-side occurrence, so the sentence sent a future auditor hunting a rule
that isn't there.

Re-grounded on the two skill-side strings verified first-party against the same
shipped binary: the Skill tool's instruction text ("Plugin skills use
`plugin:skill`.") and the skill-name validator ("Skill names match the skill's
directory name (or 'plugin:skill' for plugin-qualified skills)"). The
conclusion, the §1 naming rule, and the branch-2 decision are unchanged.

Also softened "no bare name is ever contested" by one clause: the shipped
opt-in `fallback` frontmatter field lets a bare-named skill yield to a
same-suffix plugin skill. No skill in this repo declares it, so the conclusion
stands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@usirin
usirin force-pushed the usirin/adr-0253-skill-namespace-coexistence-4829-87FC3A96 branch from 951a1fb to c6fb167 Compare August 10, 2026 05:12
@usirin

usirin commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

review-doc: advisory — blocking-set PR (§CP — approval-gated)

Re-review at the new head after repair round 1. The prior verdicts (bound to 951a1fb6) are
staleness-invalidated per ADR 0058 and are records, not current. Everything below is re-derived
first-party at this head, read-only from a per-run ref.

Reviewed-head: @ c6fb167

§CP classification — re-derived on BOTH axes at this head. Control plane, decided on the
content axis; the path axis is clear.
cp-classify classifycontent-undetermined [content-source-present] (no path matched
CONTROL_PLANE_RE, re-resolved live from main; the two changed files under
claude-plugins/fabrika/skills/ are not kampus-pipeline, and .decisions/** is not path-§CP).
guard-content-probe classify on .decisions/0255-… at this head → guard-touching (§CP, ADR
0164) [guard-vocabulary-match]
. Hence the advisory form (§ADVISORY): this verdict does not
authorize a merge — a @kamp-us/control-plane approval at head does, and ship-it then enqueues
(ADR 0135/0048).

Class fan — re-run at this head, not reused. class-probe classify --namespaces over the
3-file changed set names 2 namespaces: review-doc, review-skill. Both are dispatched this
pass, one marker per comment. No UI-affecting path, so review-design is correctly not named.

Acceptance criteria (#4829)

  • [PASS] An ADR settles which branch is taken, naming the cutover sequencing. ADR 0255 takes
    branch 2 explicitly; §2 gives why the path is not cut now (three ordered reasons) and §3 gives the
    three-step sequence ending in one edit, plus the ban on a per-skill partial cut. Unchanged by the
    repair round.

  • [PASS] Covers .claude/agents; states whether .claude/.pipeline is affected. §4 covers
    .claude/agents (same mechanism, same lever, no agent-name collision today — re-confirmed: the
    fabrika plugin root carries no agents/ directory). §5 confirms triage's read that
    .claude/.pipeline is unaffected and adds the path-alias-vs-load-path qualifier. Unchanged.

  • [PASS] report/contract.md's "dormant only by configuration" paragraph is reconciled.
    Corrected in place and pointed at the ADR; the relative link ../../../../.decisions/0255-…
    resolves to repo root from claude-plugins/fabrika/skills/report/ and the target exists at this
    head. Unchanged by the repair round.

  • [PASS] Bare-name resolution order established by observation, and now correctly attributed.
    This is the criterion the repair touched, so I re-verified it against the shipped CLI myself
    rather than reading the diff. See the grounding section below — all three claims are true at
    source.

  • [PASS] Mechanical follow-ups filed as separate issues. DEVELOPMENT.md pins 13 skill references to .claude/skills paths, so they resolve to v1 only #5275 and Retire the v1 skill roster in one change: both .claude symlinks plus the ADR 0077 suppression #5276 both exist, are open,
    and are named in §Records. Unchanged.

Grounding — is the CORRECTED attribution actually TRUE at source?

The whole point of the repair was factual accuracy, so I re-grounded every claim in the rewritten
paragraph against the shipped Claude Code 2.1.226 binary (the version the ADR names), matching
byte offsets in its JS source region and reading the surrounding function bodies. Verdict:
factually true.

  1. "the Skill tool's own instruction text says verbatim 'Plugin skills use plugin:skill.'"
    TRUE. One occurrence, inside the Skill tool's description of its skill parameter (the block
    that also documents "exact name from the listing, no leading slash"). The stored form carries JS
    template-literal escaping on the backticks; the rendered text is exactly as quoted.

  2. "the skill-name validator says verbatim 'Skill names match the skill's directory name (or
    'plugin:skill' for plugin-qualified skills)'
    "
    TRUE. It sits in the skill-name validation
    function (the one that also rejects unpaired surrogates, surrounding whitespace, *,
    wildcard-suffix names and a leading /), as the explanatory clause of its
    parentheses/commas/control-characters rejection. Correctly attributed to a skill-side
    validator.

  3. "that message occurs at exactly two sites in the shipped binary and both are on the agent path
    (parseAgentFromMarkdown and the agent parse-error helper)"
    TRUE. Four raw matches of the
    phrase, of which two are the compiled-snapshot copies of the same two source strings; the two
    source sites are (a) the frontmatter parse-error helper, which returns the
    Invalid "name": … string and whose only call site is the agent loader (it logs
    Failed to parse agent from … and fires the tengu_agent_parse_error metric), and (b) the
    agent-from-markdown parser, which emits Agent file … has invalid name '…'. No skill-side
    occurrence exists.
    The redirect the ADR now gives the reader is correct.

Also re-verified, for the softening clause: the fallback frontmatter field is real (it is in the
shipped known-frontmatter-key list and in the parsed-frontmatter default object), its own shipped
description says a skill carrying it "yields to a same-suffix plugin or MCP skill … when one is
loaded"
, and git grep '^fallback:' over the whole tree at this head returns zero hits. So
"which no skill in this repo declares" holds, and the hedge is accurate rather than decorative.

  • [PASS] Advisory from the prior round is discharged. The prior head's non-blocking advisory
    asked for exactly this swap. It was made, and made correctly.

Advisory (non-blocking, no action required) — one residual imprecision

The rewritten sentence says the colon spelling "is reserved for the plugin-qualified form." The
same Skill-tool instruction text I verified for claim 1 continues: "Directory-scoped skills are
listed with a path prefix (apps/web:deploy)"
— so a colon also appears in the directory-scoped
listing form, which is not plugin-qualified. This does not touch the conclusion: v1 loads from the
repo-root .claude/skills, which is unscoped, so its listed names are bare and disjoint from
fabrika:* exactly as the ADR says. And the sentence is a faithful reading of the validator string
it cites, which itself names only the plugin-qualified form. Recorded so a future auditor who meets
apps/web:deploy is not surprised; not worth a head move.

ADR numbering + dangling-reference check, re-run at this head

Doc hygiene

  • [PASS] House format. ADR frontmatter carries id/title/status/date/tags, id: 0255
    matches the filename, and the body has ## Context / ## Decision / ## Consequences.
  • [PASS] Index row. No .decisions/index.md is committed (correct — discovery is ambient, ADR
    0126). Frontmatter status: accepted is the source the ambient map renders.
  • [PASS] Links resolve. All in-repo links are standard markdown relative paths; every target
    exists at this head or on fresh origin/main. No wikilinks.
  • [PASS] No leaked local/home paths. The shared matcher over the diff's added lines returns
    clean.
  • [PASS] Supersession. Nothing is superseded — ADR 0077 is explicitly confirmed, its status and
    decision text untouched, so no forward-pointer is owed.
  • [PASS] Status sanity. accepted is in the house vocabulary and is coherent with an entry that
    settles a live fork.
  • [PASS] Single Diátaxis mode. Explanation throughout; the tables and the numbered sequence serve
    the reasoning rather than turning the page into a how-to.
  • [PASS] Clear, concise prose. The rewritten paragraph is direct and the added redirect earns its
    length — it is the whole point of the round. No AI-tell density.
  • [PASS] deviation-disclosure (§DEV). The body carries a literal ## Deviations heading with
    three entries, two tagged (repair round 1), each with substantive Said / Did / Why / Disposition.
    The head-moved-on-a-PASS entry and the narrower-than-the-finding entry both match what the diff
    actually did, including the rebase. Nothing undisclosed that this gate could see.

CI

All checks green at this head — 23 success, 7 skipped, 0 failures.

Verified read-only from a per-run ref at c6fb167effc717dce000849f026fe2e738db4f5f; no working tree
was moved, and no prior verdict was edited or removed.

@usirin

usirin commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

review-skill: advisory — blocking-set PR (§CP — approval-gated)

Re-review at the new head after repair round 1. The prior review-skill verdict (bound to
951a1fb6) is staleness-invalidated per ADR 0058. Re-derived first-party at this head, read-only
from a per-run ref.

Reviewed-head: @ c6fb167

§CP classification — re-derived on BOTH axes at this head. Control plane by content, not by
path. cp-classify classifycontent-undetermined (nothing matched CONTROL_PLANE_RE,
re-resolved live from main; claude-plugins/fabrika/skills/** is a non-kampus-pipeline
plugin, which the §CP set names as non-blocking). guard-content-probe classify on the sibling
.decisions/0255-… at this head → guard-touching (§CP, ADR 0164), which puts the whole PR in the
blocking set. Hence the advisory form: this verdict does not authorize a merge.

Why this gate is required. class-probe classify --namespaces re-run at this head names
review-doc and review-skill — the skills class fires on HAS_SKILLS_RE matching
claude-plugins/fabrika/skills/**. Both markers posted this pass, one namespace per comment.

What actually changed since the reviewed head

The repair round touched only .decisions/0255-… (10 insertions, 5 deletions) plus a clean
rebase onto latest origin/main. git diff 951a1fb6..c6fb167e -- claude-plugins/ is empty — the
two contracts are byte-identical to the head that already passed this gate. I re-verified them
against fresh origin/main rather than trusting that, since a rebase can change what a diff means.

Skill-rigor checklist

  • [PASS] Behavioral correctness. Both changed files are contract.md authoring contracts, not
    SKILL.md. No frontmatter, name, description, trigger surface, verb, or procedure step
    changed — the edits are confined to the premise paragraph each file recorded about the v1/fabrika
    collision. Traced as an executing agent would: nothing in either file's procedure reads
    differently after the edit.

  • [PASS] report/contract.md — the falsified premise is corrected, not merely re-pointed. The
    replacement states the mechanism correctly (the symlink loads v1 project-level so the toggle never
    reaches it; both sets are live; the names never collide; the real overlap is the description,
    which is what a model invokes off), hands the does-it-actually-fire question to the eval set per
    ADR 0249 instead of asserting it, and preserves the frozen-v1-baseline stance of ADR 0238. This is
    consistent with what I re-verified against the shipped CLI at this head (grounding detail in the
    review-doc verdict).

  • [PASS] triage/contract.md — corrected on both counts. #4829 is no longer described as an
    open question ("Settled by ADR 0255"), and the mitigation is no longer called "inert" (now "the
    right one, and it has to be load-bearing here"). Both were flatly contradicted by the ADR landing
    in the same commit. The "model-invoked deliberately" requirement and the two non-optional
    consequences below it are intact — only the justification paragraph above them was rewritten.

  • [PASS] Trigger / description quality. Out of reach: no description line and no frontmatter
    is in the diff, so nothing here can change what either skill fires on.

  • [PASS] Cross-skill conflict / shadowing. No shared contract, marker namespace, §CP row, or
    seam another skill reads is touched. Both files now agree with each other and with ADR 0255 — the
    edit removes a corpus-level contradiction rather than creating one, and it keeps the corpus off
    the ADR's own Banned list ("recording anywhere that the plugin toggle gates what the symlink
    loads, or that the bare name is contested").

  • [PASS] Gate-invariant preservation. No gate invariant is in this diff's reach: nothing under
    claude-plugins/kampus-pipeline/, no ship-it refusal, no matcher, no SHA-staleness rule, no
    author-gate, no config-pin. The two contracts govern fabrika authoring, not merge authority.

  • [PASS] Contract implementation ticket (ADR 0248). In scope by the file list (the diff changes
    two claude-plugins/fabrika/skills/*/contract.md), and discharged: the verb inventories are
    untouched
    by this PR — git diff origin/main...HEAD over both files shows no row added, removed
    or changed in either ## Verb inventory table — and each contract's verbs already carry their
    implementation ticket from its authoring lane (Implement the three /report fabrika verbs — the merged contract has no implementation ticket #4748 for the three /report verbs, Implement the nine fabrika triage verbs specified by the /triage derived contract #4831 for the
    nine /triage verbs, both closed as implemented). This PR introduces no unbuilt verb, so there is
    no new build-pool obligation to name.

  • [PASS] deviation-disclosure (§DEV). The body carries a literal ## Deviations heading with
    three entries, two tagged (repair round 1), each with substantive Said / Did / Why / Disposition.
    The pre-existing class-7 entry (editing triage/contract.md beyond the AC's named file) still
    stands and is still correctly ruled in scope — same obligation, one other file carrying the same
    falsified claim, and the ADR's Banned list would otherwise be violated by the same commit that
    lands it. The two new entries accurately describe a PASS-to-PASS head move and its narrow content.
    Nothing undisclosed that this gate could see.

Specialist fan-out (skill-class dimensions)

Ran over the changed contract text at this head: no unreachable step, no contradictory
instruction, and no uncovered procedural path introduced. Nothing to route; the linked issue's
acceptance-criteria list is unchanged by this gate.

Hygiene

  • [PASS] No home / absolute / sibling-repo paths in the diff's added lines (shared matcher, clean).
  • [PASS] Both ../../../../.decisions/0255-… links resolve to repo root from
    claude-plugins/fabrika/skills/<skill>/, and the target file exists at this head.
  • [PASS] No dangling 0253 reference anywhere in the tree at this head (git grep returns zero).
  • [PASS] All CI checks green at this head — 23 success, 7 skipped, 0 failures.

Verified read-only from a per-run ref at c6fb167effc717dce000849f026fe2e738db4f5f; no working tree
was moved, and no prior verdict was edited or removed.

@usirin usirin added this to the fabrika campaign milestone Aug 10, 2026
@usirin
usirin added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 43fa81f Aug 10, 2026
46 checks passed
@usirin
usirin deleted the usirin/adr-0253-skill-namespace-coexistence-4829-87FC3A96 branch August 10, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:cp-banked Control-plane PR banked on the board, awaiting a control-plane approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The .claude/skills symlink loads v1 skills regardless of the plugin toggle, so v1 and fabrika skills are model-invoked under one name today

2 participants