feat(skills): add adapted skill rebasing machinery - #17
Merged
Conversation
Port the captain's prior art from Quidge/ltf4 for vendoring agent skills that absorb upstream updates while keeping local deviations honest, with two intended changes from ltf4: - Colocate the script inside the skill dir at scripts/skill-adaptation.py so it travels with a vendored skill, instead of a repo-level scripts/. - Add a read-only `audit` subcommand: per attribution it resolves the pinned base tree, fetches base files at that SHA deterministically, computes `ours - base` per path, and presents those differences beside the declared `## Deviations` bullets for the agent to correlate. It performs no heuristic bullet-to-delta matching; its exit code flags the two provable rot states (all-drift-no-policy, all-policy-no-drift) and presents both sides otherwise. `-q` is a quiet predicate; a base that cannot be fetched fails loudly (exit 3) rather than reporting clean. The provenance model matches issue #15: pins are ADAPTATION.md front-matter `attributions` (full-SHA GitHub tree URLs), the `## Deviations` body is read as merge policy, and provenance lives only in ADAPTATION.md, never in SKILL.md. The script stays deliberately dumb; the agent drives the 3-way merge. SKILL.md is authored per the writing-for-agents tenets and runs audit as the first rebase step so the merge starts from an honest ledger, with a standalone audit entry as well. As firstmate's own machinery this skill carries metadata.internal: true and has no ADAPTATION.md of its own. Tests exercise the audit logic deterministically and offline through the executable via a SKILL_ADAPTATION_BASE_DIR base cache, covering the `ours - base` diff, undeclared-drift and stale-bullet detection, the mixed both-sides presentation, the exit codes, the -q predicate, multi-attribution drift, and hard errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Build Part 1 (machinery only) of the rebasing-adapted-skill firstmate skill per settled design in GH issue #15: a firstmate SHARED-TRACKED, agent-only skill for vendoring agent skills that absorb upstream updates while keeping local deviations honest. This is a faithful port of prior art in Quidge/ltf4 (.agents/skills/rebasing-adapted-skill/SKILL.md and scripts/skill-adaptation.py, a
uv runself-contained script with dep pyyaml) with exactly two intended changes and no new architecture. Do NOT vendor mattpocock's writing-for-agents here; that is Part 2's separate PR (scope guard: no skill under .agents/skills/writing-for-agents/).Provenance model (implemented exactly per #15): provenance lives ONLY in ADAPTATION.md (sibling to SKILL.md), never stamped into SKILL.md. Pins = ADAPTATION.md YAML front-matter
attributions:as full-40-char-SHA GitHub tree URLs (immutable; no branch/tag refs; multiple allowed). The## Deviationsbody section is a natural-language merge-policy ledger (keep what a bullet protects; silence = match upstream), not a changelog. The script is deliberately dumb; the AGENT does the semantic 3-way merge (base=pinned SHA, ours=current, theirs=chosen new SHA), shows a plan and waits for approval before writing, and advances pins + refreshes the ledger only on intent change; ADAPTATION.md is never taken from upstream.Change 1 (deliberate, for vendorability): the script is colocated INSIDE the skill dir at .agents/skills/rebasing-adapted-skill/scripts/skill-adaptation.py so it travels with a vendored skill, NOT at a repo-level scripts/. It stays a
uv runself-contained script with dep pyyaml only; the new network fetch uses only stdlib urllib/json/os, adding no dependency.Change 2 (additive): a new read-only
audit <skill-dir> [-q]subcommand alongside the ported, byte-faithfultemplate <tree-url>...andvalidate-skill-dir <dir> [-q]. Per attribution, audit resolves the pinned base tree and fetches base files at that exact SHA deterministically (GitHub git/trees + raw at the SHA; or an offline SKILL_ADAPTATION_BASE_DIR cache tree), computesours - baseper path (scoped to base-present paths: modified + locally-removed, which are exactly the paths a future rebase's silence=match-upstream would silently clobber; ADAPTATION.md is always excluded; local-only additions stay local and are out of scope), and PRESENTS both sides for the agent to correlate: the deterministic differences AND the declared## Deviationsbullets. Per the confirmed open refinement in #15, it does NO heuristic bullet-to-delta matching. Its exit code flags only the two provable rot states -- undeclared drift (differences with zero declared bullets) and stale deviations (bullets with zero differences) -- and in the mixed case exits 0 while presenting both sides for the agent to pair.-qis a quiet predicate; a base that cannot be fetched fails loudly with a dedicated exit code (3) rather than reporting clean. This coarse-gate limitation (the script cannot catch a single new undeclared drift when other bullets already exist; the agent does that correlation from the presentation) is intentional and documented in --help and SKILL.md.Deliberate decisions a diff-only reviewer would not know: (a)
templateandvalidate-skill-dirare ported unchanged from ltf4 on purpose -- onlyauditand the script location are new; do not flag them as reinvented. (b) Naming follows #15 exactly:ADAPTATION.mdand the## Deviationsheading (singular-cased as written), not pluralized/upper-case variants. (c) An angle-bracket template placeholder bullet is intentionally NOT counted as a real declared deviation. (d) This skill is firstmate's OWN machinery, so its SKILL.md carriesmetadata.internal: trueanduser-invocable: false, and it has NO ADAPTATION.md of its own (per #15 Q5: firstmate-own skills omit ADAPTATION.md; ADAPTATION.md presence is the orthogonal upstream-tracked signal for vendored skills). Because of this, runningvalidate-skill-diragainst THIS skill dir correctly reports "not adapted" -- that is right behavior, not a bug -- so the "validate-skill-dir passes on the new dir" acceptance is satisfied by validating a scaffolded ADAPTED dir in the tests, following the issue over the literal acceptance line and flagging the nuance. (e) A one-line section-13 load trigger was added to AGENTS.md per firstmate-coding-guidelines trigger hygiene (agent-only reference skill).CAPTAIN REQUEST -- evaluate the SKILL.md prose (and any prose I authored) against mattpocock's writing-for-agents tenets, which are explicit review criteria for this change: (1) context pointers -- sharp description wording, front-loaded leading word, one trigger per branch, cut identity the body already carries; (2) the two loads -- justify context load (always-loaded description/AGENTS.md line) vs cognitive load; (3) information hierarchy + progressive disclosure -- in-file steps first, reference on demand, disclose only what some branches need, inline what every branch needs; (4) co-location -- a concept's definition/rules/caveats under one heading; (5) no sprawl -- every line live and unique; (6) steps end on clear, checkable, exhaustive completion criteria (avoid premature completion); (7) demand that drives legwork ("every difference", "every attribution"); (8) split only when the cut earns it; (9) leading words over restatement (rebase, base/ours/theirs, policy, honest ledger, drift, stale); (10) positive phrasing over negation; (11) pruning -- single source of truth (provenance only in ADAPTATION.md; mechanics pointed at --help, not restated), environment as source of truth, relevance, no no-ops. The review SHOULD assess SKILL.md against these named criteria (implementer applied them; reviewer verifies).
Constraints honored: firstmate-coding-guidelines (one sentence per line in tracked Markdown, plain dash not em dash, no agent commit co-author, shellcheck-clean via bin/fm-lint.sh, colocated tests/.test.sh exercising behavior through the executable interface only). Tests are deterministic and offline via a SKILL_ADAPTATION_BASE_DIR base cache and cover the
ours - basediff, undeclared-drift (modified and removed), stale-bullet detection, the mixed both-sides presentation, placeholder filtering, the -q predicate, fetch-failure loud-fail, multi-attribution drift, and hard errors. The Python script is ruff-clean.What Changed
ADAPTATION.md.Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ **Rebase** - passed
✅ **Review** - completed
✅ **Test** - passed
✅ **Document** - passed
✅ **Lint** - passed
✅ **Push** - passed