Skip to content

docs: refresh the GLPI agent skills to 0.4.1 - #21

Merged
baraline merged 8 commits into
mainfrom
docs/glpi-skills-refresh
Aug 3, 2026
Merged

docs: refresh the GLPI agent skills to 0.4.1#21
baraline merged 8 commits into
mainfrom
docs/glpi-skills-refresh

Conversation

@baraline

@baraline baraline commented Aug 3, 2026

Copy link
Copy Markdown
Owner

skills/ teaches an agent how to call this package. Nothing imported it, no
build read it, and neither mypy nor ruff nor Sphinx looked at it. It had rotted
three ways at once, and none of them broke anything anyone would notice.

What was wrong

Six of the seven published skills did not load. The published copies opened
with a UTF-8 BOM. Frontmatter is recognised by the file starting with ---,
so three invisible bytes cost each skill its name and description — and the
description is the field an agent reads to decide whether a skill is relevant at
all. Only glpi-client-setup, the one file without a BOM, ever reached a
session.

Twenty-five public methods were named by no skill anywhere — the whole
knowledge base family (18) and the whole Fields-plugin family (7). The skills
declared version: 0.3.0/0.4.0; the package is at 0.4.1.

Thirty-two claims in the remaining seven were false or stale, and the
routing agent's error contract described a different library: requests
exceptions (requests is not a dependency — the package uses httpx),
tenacity.RetryError (which cannot escape, the transport sets reraise=True),
and bare ValueError for 4xx instead of the GlpiStatusError hierarchy.

What changed

  • A coverage tripwire — every public client method must be named as a whole
    word by some skill, with an allowlist that starts empty. This is what would
    have caught the original gap: every existing check validated what the skills
    said, and none asked what they omitted.
  • Two new skills. glpi-knowledge-base (18 methods) carries the gotcha that
    categories cannot be written through v2 at all — they go through the legacy v1
    API and raise a plain RuntimeError, not a GlpiError, and a failed create is
    not rolled back. glpi-plugin-fields (7 methods) documents that the Fields
    plugin is absent from the v2 contract entirely, so every method needs a v1
    session, and that values has two different shapes depending on which helper
    you call.
  • The seven older skills corrected, including a from_env snippet that did
    not compile (async with at module level), glpi-ticket-workflow claiming
    "there is no batch iterator" when iter_search_tickets exists, and
    get_ticket_statistics silently truncating at one page of 200.
  • The agent's error contract rewritten to the hierarchy the package actually
    raises, including the three plain RuntimeError sites that are not GlpiError.
  • An absolute BOM guard in the marketplace drift checker. The existing
    BOM ONLY verdict was comparative, so a BOM faithfully copied into both
    trees read as IN SYNC — which is exactly the state six skills were in while
    not loading at all.

Verification

  • pytest -m "not integration" --cov --cov-fail-under=95 → 938 passed, 97.11%
  • check_skill_drift.py → exit 0, 9/9 in sync, no BOM offenders
  • BOM gate proven non-vacuous by planting probes in three plugin directories
    (exit 1, all named) and removing them (exit 0)
  • All nine skills at 0.4.1; zero BOMs in either tree; every python block parses
  • Every method, model and enum member named in a skill verified against
    inspect.signature and __all__

Known follow-ups (not blocking)

  • check_skill_drift.py's _plugin_dirs() returns [] when no plugin.json is
    found and main() has no floor check, so the BOM sweep could go vacuous while
    still exiting 0. Harmless today; one line to fix.
  • Separately, and outside this branch: get_ticket_statistics reads one page of
    200 with no pagination (_statistics.py:402-405), yet CHANGELOG.md:59-60
    attributes a live 16,245-vs-1,552 ticket measurement to it. Those cannot both
    be true.

🤖 Generated with Claude Code

baraline and others added 8 commits August 3, 2026 13:53
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ery helper

The empty-search probe raised on the path it was meant to explain: the
call was bare, so a legitimately empty search became an unhandled
GlpiNotFoundError, and a 404 cannot separate an absent route from an
absent id anyway. Only a successful fetch is informative, so the
branches are inverted and wrapped.

Gotcha 13 listed the union of _KBArticleRevisionRef and
_KBArticleTranslationRef as one attribute set; revisions[0].name raises
AttributeError. Split them.

create_with_categories took the category ids twice, so passing them
only as the argument left the except branch dead; derive them from
article.categories instead, and state that the retry presupposes a
configured v1 session.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Widen glpi-client-setup's frontmatter description to name the v1 session
generally, so agents asking about KB category writes or the Fields plugin
can match it; qualify the GlpiError claim and name the exempt RuntimeError
and TypeError sites; note skip_entity on iter_search_users; exclude
extra_payload from "every field defaults to None"; and soften the
link_ticket_timeline_document claim, which extra_payload disproves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Seven per-task refreshes each passed review, but nothing reconciled the
nine skills as a set. A fact verified once was left out of the skills
where it bites hardest.

The 4xx-swallowing search contract is library-wide, not a KB quirk.
_transport.py checks the response status only when the caller passes a
failure_message, and _http.list_payload_items coerces a non-list payload
-- a GLPI error object -- to []. All seven search_* helpers pass no
failure_message and swallow; all seven list_* helpers pass one and raise.
State it as library-wide in glpi-knowledge-base and add it to
glpi-ticket-workflow, glpi-document-workflow and
glpi-user-location-provisioning, where the find-or-create example shipped
the exact bug: an empty result from a rejected filter or a 403 fired the
else branch and created a duplicate. That example is now a named helper
with two guards, both failing closed.

Also: qualify the two unqualified claims that link_ticket_timeline_document
attaches an existing document (PostTimelineDocument declares only
extra_payload and timeline_position, and the URL carries only the ticket
id); soften the README clause that glpi-client-setup contradicts; drop the
hardcoded entities_id=0 that pinned every created plugin-fields row to
entity 0 while the adjacent comment claimed the opposite; correct the
claim that an empty get_ticket_custom_fields dict means "no rows anywhere"
when it means that or "no containers"; give the four update_ticket_*
methods their real signatures and name PatchFollowup, PatchTicketTask,
PatchSolution and PatchTeamMember; document the five exported enums no
skill named; and fix the two list_item_plugin_field_rows inaccuracies
(no row_id parameter, and its parameters are POSITIONAL_OR_KEYWORD).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.10%. Comparing base (e6ed643) to head (2d09875).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #21   +/-   ##
=======================================
  Coverage   97.10%   97.10%           
=======================================
  Files          79       79           
  Lines        2422     2422           
=======================================
  Hits         2352     2352           
  Misses         70       70           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@baraline
baraline merged commit bed254a into main Aug 3, 2026
8 checks passed
@baraline
baraline deleted the docs/glpi-skills-refresh branch August 3, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants