feat(kyberforge): ADR-0020 context contract for skills and agents #103

Merged
Defame1297 merged 18 commits from refactor/trim-skills-agents-context into main 2026-08-16 21:20:02 +00:00
7 changed files with 142 additions and 5 deletions
Showing only changes of commit 75a13c82f6 - Show all commits

View File

@@ -129,6 +129,14 @@ clause**, and a **boundary clause**. Capability enumeration, output-format detai
dependency, and none needs to. Verified: a tree holding only `plugins/` and the root `apm.yml`,
with no `.claude/` or `.agents/` anywhere, now produces findings identical to the working tree —
26 description FAILs, 9 body FAILs, 2 dangling targets, 0 missing references, 58 SUGGESTIONs.
- **Blocking is scoped to a sentence, which makes sentence boundaries load-bearing.** A prose-form
target earns a hard error only when its own sentence names another target that *resolves*; route
notation (`/name`, `→ name`) is exempt and always blocks. So the splitter is part of the contract,
not a detail of it. `e.g. "…"` is not a sentence end, and a sentence opening with a code span or a
lowercase skill name is a start; getting either wrong moves targets between the two tiers in
opposite directions — a stranded corroborator silently demotes a real finding to SUGGESTION, and a
missed boundary lets one sentence vouch for a target it never stood beside, producing a hard FAIL
with no escape hatch.
- **The blanket pushiness rules are deleted.** `skill-author/SKILL.md:104` and
`description-quality.md:21` are replaced by a conditional: add an indirect trigger only where the
user's natural phrasing genuinely omits the domain word — true for the `gitea-*` family, false for

View File

@@ -554,7 +554,28 @@ BACKTICK = re.compile(r"`(%s)`" % NAME_HYPH, re.I)
# ADR-0020's compressed arrow form `Not <thing> -> <name>`.
BOUNDARY_MARKER = re.compile(r"\b(?:do\s+not|instead|rather\s+than|not\s+for)\b", re.I)
BOUNDARY_ARROW = re.compile(r"\bnot\b[^.;]*?(?:->|→)", re.I)
SENTENCE_SPLIT = re.compile(u'(?<=[.!?])\\s+(?=[A-Z"“(])')
# Sentence boundaries decide the CORROBORATION scope above, so getting one wrong
# is not cosmetic — it moves a target between SUGGESTION and blocking ERROR. Two
# shapes common in these descriptions defeat the naive "period, space, capital"
# rule, in OPPOSITE directions:
# OVER-SPLIT. `e.g. "set up the manifest"` ends no sentence, but the quote
# looks like one starting. The clause is cut in half, the corroborating
# target lands on the far side of the cut, and a genuinely dangling target
# silently demotes to SUGGESTION — the gate takes a measurement and then
# throws it away, which is the vacuous-green shape this file exists to stop.
# UNDER-SPLIT. A real sentence opening with a code span or a lowercase skill
# name ("... Composes it. `gitea-prs` also uses it.") is not seen as a start
# at all, so two sentences merge and a resolving target vouches for an
# unresolvable one it never stood beside — a hard FAIL with no escape hatch,
# which is exactly the failure the corroboration rule was added to prevent.
# Both are closed here: the five abbreviations that actually occur in routing
# prose are excluded as sentence ends, and the opener class admits a backtick or
# a lowercase letter. Verified zero-delta on the current corpus (37 ERROR / 58
# SUGGESTION / 2 dangling before and after) — this protects the descriptions
# issue #99 is about to rewrite, not the ones already measured.
SENTENCE_SPLIT = re.compile(
u'(?<!\\be\\.g\\.)(?<!\\bi\\.e\\.)(?<!\\betc\\.)(?<!\\bvs\\.)(?<!\\bcf\\.)'
u'(?<=[.!?])\\s+(?=[A-Za-z`"“(])')
# The token that may follow a route target without turning it into a compound
# modifier: punctuation, end of sentence, a conjunction, a boundary word, or a

View File

@@ -480,7 +480,28 @@ BACKTICK = re.compile(r"`(%s)`" % NAME_HYPH, re.I)
# ADR-0020's compressed arrow form `Not <thing> -> <name>`.
BOUNDARY_MARKER = re.compile(r"\b(?:do\s+not|instead|rather\s+than|not\s+for)\b", re.I)
BOUNDARY_ARROW = re.compile(r"\bnot\b[^.;]*?(?:->|→)", re.I)
SENTENCE_SPLIT = re.compile(u'(?<=[.!?])\\s+(?=[A-Z"“(])')
# Sentence boundaries decide the CORROBORATION scope above, so getting one wrong
# is not cosmetic — it moves a target between SUGGESTION and blocking ERROR. Two
# shapes common in these descriptions defeat the naive "period, space, capital"
# rule, in OPPOSITE directions:
# OVER-SPLIT. `e.g. "set up the manifest"` ends no sentence, but the quote
# looks like one starting. The clause is cut in half, the corroborating
# target lands on the far side of the cut, and a genuinely dangling target
# silently demotes to SUGGESTION — the gate takes a measurement and then
# throws it away, which is the vacuous-green shape this file exists to stop.
# UNDER-SPLIT. A real sentence opening with a code span or a lowercase skill
# name ("... Composes it. `gitea-prs` also uses it.") is not seen as a start
# at all, so two sentences merge and a resolving target vouches for an
# unresolvable one it never stood beside — a hard FAIL with no escape hatch,
# which is exactly the failure the corroboration rule was added to prevent.
# Both are closed here: the five abbreviations that actually occur in routing
# prose are excluded as sentence ends, and the opener class admits a backtick or
# a lowercase letter. Verified zero-delta on the current corpus (37 ERROR / 58
# SUGGESTION / 2 dangling before and after) — this protects the descriptions
# issue #99 is about to rewrite, not the ones already measured.
SENTENCE_SPLIT = re.compile(
u'(?<!\\be\\.g\\.)(?<!\\bi\\.e\\.)(?<!\\betc\\.)(?<!\\bvs\\.)(?<!\\bcf\\.)'
u'(?<=[.!?])\\s+(?=[A-Za-z`"“(])')
# The token that may follow a route target without turning it into a compound
# modifier: punctuation, end of sentence, a conjunction, a boundary word, or a

View File

@@ -554,7 +554,28 @@ BACKTICK = re.compile(r"`(%s)`" % NAME_HYPH, re.I)
# ADR-0020's compressed arrow form `Not <thing> -> <name>`.
BOUNDARY_MARKER = re.compile(r"\b(?:do\s+not|instead|rather\s+than|not\s+for)\b", re.I)
BOUNDARY_ARROW = re.compile(r"\bnot\b[^.;]*?(?:->|→)", re.I)
SENTENCE_SPLIT = re.compile(u'(?<=[.!?])\\s+(?=[A-Z"“(])')
# Sentence boundaries decide the CORROBORATION scope above, so getting one wrong
# is not cosmetic — it moves a target between SUGGESTION and blocking ERROR. Two
# shapes common in these descriptions defeat the naive "period, space, capital"
# rule, in OPPOSITE directions:
# OVER-SPLIT. `e.g. "set up the manifest"` ends no sentence, but the quote
# looks like one starting. The clause is cut in half, the corroborating
# target lands on the far side of the cut, and a genuinely dangling target
# silently demotes to SUGGESTION — the gate takes a measurement and then
# throws it away, which is the vacuous-green shape this file exists to stop.
# UNDER-SPLIT. A real sentence opening with a code span or a lowercase skill
# name ("... Composes it. `gitea-prs` also uses it.") is not seen as a start
# at all, so two sentences merge and a resolving target vouches for an
# unresolvable one it never stood beside — a hard FAIL with no escape hatch,
# which is exactly the failure the corroboration rule was added to prevent.
# Both are closed here: the five abbreviations that actually occur in routing
# prose are excluded as sentence ends, and the opener class admits a backtick or
# a lowercase letter. Verified zero-delta on the current corpus (37 ERROR / 58
# SUGGESTION / 2 dangling before and after) — this protects the descriptions
# issue #99 is about to rewrite, not the ones already measured.
SENTENCE_SPLIT = re.compile(
u'(?<!\\be\\.g\\.)(?<!\\bi\\.e\\.)(?<!\\betc\\.)(?<!\\bvs\\.)(?<!\\bcf\\.)'
u'(?<=[.!?])\\s+(?=[A-Za-z`"“(])')
# The token that may follow a route target without turning it into a compound
# modifier: punctuation, end of sentence, a conjunction, a boundary word, or a

View File

@@ -480,7 +480,28 @@ BACKTICK = re.compile(r"`(%s)`" % NAME_HYPH, re.I)
# ADR-0020's compressed arrow form `Not <thing> -> <name>`.
BOUNDARY_MARKER = re.compile(r"\b(?:do\s+not|instead|rather\s+than|not\s+for)\b", re.I)
BOUNDARY_ARROW = re.compile(r"\bnot\b[^.;]*?(?:->|→)", re.I)
SENTENCE_SPLIT = re.compile(u'(?<=[.!?])\\s+(?=[A-Z"“(])')
# Sentence boundaries decide the CORROBORATION scope above, so getting one wrong
# is not cosmetic — it moves a target between SUGGESTION and blocking ERROR. Two
# shapes common in these descriptions defeat the naive "period, space, capital"
# rule, in OPPOSITE directions:
# OVER-SPLIT. `e.g. "set up the manifest"` ends no sentence, but the quote
# looks like one starting. The clause is cut in half, the corroborating
# target lands on the far side of the cut, and a genuinely dangling target
# silently demotes to SUGGESTION — the gate takes a measurement and then
# throws it away, which is the vacuous-green shape this file exists to stop.
# UNDER-SPLIT. A real sentence opening with a code span or a lowercase skill
# name ("... Composes it. `gitea-prs` also uses it.") is not seen as a start
# at all, so two sentences merge and a resolving target vouches for an
# unresolvable one it never stood beside — a hard FAIL with no escape hatch,
# which is exactly the failure the corroboration rule was added to prevent.
# Both are closed here: the five abbreviations that actually occur in routing
# prose are excluded as sentence ends, and the opener class admits a backtick or
# a lowercase letter. Verified zero-delta on the current corpus (37 ERROR / 58
# SUGGESTION / 2 dangling before and after) — this protects the descriptions
# issue #99 is about to rewrite, not the ones already measured.
SENTENCE_SPLIT = re.compile(
u'(?<!\\be\\.g\\.)(?<!\\bi\\.e\\.)(?<!\\betc\\.)(?<!\\bvs\\.)(?<!\\bcf\\.)'
u'(?<=[.!?])\\s+(?=[A-Za-z`"“(])')
# The token that may follow a route target without turning it into a compound
# modifier: punctuation, end of sentence, a conjunction, a boundary word, or a

View File

@@ -564,7 +564,28 @@ BACKTICK = re.compile(r"`(%s)`" % NAME_HYPH, re.I)
# ADR-0020's compressed arrow form `Not <thing> -> <name>`.
BOUNDARY_MARKER = re.compile(r"\b(?:do\s+not|instead|rather\s+than|not\s+for)\b", re.I)
BOUNDARY_ARROW = re.compile(r"\bnot\b[^.;]*?(?:->|→)", re.I)
SENTENCE_SPLIT = re.compile(u'(?<=[.!?])\\s+(?=[A-Z"“(])')
# Sentence boundaries decide the CORROBORATION scope above, so getting one wrong
# is not cosmetic — it moves a target between SUGGESTION and blocking ERROR. Two
# shapes common in these descriptions defeat the naive "period, space, capital"
# rule, in OPPOSITE directions:
# OVER-SPLIT. `e.g. "set up the manifest"` ends no sentence, but the quote
# looks like one starting. The clause is cut in half, the corroborating
# target lands on the far side of the cut, and a genuinely dangling target
# silently demotes to SUGGESTION — the gate takes a measurement and then
# throws it away, which is the vacuous-green shape this file exists to stop.
# UNDER-SPLIT. A real sentence opening with a code span or a lowercase skill
# name ("... Composes it. `gitea-prs` also uses it.") is not seen as a start
# at all, so two sentences merge and a resolving target vouches for an
# unresolvable one it never stood beside — a hard FAIL with no escape hatch,
# which is exactly the failure the corroboration rule was added to prevent.
# Both are closed here: the five abbreviations that actually occur in routing
# prose are excluded as sentence ends, and the opener class admits a backtick or
# a lowercase letter. Verified zero-delta on the current corpus (37 ERROR / 58
# SUGGESTION / 2 dangling before and after) — this protects the descriptions
# issue #99 is about to rewrite, not the ones already measured.
SENTENCE_SPLIT = re.compile(
u'(?<!\\be\\.g\\.)(?<!\\bi\\.e\\.)(?<!\\betc\\.)(?<!\\bvs\\.)(?<!\\bcf\\.)'
u'(?<=[.!?])\\s+(?=[A-Za-z`"“(])')
# The token that may follow a route target without turning it into a compound
# modifier: punctuation, end of sentence, a conjunction, a boundary word, or a

View File

@@ -564,6 +564,30 @@ grammar_case tp-backticked errors "routes to 'no-such-backticked-skill'" \
grammar_case tp-bare-terminal errors "routes to 'no-such-bare-skill'" \
"Use when doing the thing. Do not use for improvements — use sibling-skill or no-such-bare-skill instead."
echo ""
echo "--- corroboration is scoped to a REAL sentence, not to whatever the splitter says ---"
# Corroboration decides SUGGESTION vs blocking ERROR, so a mis-placed sentence
# boundary moves a target between the two tiers. The naive "period, space,
# capital" rule got this wrong in both directions, and both were live:
#
# OVER-SPLIT. `e.g. "..."` is not a sentence end, but the quote looks like a
# start. The clause was cut in half and the corroborator stranded on the far
# side, so a target that DOES sit beside a resolving sibling silently demoted
# to SUGGESTION — a measurement taken and then discarded.
grammar_case abbrev-split errors "routes to 'no-such-abbrev-skill'" \
"Use when doing the thing. Do not use for improvements — use sibling-skill first, e.g. \"run the audit\", then use no-such-abbrev-skill instead."
#
# UNDER-SPLIT. A sentence opening with a lowercase word or a code span was not
# seen as a start at all, so two sentences merged and a resolving target in the
# FIRST vouched for an unresolvable one in the SECOND that it never stood
# beside — a hard FAIL with no escape hatch, which is the exact failure
# corroboration was added to prevent. The target must still be REPORTED; only
# the power to block is withdrawn.
grammar_case lowercase-start suggests "routes to 'no-such-lower-skill'" \
"Use when doing the thing. Use sibling-skill for the main case. do not use for improvements — use no-such-lower-skill instead."
grammar_case backtick-start suggests "routes to 'no-such-tick-skill'" \
"Use when doing the thing. Use sibling-skill for the main case. \`no-such-tick-skill\` is not for this — do not use it instead."
# And the confirming half of the grammar rule: a compound-modifier target is
# CONFIRM-ONLY, not ignored. When the name does exist it still counts as a route
# — the rule suppresses the ERROR, it does not delete the target.