Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .claude/scripts/authoring-surface-provenance.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
#
# Guards § Agent definition locations against naming a SYNCED file as an authoring
# surface.
#
# `plugins/*/skills/*/SKILL.md` in devantler-tech/agent-plugins carry
# `metadata.github-repo` provenance and are re-pulled by the daily
# `update-agent-skills` workflow. A hand-edit there survives until the next sync and
# then silently disappears — no conflict, no CI failure, no signal. The sibling
# `plugins/*/agents/*.agent.md` carry no provenance and ARE authored there, so
# "editable here?" is a per-file question and a per-directory rule gets it wrong.
#
# The contract named the synced agent-improvement SKILL.md as a version-controlled
# authoring target (caught 2026-07-25 on agent-plugins#89, rated Major), which would
# route a generic skill change into a file that reverts it. This pins that shut.

set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
constitution="${repo_root}/AGENTS.md"

fail() {
echo "authoring-surface provenance: FAIL — $*" >&2
exit 1
}

[ -f "${constitution}" ] || fail "missing ${constitution}"

# Flatten so a rule wrapped across lines is still matched (prose guards must
# flatten whitespace — a line-anchored grep silently passes on reflowed text).
flat="$(tr '\n' ' ' <"${constitution}" | tr -s ' ')"

section="$(printf '%s' "${flat}" |
sed -n 's/.*### Agent definition locations\(.*\)### Authority model.*/\1/p')"
[ -n "${section}" ] || fail "could not isolate § Agent definition locations"

# 1. No CONCRETE bundled skill path may be named as an authoring surface. The glob
# form `plugins/*/skills/*/SKILL.md` is the warning itself and must stay allowed,
# so match only path segments that name a real plugin and a real skill.
if printf '%s' "${section}" |
grep -qE 'plugins/[A-Za-z0-9_.-]+/skills/[A-Za-z0-9_.-]+/SKILL\.md'; then
fail "§ Agent definition locations names a concrete bundled
plugins/<plugin>/skills/<skill>/SKILL.md path as an authoring surface.
Those files are SYNCED from their metadata.github-repo upstream and an edit there is
silently reverted. Name the upstream authoring repo instead (agent-skills)."
fi

# 2. The true upstream for skill bodies must be named, by its FULL slug. A bare
# `agent-skills` would be satisfied by the `update-agent-skills` workflow name
# that this same paragraph mentions — a vacuous assertion.
printf '%s' "${section}" | grep -q 'devantler-tech/agent-skills' ||
fail "§ Agent definition locations must name devantler-tech/agent-skills as the
authoring home for generic skill bodies."

# 3. The sibling agents file genuinely IS authored in agent-plugins — keep it named,
# so a fix for (1) does not over-correct and strand the agent definition.
printf '%s' "${section}" | grep -q 'agents/agent-improver\.agent\.md' ||
fail "§ Agent definition locations must still name
plugins/agentic-engineering/agents/agent-improver.agent.md, which carries no
provenance and is authored in agent-plugins."

# 4. The per-file provenance test must be stated, not just the conclusion — the
# directory mixes both kinds, so the next new file needs the check, not the answer.
printf '%s' "${section}" | grep -q 'metadata\.github-repo' ||
fail "§ Agent definition locations must state the per-file provenance check by its
exact key (metadata.github-repo) rather than only its conclusion for today's files."

echo "authoring-surface provenance: OK — 4 assertions passed"
22 changes: 22 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
board-add: ${{ steps.filter.outputs.board-add }}
flow-scorecard: ${{ steps.filter.outputs.flow-scorecard }}
memory-hygiene: ${{ steps.filter.outputs.memory-hygiene }}
authoring-surface: ${{ steps.filter.outputs.authoring-surface }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -159,6 +160,12 @@ jobs:
- '.claude/scripts/product-value-contract.test.sh'
- 'docs/README.md'
- '.github/workflows/ci.yaml'
authoring-surface:
# The guard script itself is listed so a PR deleting it still runs it,
# and ci.yaml so removing the job below is covered the same way.
- 'AGENTS.md'
- '.claude/scripts/authoring-surface-provenance.test.sh'
- '.github/workflows/ci.yaml'

build-docs:
needs: changes
Expand Down Expand Up @@ -419,6 +426,21 @@ jobs:
- name: Verify the product value contract
run: bash .claude/scripts/product-value-contract.test.sh

test-authoring-surface-provenance:
name: Test authoring-surface provenance
needs: changes
if: needs.changes.outputs.authoring-surface == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Verify the authoring-surface provenance guard
run: bash .claude/scripts/authoring-surface-provenance.test.sh

test-self-review-contract:
name: Test self-review contract
needs: changes
Expand Down
16 changes: 12 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,18 @@ definition surface, and an installed/cache copy is never an authoring target.
script, the provider-neutral desired state, plugin settings, and the Cursor loader source. The local
Agent Improver agent/skill forks are retired, and so is the standalone FinOps agent fork — the
reviewed plugin is the source for both roles.
- The generic upstream source in `devantler-tech/agent-plugins`, specifically
`plugins/agentic-engineering/agents/agent-improver.agent.md`,
`plugins/agentic-engineering/skills/agent-improvement/SKILL.md`, the plugin README/desired state,
and their manifest/contract validation. Change generic behaviour there first, merge it, then update
- The generic upstream source — **in the repository that actually authors the file**, which differs
per file inside one plugin directory. In `devantler-tech/agent-plugins`:
`plugins/agentic-engineering/agents/agent-improver.agent.md`, the plugin README/desired state, and
their manifest/contract validation. **Skill bodies are NOT authored there:**
`plugins/*/skills/*/SKILL.md` carry `metadata.github-repo` provenance and are re-pulled by the
daily `update-agent-skills` workflow, so a hand-edit survives until the next sync and then
**silently disappears** — no conflict, no CI failure, no signal. Author them in the repo that
`github-repo` names, normally **`devantler-tech/agent-skills`** (the Agent Improver's skill is
`agent-improvement/SKILL.md` there), and let the sync carry the bundled copy down; the interval
where the bundled copy still reads the old wording is correct, not a miss. Provenance is a
**per-file** question — run `grep -l github-repo <path>` before editing anything under a plugin,
rather than trusting the directory. Change generic behaviour upstream first, merge it, then update
this consumer's `libraries/agent-plugins` gitlink and copied desired state.

**Runtime-local surfaces — back up before editing, verify in place, and record before/after in native
Expand Down
Loading