Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
"""Regression tests for issue #92993 — `curator pin` false success + invisible pin.
|
||||
|
||||
Two stacked defects, each with its own failing assertion:
|
||||
|
||||
1. **False success (write-path).** `_cmd_pin` gates on `is_agent_created()`
|
||||
and then prints "pinned" unconditionally, but `set_pinned()` routes through
|
||||
`_mutate(require_curation_eligible=True)`, which silently no-ops when
|
||||
`is_curation_eligible()` is False. A skill can pass the first gate and fail
|
||||
the second; the user sees "pinned" while nothing was written. The CLI must
|
||||
detect the failed write and exit nonzero with a clear error.
|
||||
|
||||
2. **Invisible pin (visibility).** `curator status` reads pins from
|
||||
`curated_report()`, which only iterates `list_agent_created_skill_names()` —
|
||||
and that list requires the `created_by: agent` management marker. A skill
|
||||
that IS curation-eligible but carries no marker (pre-provenance record,
|
||||
foreground-created) never appears as a row, so its pin is silently absent
|
||||
from the status output even when successfully written.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import io
|
||||
from argparse import Namespace
|
||||
from contextlib import redirect_stdout
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _make_skill(skills_dir: Path, name: str):
|
||||
d = skills_dir / name
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
(d / "SKILL.md").write_text(
|
||||
f"---\nname: {name}\ndescription: x\n---\n", encoding="utf-8",
|
||||
)
|
||||
return d
|
||||
|
||||
|
||||
class _Args:
|
||||
def __init__(self, skill: str):
|
||||
self.skill = skill
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pin_env(tmp_path, monkeypatch):
|
||||
"""Isolated HERMES_HOME with freshly reloaded modules (same pattern as
|
||||
tests/agent/test_curator.py::curator_env but scoped for the CLI layer)."""
|
||||
home = tmp_path / ".hermes"
|
||||
skills = home / "skills"
|
||||
skills.mkdir(parents=True)
|
||||
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
||||
monkeypatch.setenv("HERMES_HOME", str(home))
|
||||
|
||||
import hermes_constants
|
||||
importlib.reload(hermes_constants)
|
||||
from tools import skill_usage
|
||||
importlib.reload(skill_usage)
|
||||
# curator module reads config through its own loader; keep defaults.
|
||||
from agent import curator
|
||||
importlib.reload(curator)
|
||||
monkeypatch.setattr(curator, "_load_config", lambda: {})
|
||||
monkeypatch.setattr(skill_usage, "_prune_builtins_enabled", lambda: False)
|
||||
from hermes_cli import curator as curator_cli
|
||||
importlib.reload(curator_cli)
|
||||
|
||||
yield {
|
||||
"home": home,
|
||||
"skills": skills,
|
||||
"usage": skill_usage,
|
||||
"cli": curator_cli,
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 1 — pin must fail loudly when the write does not land
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_pin_fails_loudly_when_write_does_not_land(pin_env, capsys, monkeypatch):
|
||||
"""A skill that passes `is_agent_created()` but fails
|
||||
`is_curation_eligible()` must produce a NONZERO exit and an explanatory
|
||||
error — not a success message over a silent no-write.
|
||||
|
||||
Real trigger: PROTECTED_BUILTIN_SKILLS blocks by NAME. A user's own skill
|
||||
whose name collides with a protected entry is not in the bundled
|
||||
manifest, so ``is_agent_created()`` says True — but
|
||||
``is_protected_builtin()`` makes it ineligible, and ``set_pinned()``
|
||||
silently no-ops through ``_mutate(require_curation_eligible=True)``.
|
||||
|
||||
The shipped set is currently empty (``plan`` graduated to a built-in
|
||||
command), so the collision is staged with a monkeypatched sentinel."""
|
||||
env = pin_env
|
||||
name = "sentinel-protected-skill" # collides with the (patched) protected name
|
||||
monkeypatch.setattr(env["usage"], "PROTECTED_BUILTIN_SKILLS", {name})
|
||||
|
||||
_make_skill(env["skills"], name)
|
||||
|
||||
# Sanity: the two gates genuinely disagree for this skill.
|
||||
assert env["usage"].is_agent_created(name) is True
|
||||
assert env["usage"].is_curation_eligible(name) is False
|
||||
|
||||
cli = env["cli"]
|
||||
buf = io.StringIO()
|
||||
with redirect_stdout(buf):
|
||||
rc = cli._cmd_pin(_Args(name))
|
||||
out = buf.getvalue()
|
||||
|
||||
# The bug: rc == 0 with "pinned" printed although nothing was written.
|
||||
assert rc != 0, (
|
||||
f"_cmd_pin reported success (rc=0, output={out!r}) for a skill whose "
|
||||
"pin write silently did not land — this is the false-success defect."
|
||||
)
|
||||
# Record on disk must NOT claim a pin.
|
||||
rec = env["usage"].get_record(name)
|
||||
assert not rec.get("pinned"), "usage record claims pinned=true despite failed eligibility"
|
||||
# Error output must explain the refusal, not just fail quietly.
|
||||
assert "pin" in out.lower(), "refusal must mention the pin outcome"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 2 — successful pin on eligible-but-unmanaged skill must be VISIBLE
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_pinned_eligible_unmanaged_skill_visible_in_status(pin_env):
|
||||
"""A curation-ELIGIBLE skill without the created_by marker (foreground-
|
||||
created / pre-provenance) can be pinned successfully — and once pinned,
|
||||
it MUST surface in `curator status` output. Before the fix it vanishes:
|
||||
curated_report() skips it because list_agent_created_skill_names()
|
||||
requires the management marker."""
|
||||
env = pin_env
|
||||
usage = env["usage"]
|
||||
cli = env["cli"]
|
||||
|
||||
_make_skill(env["skills"], "legacy-skill")
|
||||
# No mark_agent_created() call — deliberately unmanaged-but-eligible.
|
||||
|
||||
buf = io.StringIO()
|
||||
with redirect_stdout(buf):
|
||||
rc = cli._cmd_pin(_Args("legacy-skill"))
|
||||
out = buf.getvalue()
|
||||
|
||||
# The pin MUST succeed for an eligible skill — unconditional, so a
|
||||
# regression that flips pin to failure fails here instead of silently
|
||||
# skipping the visibility assertions below.
|
||||
assert rc == 0, f"eligible-skill pin must succeed (got rc={rc}): {out.strip()}"
|
||||
rec = usage.get_record("legacy-skill")
|
||||
assert rec.get("pinned") is True, "eligible skill pin wrote nothing despite rc=0"
|
||||
|
||||
# ...and then status MUST show it. This is the visibility half.
|
||||
buf2 = io.StringIO()
|
||||
with redirect_stdout(buf2):
|
||||
status_rc = cli._cmd_status(Namespace())
|
||||
assert status_rc == 0
|
||||
status = buf2.getvalue()
|
||||
assert "legacy-skill" in status and "pinned" in status.lower(), (
|
||||
"pin landed on disk but the skill is absent from `curator status` "
|
||||
"— the invisible-pin defect."
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 3 — regression guard: normal managed pin still works end to end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_pin_managed_skill_end_to_end(pin_env):
|
||||
"""The happy path must stay green: a properly adopted (managed) skill
|
||||
pins, reports success, and shows in status. Guards against fixing the
|
||||
two defects by breaking legitimate pins."""
|
||||
env = pin_env
|
||||
usage = env["usage"]
|
||||
cli = env["cli"]
|
||||
|
||||
_make_skill(env["skills"], "managed-skill")
|
||||
usage.mark_agent_created("managed-skill")
|
||||
|
||||
buf = io.StringIO()
|
||||
with redirect_stdout(buf):
|
||||
rc = cli._cmd_pin(_Args("managed-skill"))
|
||||
assert rc == 0, "managed skill pin must succeed"
|
||||
assert "pinned" in buf.getvalue().lower()
|
||||
|
||||
assert usage.get_record("managed-skill").get("pinned") is True
|
||||
|
||||
buf2 = io.StringIO()
|
||||
with redirect_stdout(buf2):
|
||||
assert cli._cmd_status(Namespace()) == 0
|
||||
status = buf2.getvalue()
|
||||
assert "managed-skill" in status
|
||||
assert "pinned" in status.lower(), (
|
||||
"managed+pinned skill missing from the pinned list in status output"
|
||||
)
|
||||
Reference in New Issue
Block a user