feat(persona): load the falsification skill from commons at call time
[skip-docs] — the invariant row for this plan lands in Step 13, after the mutations. Amendment 2 in the same commit: the framework-neutrality sweep covered only expert-reviewer, so the skill that arrived by subtree pull had no framework guard anywhere. GUARDED_SKILL_DIRS names both, and a fail-closed coverage arm turns red when a future pull brings a third skill that is not listed. Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
21c476bbe7
commit
488a0f2b6c
3 changed files with 276 additions and 3 deletions
|
|
@ -26,8 +26,15 @@ from portfolio_optimiser.verdicts import ProposalFeatures, capture_verdict, verd
|
|||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
SPEC_PATH = REPO_ROOT / "shared" / "method-spec.md"
|
||||
INGEST_SPEC_PATH = REPO_ROOT / "shared" / "ingest-spec.md"
|
||||
SKILL_DIR = REPO_ROOT / "shared" / "skills" / "expert-reviewer"
|
||||
SKILLS_ROOT = REPO_ROOT / "shared" / "skills"
|
||||
SKILL_DIR = SKILLS_ROOT / "expert-reviewer"
|
||||
EXAMPLE_VERDICT = SKILL_DIR / "references" / "example-verdict.json"
|
||||
#: Every shared Agent Skill the framework-neutrality sweep covers, ENUMERATED rather than globbed —
|
||||
#: "a new spec file is guarded explicitly, never implicitly", the rule this file already states for
|
||||
#: the two specs. The enumeration is kept honest by the coverage arm at the bottom of this module:
|
||||
#: a third skill arriving in a subtree pull turns THAT red until it is listed here, so explicit
|
||||
#: never degrades into stale (the ``_LIVE_DOCS`` coverage pattern).
|
||||
GUARDED_SKILL_DIRS = (SKILL_DIR, SKILLS_ROOT / "falsification-reviewer")
|
||||
BUNDLE_DIR = REPO_ROOT / "shared" / "examples" / "bygg-energi-mikro"
|
||||
|
||||
# Name-shaped framework guard (stricter than the persona test's import-shaped guard): the spec's
|
||||
|
|
@ -190,8 +197,15 @@ def test_method_spec_is_framework_neutral() -> None:
|
|||
NAME a concrete agent framework or vendor stack. Mirrors the persona SKILL.md rule; stricter
|
||||
than the import-shaped guard because the specs are pure prose (no AST to parse). RED the moment
|
||||
a framework name leaks into the shared method prose. Covers BOTH shared specs: the method spec
|
||||
and the ingest spec (I1) — a new spec file is guarded explicitly, never implicitly."""
|
||||
for f in [SPEC_PATH, INGEST_SPEC_PATH, *sorted(p for p in SKILL_DIR.rglob("*") if p.is_file())]:
|
||||
and the ingest spec (I1) — a new spec file is guarded explicitly, never implicitly.
|
||||
|
||||
Amendment 2 (2026-09-02) widened the skill half from ``expert-reviewer`` alone to
|
||||
``GUARDED_SKILL_DIRS``: the falsification-reviewer skill arrived by subtree pull with NO
|
||||
framework guard anywhere, and a sweep pinned to one skill silently stops covering the tree it
|
||||
claims to cover."""
|
||||
guarded = [f for d in GUARDED_SKILL_DIRS for f in sorted(d.rglob("*")) if f.is_file()]
|
||||
assert guarded, "the sweep found no skill files — it would be green by construction"
|
||||
for f in [SPEC_PATH, INGEST_SPEC_PATH, *guarded]:
|
||||
assert f.is_file(), f"expected shared artifact missing: {f}"
|
||||
hit = _FRAMEWORK_NAMES.search(f.read_text(encoding="utf-8"))
|
||||
assert hit is None, f"framework name {hit.group(0)!r} in shared method prose: {f}"
|
||||
|
|
@ -342,3 +356,23 @@ def test_ingest_spec_documents_every_contract_field() -> None:
|
|||
# The §11 golden extraction case layout.
|
||||
for entry in ("manifest.json", "fixture/", "ingested-at.txt", "expected-bundle/"):
|
||||
documented(entry, "golden extraction case")
|
||||
|
||||
|
||||
def test_every_shared_skill_is_covered_by_the_framework_sweep() -> None:
|
||||
"""Fail-closed coverage for Amendment 2's enumeration.
|
||||
|
||||
``GUARDED_SKILL_DIRS`` is written out by hand, following this file's own rule that a new shared
|
||||
artefact is guarded explicitly rather than by a glob that quietly absorbs it. The cost of an
|
||||
explicit list is that it goes stale, and ``shared/`` is a PULL-ONLY subtree — a third skill can
|
||||
arrive here without anyone in this repo deciding to add it. This arm makes staleness LOUD: the
|
||||
enumeration must name every directory under ``shared/skills/``, so the next pull that brings a
|
||||
skill turns this red until it is listed. Mirrors
|
||||
``test_every_document_is_classified_live_or_archive``.
|
||||
"""
|
||||
on_disk = {d for d in SKILLS_ROOT.iterdir() if d.is_dir()}
|
||||
assert on_disk, "no shared skills found — the control cannot distinguish covered from empty"
|
||||
missing = sorted(d.name for d in on_disk - set(GUARDED_SKILL_DIRS))
|
||||
assert not missing, (
|
||||
"shared skills not named in GUARDED_SKILL_DIRS, so the framework sweep does not reach "
|
||||
f"them: {missing}"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue