This commit is contained in:
dayland 2026-10-05 06:05:48 +03:00 • committed by GitHub
commit c83aaec072
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 1311 additions and 1 deletions

View file

@ -34,6 +34,11 @@ KNOWLEDGE_REQUIRED_KEYS = {
"bc-version", "domain", "keywords", "technologies",
"countries", "application-area",
}
# Optional knowledge keys (do not trigger the R02 closed-key-set error).
# `signals`: OPTIONAL routing-signal declarations. RESERVED & DORMANT — the shape
# is validated (R29 below) so authors and the authoring-assist advisory can
# populate it, but no production pipeline consumes it yet. See tools/authoring-assist.md.
KNOWLEDGE_OPTIONAL_KEYS = {"signals"}
ACTION_SKILL_REQUIRED_KEYS = {
"kind", "id", "version", "title", "description", "inputs", "outputs",
}
@ -204,7 +209,7 @@ def validate_knowledge(path: Path, parsed: Parsed, report: Report) -> None:
# R02 required keys, no extras, none empty
missing = KNOWLEDGE_REQUIRED_KEYS - fm.keys()
extras = fm.keys() - KNOWLEDGE_REQUIRED_KEYS
extras = fm.keys() - KNOWLEDGE_REQUIRED_KEYS - KNOWLEDGE_OPTIONAL_KEYS
if missing:
report.error(path, "R02", f"missing required frontmatter keys: {sorted(missing)}", 1)
if extras:
@ -214,6 +219,35 @@ def validate_knowledge(path: Path, parsed: Parsed, report: Report) -> None:
if v is None or v == "" or v == []:
report.error(path, "R02", f"frontmatter key '{k}' must not be empty", 1)
# R29 optional routing `signals` block. Each entry is either a bare token
# string or a mapping with a required 'token' and optional 'pattern'/'domain'/
# 'effect' (all non-empty strings; effect in {raise, suppress}). Reserved &
# dormant: validated for shape only so the block stays well-formed for the
# authoring-assist advisory; no production pipeline consumes it yet.
if "signals" in fm:
sigs = fm["signals"]
if not isinstance(sigs, list) or not sigs:
report.error(path, "R29", "signals must be a non-empty list", 1)
else:
for entry in sigs:
if isinstance(entry, str):
if not entry.strip():
report.error(path, "R29", "signals token string must not be empty", 1)
elif isinstance(entry, dict):
tok = entry.get("token")
if not isinstance(tok, str) or not tok.strip():
report.error(path, "R29", "signals entry must have a non-empty 'token'", 1)
for opt in ("pattern", "domain"):
if opt in entry and (not isinstance(entry[opt], str) or not entry[opt].strip()):
report.error(path, "R29", f"signals '{opt}' must be a non-empty string", 1)
if "effect" in entry and entry["effect"] not in ("raise", "suppress"):
report.error(path, "R29", "signals 'effect' must be 'raise' or 'suppress'", 1)
unknown = set(entry.keys()) - {"token", "pattern", "domain", "effect"}
if unknown:
report.error(path, "R29", f"signals entry has unknown keys: {sorted(unknown)}", 1)
else:
report.error(path, "R29", "signals entry must be a string or a mapping", 1)
# R03 bc-version
if "bc-version" in fm:
_, err = expand_bc_version(fm["bc-version"])