feat(kyberforge): ADR-0020 context contract for skills and agents #103
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user