Commit Graph

6 Commits

Author SHA1 Message Date
07ea0aeb17 fix(kyberforge): stop union-masking drift between vale-audit-prefilter manifests
hook_file_regexes() unioned the `files:` regex from .pre-commit-hooks.yaml
and .pre-commit-config.yaml before checking whether a probe path is in
scope of a kyberforge vale-audit-prefilter hook. That union let a probe
matching only the old, looser .pre-commit-hooks.yaml pattern pass even
after .pre-commit-config.yaml's copy of the same hook had been narrowed
(e.g. to require a `.agent.md` suffix) -- silently masking exactly the
kind of hook-rescoping drift this check exists to catch. Per ADR-0014
the two manifests are meant to exercise the same resolution path an
external consumer's hook would, so this divergence is real drift, not
noise.

hook_file_regexes() now takes the manifest path explicitly and caches
per (skill, manifest) pair instead of per skill, so each manifest's
regex set can be inspected on its own. The probe-validation loop
computes in_hooks/in_config independently via a new matches_any_regex()
helper. Probes carry a new third heredoc field, `shared` or
`hooks-only`: `shared` probes (a file shape genuinely covered by both
manifests, e.g. plugins/demo/.apm/agents/demo.agent.md) must agree
between the two or the check now fails with a drift error;
`hooks-only` probes (a Copilot .agent.md living outside this repo's
own plugins/.apm/ layout, and the legacy bare-`.md`-under-agents/ shape
kept only to exercise a distinct .vale.ini glob section in isolation)
are exempt, since .pre-commit-hooks.yaml is deliberately broader there
by design. The original "matches no regex in either manifest"
staleness check is unchanged.

Added case 11b to tests/test-check-vale-style-sync.sh: narrows a
fixture's local config regex further while leaving
.pre-commit-hooks.yaml untouched, and asserts the check now flags it.
Confirmed red against the pre-fix script before applying the fix.

Refs: #95
2026-08-13 22:07:22 +00:00
5e232503c4 feat(kyberforge): execute plugin-to-apm marketplace conversion
Why:
ADR-0015 established that Microsoft APM (apm.yml + .apm/) should replace
this repo's hand-authored plugin.json/marketplace.json model, with those
files becoming compiled output of `apm pack` instead of files edited by
hand via the (now-retired) plugin-author/marketplace-author skills.
Issue #90 was the deferred execution of that decision, gated on #88
(apm tooling) and #89 (apm-native agent-author/skill-author routing).

Implementation notes:
- All six plugins (bin, core, git, gitea, kyberforge, lint) now carry
  apm.yml + .apm/{skills,agents,hooks} as their authoring source. Skills
  moved with a plain git mv (content-identical across targets). Agents
  were re-authored, not moved: per ADR-0016, .apm/agents/*.agent.md
  compiles verbatim to both Claude and Copilot, so plugin-scope agents
  now carry only name/description/model/source_keys -- no tools: field,
  no Claude-only knobs (isolation, maxTurns, effort, memory,
  permissionMode).
- Root apm.yml registers all 7 marketplace packages (6 local plus
  mattpocock-skills as a remote entry) under versioning: per_package,
  matching this repo's existing independent-plugin-versioning practice.
- .claude-plugin/marketplace.json and every plugin's plugin.json are now
  apm-pack-compiled output, verified against the prior hand-maintained
  content: same names/descriptions/versions/licenses/authors, only
  cosmetic serialization differences (JSON key order, owner email vs.
  url, Unicode escaping).
- plugin-author and marketplace-author are retired now that apm-based
  authoring fully replaces their job; kyberforge bumped 1.3.1 -> 1.4.0
  for that removal, and the root marketplace catalog bumped
  0.3.1 -> 0.3.2 to match, per the version-bump convention now
  documented in apm-workflow's reference docs instead of a dedicated
  script (apm has no native version-bump automation).
- Fixed hardcoded pre-.apm/ path assumptions across
  .pre-commit-config.yaml, .pre-commit-hooks.yaml,
  scripts/check-scope-walkup-sync.sh, scripts/sync-vale-styles.sh,
  scripts/check-vale-style-sync.sh, six plugins' root plugin.json
  (stale skills/hooks/agents pointer fields that check-manifests.sh
  validates), and several tests/*.bats and tests/*.sh fixtures --
  including a bats REPO_ROOT relative-path depth bug (10 files, one
  extra .apm/ directory level to walk up) and a vale probe-path
  isolation regression introduced mid-fix.
- Corrected empirically-wrong assumptions surfaced this session in
  apm-workflow/apm-install's own reference docs: `apm marketplace
  package add` does not accept local paths (only owner/repo remote
  shorthand -- local packages are registered by editing apm.yml's
  marketplace.packages[] directly); `apm compile` is a consumer-side
  AGENTS.md/CLAUDE.md generator, not the plugin.json producer, and
  hard-fails on skill/agent-only packages without --clean; `apm plugin
  init <name>` nests a stray subdirectory when run with a positional
  name arg from inside a same-named directory; no native Copilot
  marketplace output profile exists; .mcp.json is merged into the
  compiled plugin.json content-aware and target-scoped, with no
  dependencies.mcp entry needed for simple passthrough; pipx is the
  correct pip fallback on externally-managed Python environments.
- Renamed agent-author's copilot.agent.md template asset to
  copilot.agent.md.template so apm compile's recursive *.agent.md glob
  stops misparsing the placeholder template as a real agent primitive.

Impact:
plugin.json and marketplace.json are compiled artifacts from here on --
editing them by hand is no longer the workflow; edit apm.yml/.apm/ and
run apm pack. CONTEXT.md's Plugin/Plugin marketplace glossary entries
reflect this. ADR-0001 is marked superseded, ADR-0006 moot, and
ADR-0010 updated for the new .apm/agents/ path (project/user scope
unaffected, per ADR-0016). Full local verification: claude plugin
validate --strict on all 6 plugins, apm audit --ci, apm marketplace
check, check-manifests.sh, and the full test suite (165/165 bats,
13/13 shell scripts) all pass clean.

Fixes: #90
Refs: #88, #89
ADR: 0015
ADR: 0016

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ub96PyaSRD9BHPktotj1pC
2026-08-12 18:21:24 +00:00
cf5de2bd87 fix(lint): fail loudly on a nonexistent REPO_ROOT in check-vale-style-sync.sh
A bad or stale REPO_ROOT argument fell through to the "neither copy
present" no-op guard and exited 0 — the exact "clean result can mean
nothing was checked" anti-pattern this PR spent multiple review rounds
eliminating elsewhere. That guard exists for a repo that legitimately has
no kyberforge plugin installed, not for a typo'd path.

Only the documented manual-invocation mode was affected: the shipped
pre-push hook always calls this script with zero args, which resolves via
`git rev-parse --show-toplevel` and is always valid inside a repo.

Added a regression test asserting a nonexistent REPO_ROOT exits non-zero.

Refs: #85
2026-08-10 07:45:58 +00:00
d25355077f fix(lint): attribute Vale alerts per hook and cover .vale.ini in the sync check
The external-consumer test asserted a combined alert count (>=2) across both
shipped Vale hooks, but the SKILL.md fixture alone raises two alerts — so one
working hook satisfied the threshold. Retargeting agent-audit's glob to match
nothing left the suite reporting "3 passed" under the message "both hooks
flatten and flag". The Skipped guard does not catch this: the hook still
matches the file, Vale lints nothing, reports 0 errors in 1 file and exits 0,
which pre-commit renders as Passed. An assertion aggregating over N subjects
proves nothing about any individual subject.

Each hook now runs individually and its alerts are attributed to the nearest
preceding path header, so an alert is checked by path rather than by presence
in the combined blob. The two fixtures carry distinct VagueWording tokens, so
one hook's alert cannot be credited to another.

Nothing in the repo read either .vale.ini — the sync check diffed only
vale-wrap.sh and styles/Kyberforge, so a one-line glob typo silently disabled
the prefilter for a whole file type. That was the enabling half of the same
defect. The check now asserts the shared lines both copies must carry
(StylesPath, a section naming Kyberforge as a whole word) without flagging
their intentional divergence, and probes each glob section by asking Vale
itself to lint a representative path. Regex-to-glob comparison was rejected as
it means reimplementing doublestar semantics in bash; a file-count dry-run was
rejected because a section whose glob matches but whose BasedOnStyles lost
Kyberforge reports "1 file" with no alerts and would pass it.

Every new assertion is bound to a failing case in both directions: breaking the
artifact fails the suite, and neutering the assertion fails exactly one case.
That reverse sweep exposed two assertions bound to no failing case at all, one
masked by a stronger check running first.

Refs: #85
2026-08-09 17:23:10 +00:00
714e8a0c78 fix(lint): fail the style-sync check when one copy is missing
The guard used `||`, so exactly one of the two audit skill directories
missing also exited 0, where the intended silent no-op is both absent.
A renamed skill-audit reported green instead of flagging that a
canonical style copy had lost its counterpart.

One-present now exits 1 naming the missing side and the remedy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCQ648fLSFXPHGZdQ8gn58
2026-08-09 13:06:36 +00:00
1164f3abad fix(lint): make Vale prefilter portable via the plugin
skill-audit/agent-audit's Step 1 resolved vale-wrap.sh/.vale.ini via
`git rev-parse --show-toplevel`, which returns whichever repo the skill
happens to run in. Inside ai-development that works; in any external repo
that installs kyberforge@holocron as a plugin, it resolves to that repo's
own root, which has no .vale.ini — the prefilter silently fell back to
full LLM judgment. ADR-0013 named this as a deliberately deferred gap.

Vale's config/styles/wrapper now ship inside the plugin itself: a
canonical copy in agent-audit/assets/vale/ (Kyberforge + KyberforgeCopilot,
the superset agent-audit needs) and a smaller duplicate in
skill-audit/assets/vale/ (Kyberforge only) — per the no-cross-skill-path
rule already established for plugin cache-installs. Both skills resolve
these relative to their own directory, same as scripts/validate.sh
already does.

A new root .pre-commit-hooks.yaml exposes both copies plus
skill-size-check so any external repo can enforce the same rules via
`repo: <this-repo-url>, rev: <tag>` in its own pre-commit config,
independent of Claude Code entirely — the same mechanism covers CI. This
repo's own pre-commit hook now consumes the identical plugin-bundled
copies via repo: local (not a third root copy, and not a pinned
self-reference, which would lint working-tree edits against the last
tagged release instead of the change being made). Split into
vale-audit-prefilter-skill/-agent hooks after confirming, by diffing the
full corpus against both old and new config before deleting the old
files, that one combined hook pointed at only one copy silently 0-file-
skips the other file type.

scripts/check-vale-style-sync.sh guards the two copies against drift,
wired at pre-push alongside check-manifests.

ADR: 0014
2026-08-09 10:04:19 +00:00