@graftsync/skill 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/SKILL.md +56 -51
  2. package/package.json +1 -1
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: graft-sync
3
- description: Keep local modifications ("grafts") to upstream npm packages working when upstream releases a new version. Use when a repo has a `.grafts/` directory and an upstream dependency was bumped, `graft sync` reported `needs-agent`, a graft's anchors miss or its oracle fails, or you are asked to sync, repair, add, or retire a graft.
3
+ description: Keep local modifications ("grafts") to upstream npm packages working when upstream releases a new version. Use when a repo has a `.grafts/` directory and an upstream dependency was bumped, `graft sync` reported `needs-agent`, a graft's anchors miss or its tests fail after an upgrade, or you are asked to sync, repair, add, or retire a graft.
4
4
  ---
5
5
 
6
6
  # Graft sync
@@ -17,19 +17,19 @@ https://github.com/brandonroberts/graft/blob/main/docs/spec.md
17
17
 
18
18
  `graft.md` frontmatter:
19
19
 
20
- - `rootstock.package` / `rootstock.verified` — upstream package and the last version the
21
- oracle passed against.
20
+ - `rootstock.package` / `rootstock.verified` — upstream package and the last version the graft
21
+ was verified against.
22
22
  - `mechanism` — how it is applied (e.g. `patch`: a pnpm `patchedDependencies` patch file).
23
23
  - `variants` — one implementation per upstream version range (`range` + `module` path).
24
24
  Ranges are disjoint; exactly one variant applies to any version.
25
- - `oracle` — the command that proves the invariants hold, run once per `mode`
26
- (with `GRAFT_MODE` and `GRAFT_UPSTREAM_VERSION` set).
27
25
  - `status` — `applied` | `conflicted` | `upstreamed` | `obsolete`.
28
26
 
29
27
  Body sections, all required — read them before touching anything:
30
28
 
31
29
  - **Why** — what the modification enables and why upstream doesn't do it.
32
- - **Invariants** — observable behaviors that must hold. **This is the contract.**
30
+ - **Invariants** — observable behaviors that must hold, and which of the project's tests cover
31
+ them. **This is the contract, and verifying it is your job:** Graft only checks that a graft
32
+ still *fits* (anchors, apply, install). It runs no tests.
33
33
  - **Where to look** — the upstream area, described by behavior so you can re-find it after
34
34
  a refactor. Trust this over file paths.
35
35
  - **Anchors** — what each variant matches on and why it was chosen.
@@ -51,7 +51,7 @@ the project root. Exit codes: `0` ok, `1` something needs attention, `2` tooling
51
51
  | Apply a variant to pristine upstream, for inspection | `graft preview <id> [--version X] [--out dir]` |
52
52
  | Register a new variant | `graft variant add <id> --range R --module ./variants/… --verify-version X --note "…"` |
53
53
  | Wire the package manager to the selected variants + reinstall | `graft apply` |
54
- | Prove it | `graft verify <id>` |
54
+ | Prove the Invariants | the project's own tests (see *Invariants*) |
55
55
  | Status, verified version, History | `graft mark <id> [--status S] [--verified X] [--history "…"]` |
56
56
  | Where a variant fits across published versions | `graft replay <id> [--range R]` |
57
57
 
@@ -65,7 +65,7 @@ Reports from `sync` land in `.grafts/.report/` (`sync.md`, `sync.json`, and per
65
65
  `verified` and wrote History. Only grafts marked `needs-agent` are yours.
66
66
  2. **Gather context for each `needs-agent` graft**, in this order:
67
67
  `graft show <id>` → the reasons in the sync report → `graft check <id> --version <target>` →
68
- `graft diff <id>` (from `verified` to the target) → the failing oracle output, if the oracle ran.
68
+ `graft diff <id>` (from `verified` to the target).
69
69
  3. **Locate the new equivalent code by behavior.** Use *Where to look* and *Anchors*, then
70
70
  the scoped diff. If a target file was removed or renamed, search the new version's
71
71
  published files for the behavior (the pristine copy is under `.grafts/.cache/upstream/`). Upstream
@@ -81,19 +81,21 @@ Reports from `sync` land in `.grafts/.report/` (`sync.md`, `sync.json`, and per
81
81
  refuses (and rolls back) if the anchors don't match at the target. Older open-ended ranges
82
82
  are narrowed automatically; existing variant files are never touched. Then
83
83
  `graft check <id> --version <target>` should be clean.
84
- 7. **Prove it:** re-run `graft sync --version <target> --install`. With anchors now matching,
85
- it wires the package manager to the new variant, installs the target, and runs the oracle
86
- in every mode; on green it sets `verified` and status `applied`. (Or install yourself, run
87
- `graft verify <id>`, then `graft mark <id> --status applied --verified <target>`.)
88
- 8. **Finish:** open a PR (see below).
84
+ 7. **Check it fits:** re-run `graft sync --version <target> --install`. With anchors now
85
+ matching, it wires the package manager to the new variant, installs the target, and on green
86
+ sets `verified` and status `applied`.
87
+ 8. **Prove the Invariants** at the target with the project's tests: run the ones *Invariants*
88
+ names (and the suite), and add a test for any Invariant nothing covers. Show it fails
89
+ without the graft. If a test fails, the graft isn't done, whatever sync said: fix the
90
+ variant, or stop (see *When to stop*).
91
+ 9. **Finish:** open a PR (see below), saying how you verified each Invariant.
89
92
 
90
93
  ## Adding a new graft
91
94
 
92
95
  When asked to capture a modification as a graft (new, or migrating an existing patch):
93
96
 
94
97
  1. **Scaffold:** `graft init --agent` (once; it also installs this skill), then `graft add <id> --package <pkg> --target <file>`.
95
- This creates `graft.md`, an empty variant stub (which fails loudly until written), and an
96
- oracle stub that exits 1.
98
+ This creates `graft.md` and an empty variant stub (which fails loudly until written).
97
99
  2. **Write the intent first,** with the person who owns the modification if you can: *Why*,
98
100
  the *Invariants* (observable behaviors, one per bullet), *Where to look* (by behavior), and
99
101
  *Anchors* (what the variant matches on and why).
@@ -106,23 +108,18 @@ When asked to capture a modification as a graft (new, or migrating an existing p
106
108
  (`pnpm patch-commit` may also write a `package.json` entry; the next step replaces it).
107
109
  4. **Apply it:** `graft apply`. This points the package manager at the
108
110
  variant, reinstalls, and re-checks anchors.
109
- 5. **Write the oracle** so it fails before the modification and passes after. Check that it
110
- fails without the graft. An oracle that can't fail proves nothing. Test the Invariants,
111
- plus at least one pre-existing behavior the graft must not break.
112
- - **Browser oracles** (Playwright etc.) fail on uncaught page errors, **`console.error`**
113
- messages, and failed (5xx) requests. Frameworks catch errors and only log them:
114
- Angular's `ErrorHandler`, React error boundaries, Vue's `errorHandler`. Listening only
115
- for page errors lets a broken page pass.
116
- - **Assert something visible that depends on the graft,** not only "no errors": a page can
117
- fail to render without throwing.
118
- - **Clean up what you start:** stop a dev server on *every* exit (`process.on('exit', …)`),
119
- because `process.exit` skips `finally` blocks and leaves the port taken.
120
- - **Test the code path, not the whole stack,** when the rest isn't what the graft changes.
121
- For a dependency's browser behavior, load it through the dev server (e.g. a one-line
122
- module importing it) instead of rendering pages through a framework's dev SSR.
123
- - **Cover the modes the bug lives in.** A dev-only bug needs a `dev` mode; a production
124
- build may behave differently (e.g. Vite replaces `process.env` only there).
125
- 6. **Prove it:** `graft verify <id>`, then `graft sync` (green).
111
+ 5. **Cover the Invariants with the project's tests.** Find the tests that exercise each
112
+ Invariant, or add them to the project's suite, and name them under *Invariants*. Show each
113
+ fails without the modification and passes with it: a test that can't fail proves nothing.
114
+ Also keep one pre-existing behavior the graft must not break covered.
115
+ - **Test what the graft changes, not the whole stack.** For a patch that stops a dependency
116
+ loading Node polyfills, assert it doesn't load them (e.g. via `require.cache`); you don't
117
+ need a browser or a build.
118
+ - **Browser tests** (Playwright etc.), when you need one, fail on uncaught page errors,
119
+ **`console.error`** messages and failed (5xx) requests: Angular's `ErrorHandler`, React
120
+ error boundaries and Vue's `errorHandler` catch errors and only log them. Assert something
121
+ visible that depends on the graft, and stop any server you start on every exit.
122
+ 6. **Check it:** `graft sync` (green), and the project's tests pass.
126
123
  7. **Find the variant's real range:** `graft replay <id>` checks every published version.
127
124
  Record what you learn in *History*.
128
125
 
@@ -130,12 +127,13 @@ When asked to capture a modification as a graft (new, or migrating an existing p
130
127
 
131
128
  - **Never edit an existing variant.** Older version ranges keep their variants and tests.
132
129
  Write a new file for the new range. `graft variant add` refuses to re-register a file.
133
- - **The Invariants are the contract and the oracle is the only proof.** Anchors matching or a
134
- clean apply is not success; a passing oracle is.
135
- - **Never weaken the oracle** to get green: don't skip, loosen, or delete checks, and don't
136
- change Invariants to match what you built. If an invariant is genuinely obsolete, say so
137
- and ask a human.
138
- - **Never set `verified`** to a version the oracle has not passed against.
130
+ - **The Invariants are the contract, and you verify them.** A green sync means the change still
131
+ fits and installs, not that it still works. Prove the Invariants with the project's tests.
132
+ - **Never weaken a test** to get green: don't skip, loosen, or delete checks, and don't change
133
+ Invariants to match what you built. If an invariant is genuinely obsolete, say so and ask a
134
+ human.
135
+ - **Never set `verified`** (or register a variant) for a version you haven't verified the
136
+ Invariants at.
139
137
  - **Fail loudly.** A graft that silently applies nothing is the worst outcome. Anchors must
140
138
  match exactly once; don't add fuzz, catch-alls, or "skip if not found" logic.
141
139
  - **Anchor by behavior.** Prefer stable exported names, public API lists, and user-facing
@@ -179,8 +177,8 @@ When asked to capture a modification as a graft (new, or migrating an existing p
179
177
  - If the graft's variants are generated, give it a `generator: { command }` in its
180
178
  frontmatter (`{version}` and `{out}` are substituted; e.g.
181
179
  `node tools/cjs-to-esm/make-variant.mjs semver {version} {out}`). `graft sync` then
182
- regenerates and registers a variant itself when the old one misses, and keeps it only if
183
- the oracle passes. If sync still hands the graft to you, its reasons say what the generator
180
+ regenerates and registers a variant itself when the old one misses, and keeps it only if it
181
+ fits and installs. If sync still hands the graft to you, its reasons say what the generator
184
182
  did; the rejected output is under `.grafts/.report/<id>/regenerated/`. Fix the generator
185
183
  or review that output rather than hand-patching the old variant.
186
184
 
@@ -204,13 +202,14 @@ When asked to capture a modification as a graft (new, or migrating an existing p
204
202
  `{ "ref": "graft/<id>-<target>", "base": "<target>" }`. Register it with
205
203
  `graft variant add <id> --range '>=<target>' --module ./variants/v<target>.json
206
204
  --verify-version <target>`.
207
- 4. Prove it with `graft sync --version <target> --install`.
205
+ 4. Check it with `graft sync --version <target> --install`, then prove the Invariants with the
206
+ project's tests.
208
207
  - **Repo mode** (`fork.install: none`): the fork is the product (e.g. a translated docs site),
209
- nothing is installed, and the oracle runs in the rebased checkout. The diff in
208
+ nothing is installed, and a green sync materializes the rebased checkout. The diff in
210
209
  `.grafts/.report/<id>/diff.patch` is what upstream changed since the fork's base in the files
211
210
  the fork touches. A clean rebase is not enough: upstream text that lands next to the change
212
- (e.g. a new English paragraph in a translated page) only fails the oracle, so read its
213
- output. Same fix steps as above, with `graft sync --version <target>` (no `--install`). A
211
+ (e.g. a new English paragraph in a translated page) merges without a conflict, so run the
212
+ project's checks (the graft's *Invariants* say which) on the result. Same fix steps as above, with `graft sync --version <target>` (no `--install`). A
214
213
  green sync leaves the result as branch `graft/<id>/<target>` in `.grafts/.cache/fork/<id>`;
215
214
  fetch it from there. Never push it unless the user asks.
216
215
  - A target can be an upstream commit between releases: `--version 22.3.0-next.3+<sha>` (the
@@ -218,10 +217,14 @@ When asked to capture a modification as a graft (new, or migrating an existing p
218
217
  - To create one in a fork that is the product: `graft add <id> --package <name> --mechanism fork
219
218
  --in-place --version <upstream version or version+commit>` from the fork's checkout.
220
219
  - **In place** (variant `{ "ref": "@worktree", "base": "@verified" }`): never write a new
221
- variant. A green sync puts the new `verified` only in the result branch; adopt it with
222
- `git fetch <repo> <ref> && git read-tree -u --reset FETCH_HEAD`. On the `-agent` branch,
220
+ variant. A green sync puts the new `verified` only in the result branch; adopt it as a merge
221
+ of the upstream commit it sits on (`upstream` in the report's `materialized.fetch` /
222
+ `agentStart`), so the fork keeps upstream in its history: `git fetch <repo> <ref> && git merge
223
+ --no-ff --no-commit -s ours --allow-unrelated-histories <upstream> && git read-tree -u --reset
224
+ FETCH_HEAD`, then commit. Never adopt by a plain tree swap or by checking out the result branch
225
+ (that drops the fork's history). On the `-agent` branch,
223
226
  graft.md already says the target version (`status: conflicted`): resolve, then
224
- `graft sync --version <target>` verifies the working tree as it is.
227
+ `graft sync --version <target>` checks the working tree as it is; then run the project's checks.
225
228
  - **Start from the prepared branch.** On `needs-agent`, the report's "Start from" line names
226
229
  `graft/<id>/<target>-agent` in the cache clone: git's merge of the fork's change onto the
227
230
  target, with conflict markers left only in the files it lists. Fetch it into the fork, resolve
@@ -235,7 +238,7 @@ Stop and mark the graft `conflicted` (`graft mark <id> --status conflicted --his
235
238
 
236
239
  - the behavior the graft modifies no longer exists upstream, or upstream changed it in a way
237
240
  that makes an invariant impossible or contradictory;
238
- - you cannot make the oracle pass in every mode without weakening it;
241
+ - you cannot make the project's tests pass for an Invariant without weakening them;
239
242
  - upstream now does what the graft did — say so; a human decides whether to mark it
240
243
  `upstreamed`. Graft detects the common case for you: when the variant no longer applies
241
244
  but upstream already contains its change (for patches, the reverse patch applies cleanly),
@@ -256,7 +259,9 @@ at everything else: `GRAFT_SKILL_PATH` (this file), `GRAFT_COMMAND` (how to run
256
259
  `GRAFT_TARGET_VERSION`, and `GRAFT_BRANCH` (already checked out).
257
260
 
258
261
  - Do **not** commit, push, or open a pull request. The action re-runs `graft sync` on your
259
- work and opens the PR itself: a normal PR only if the oracle passes, otherwise a draft.
262
+ work and opens the PR itself: a normal PR when every graft fits, otherwise a draft. It can't
263
+ check behavior: prove the Invariants with the project's tests before you finish, and say how
264
+ in the PR description.
260
265
  - Write the PR description (format below) to `GRAFT_PR_BODY_FILE`.
261
266
 
262
267
  ## PR description
@@ -272,8 +277,8 @@ at everything else: `GRAFT_SKILL_PATH` (this file), `GRAFT_COMMAND` (how to run
272
277
  **What changed upstream:** <the behavior-level change, citing the scoped diff>
273
278
  **Anchors that moved:** <anchor names, and where they went>
274
279
  **New variant:** `variants/<file>` for `<range>` — <how it differs from the previous variant>
275
- **Proof:** oracle passed in <modes> against <package>@<target>
280
+ **Verified:** <which tests, covering which Invariants, passed against <package>@<target>>
276
281
  ```
277
282
 
278
- For a conflicted graft, replace *New variant* and *Proof* with **Failing invariant**,
283
+ For a conflicted graft, replace *New variant* and *Verified* with **Failing invariant**,
279
284
  **What was tried**, and **What a human needs to decide**.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graftsync/skill",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Agent skill (SKILL.md) teaching coding agents the graft sync loop.",
5
5
  "license": "MIT",
6
6
  "repository": {