From 09f70fd203b2dc541a1aef3dd2d1a4a8def6cd07 Mon Sep 17 00:00:00 2001 From: Kyle Merritt Date: Mon, 12 Oct 2026 09:26:21 -0400 Subject: [PATCH 1/2] Teach planner-scope the owner issue-claim handoff --- README.md | 2 +- packages/planner-scope/SKILL.md | 4 +++- packages/planner-scope/relayview-skill.json | 4 ++-- tests/test_packages.py | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ae9ab4d..7ce5457 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Public source for RelayView-maintained instruction-only role skills and an illus | Directory | Status | Role / step | Version | | --- | --- | --- | --- | -| `packages/planner-scope` | Maintained default | Planner / planning | 0.2.0 | +| `packages/planner-scope` | Maintained default | Planner / planning | 0.3.0 | | `packages/implementer-handoff` | Maintained default | Implementer / implementation | 0.2.0 | | `packages/codebase-design` | Optional guidance | Planner / planning | 0.3.0 | | `packages/grill-me` | Optional guidance | Planner / planning | 0.3.0 | diff --git a/packages/planner-scope/SKILL.md b/packages/planner-scope/SKILL.md index 3e59146..1dd3bff 100644 --- a/packages/planner-scope/SKILL.md +++ b/packages/planner-scope/SKILL.md @@ -3,7 +3,7 @@ name: planner-scope description: Check assigned issue scope, neighboring work, and verification before drafting a Plan. metadata: relayview-display-name: Planner scope - relayview-version: 0.2.0 + relayview-version: 0.3.0 --- # Planner scope @@ -13,3 +13,5 @@ When drafting the assigned Plan, identify the requested behavior, its boundary, If the owner supplies an admitted work-item identity, use the assigned read tools to inspect it. Preserve source attribution; an issue description is a request, not evidence that its proposed behavior exists. Where tracker access is unavailable, state the missing context instead of guessing a work-item identity. Write the Plan through the owner's assigned planning interface. The Plan is ready when each requested outcome has an implementation boundary and a checkable success or recovery observation. Owner-controlled workflow stages and checks determine execution authority. + +For a linked Gitea issue, the default claim is the owner's transition from `status/ready-for-agent` to `status/in-progress` after it proves that the exact Plan reached the Implementer. Finalize the Plan through the assigned interface, then use owner-reported handoff and issue-claim outcomes when explaining progress to the Human. An accepted Plan alone does not establish that claim. The operator may disable or replace the status transition through owner policy; follow the effective policy recorded for the task. diff --git a/packages/planner-scope/relayview-skill.json b/packages/planner-scope/relayview-skill.json index a4c6243..1cb7061 100644 --- a/packages/planner-scope/relayview-skill.json +++ b/packages/planner-scope/relayview-skill.json @@ -2,14 +2,14 @@ "schemaVersion": 1, "source": { "kind": "first-party", - "reference": "v0.2.0" + "reference": "v0.4.0" }, "role": "planner", "step": "planning", "files": [ { "path": "SKILL.md", - "sha256": "31399ba322d8a4d3ed9814e7fd5d54883b4773c5b7dc3a241b8e14f8f7c16072" + "sha256": "bfac8352adffa8e2aaa246a6b5f11a333045a82aeefe5d0e42e76eaa776a6a70" }, { "path": "scope.md", diff --git a/tests/test_packages.py b/tests/test_packages.py index e3dcb8d..118c4d7 100644 --- a/tests/test_packages.py +++ b/tests/test_packages.py @@ -65,7 +65,7 @@ class PackageTests(unittest.TestCase): package = ROOT / "packages" / name manifest = json.loads((package / "relayview-skill.json").read_text()) self.assertEqual((manifest["role"], manifest["step"]), (role, step)) - self.assertEqual(manifest["source"], {"kind": "first-party", "reference": "v0.2.0"}) + self.assertEqual(manifest["source"], {"kind": "first-party", "reference": "v0.4.0" if name == "planner-scope" else "v0.2.0"}) self.assertIn(resource, [item["path"] for item in manifest["files"]]) self.assertIn(marker, (package / "SKILL.md").read_text() + (package / resource).read_text()) -- 2.54.0 From 24e3deb34ef531fcdeabf549c358949b923eb723 Mon Sep 17 00:00:00 2001 From: Kyle Merritt Date: Mon, 12 Oct 2026 09:49:31 -0400 Subject: [PATCH 2/2] Let replacement Planner packages declare issue claim policy --- README.md | 8 +++++--- packages/planner-scope/SKILL.md | 4 ++-- packages/planner-scope/relayview-skill.json | 9 ++++++--- tests/test_packages.py | 4 +++- tools/packages.py | 15 +++++++++++++-- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7ce5457..2d409b1 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Public source for RelayView-maintained instruction-only role skills and an illus | Directory | Status | Role / step | Version | | --- | --- | --- | --- | -| `packages/planner-scope` | Maintained default | Planner / planning | 0.3.0 | +| `packages/planner-scope` | Maintained default | Planner / planning | 0.4.0 | | `packages/implementer-handoff` | Maintained default | Implementer / implementation | 0.2.0 | | `packages/codebase-design` | Optional guidance | Planner / planning | 0.3.0 | | `packages/grill-me` | Optional guidance | Planner / planning | 0.3.0 | @@ -29,7 +29,7 @@ Public source for RelayView-maintained instruction-only role skills and an illus | `packages/c4-diff` | Optional guidance | Reviewer / review | 0.3.0 | | `examples/starter-planner` | Illustrative starter; not a production default | Planner / planning | 0.1.0 | -Only named releases of maintained packages receive RelayView compatibility testing and maintenance. Optional guidance requires explicit role assignment; the two original defaults remain available. User forks are maintained by their publishers. This release uses the instruction package schema version 1 admitted by RelayView's Community MVP. It contains only `SKILL.md`, declared `.md`/`.txt` resources, and `relayview-skill.json` within each package. Scripts, executable files, undeclared files, and requested permissions fail checks and owner admission. There is no fixed release calendar: maintained packages are versioned when their content or compatibility changes. A breaking owner contract requires a new compatible maintained release before the product depends on it. +Only named releases of maintained packages receive RelayView compatibility testing and maintenance. Optional guidance requires explicit role assignment; the two original defaults remain available. User forks are maintained by their publishers. The Planner default uses instruction package schema version 2 for its bounded issue-claim policy; other packages retain version 1. Packages contain only `SKILL.md`, declared `.md`/`.txt` resources, and `relayview-skill.json`. Scripts, executable files, undeclared files, and requested permissions fail checks and owner admission. There is no fixed release calendar: maintained packages are versioned when their content or compatibility changes. A breaking owner contract requires a new compatible maintained release before the product depends on it. ## A named set in RelayView @@ -64,7 +64,9 @@ Replace `examples/starter-planner` in the last two commands with your package di To use a user fork, place its closed package directory under the owner's configured trusted root and use Settings → Skills to import the relative `SKILL.md` path under a new unique ID. Import validates and records the package; it does not assign it. Add that ID to a role-compatible named set in the owner configuration and restart the owner to load the set. Then select the set for the matching role in Settings → Skills and save. That selection takes effect for future sessions immediately; existing sessions retain their captured bytes. The Settings catalog displays source, version, digest, role, step, and pending versus effective assignment. The owner's admission remains authoritative even when these local checks pass. -To use a maintained default, copy its release package under the same trusted root, admit its `SKILL.md` through the owner catalog, then select it for its declared role in Settings. Settings import accepts user-source packages; configure first-party packages in the owner catalog. Select a fork with `source.kind: user` and a distinct ID to replace that role's future assignment. Planner guidance uses existing read and planning tools; Implementer guidance uses existing implementation and reporting tools. Neither selection changes a role's tools, credentials, lifecycle stages, validation requirements, Gitea effects, review, merge, or Human acceptance. The owner keeps the existing Gitea task workflow. +To replace the linked-issue claim convention, fork `planner-scope` as a schema-version-2 Planner package and set `issueClaim.statusLabel` to another existing `status/` label, or to `null` to disable the claim. Update the package's source reference and file hashes, import it, and select it as the automatic Planner skill for future tasks. The owner validates this declaration and freezes the selected package digest and target with each new linked task. Skill text cannot itself dispatch the Gitea effect. + +To use a maintained default, copy its release package under the same trusted root, admit its `SKILL.md` through the owner catalog, then select it for its declared role in Settings. Settings import accepts user-source packages; configure first-party packages in the owner catalog. Select a fork with `source.kind: user` and a distinct ID to replace that role's future assignment. Planner guidance uses existing read and planning tools; Implementer guidance uses existing implementation and reporting tools. Skill selection does not change a role's tools, credentials, lifecycle stages, validation requirements, review, merge, or Human acceptance. The owner keeps custody of the Gitea credential and executes only its validated claim policy. ## Release identity diff --git a/packages/planner-scope/SKILL.md b/packages/planner-scope/SKILL.md index 1dd3bff..f460433 100644 --- a/packages/planner-scope/SKILL.md +++ b/packages/planner-scope/SKILL.md @@ -3,7 +3,7 @@ name: planner-scope description: Check assigned issue scope, neighboring work, and verification before drafting a Plan. metadata: relayview-display-name: Planner scope - relayview-version: 0.3.0 + relayview-version: 0.4.0 --- # Planner scope @@ -14,4 +14,4 @@ If the owner supplies an admitted work-item identity, use the assigned read tool Write the Plan through the owner's assigned planning interface. The Plan is ready when each requested outcome has an implementation boundary and a checkable success or recovery observation. Owner-controlled workflow stages and checks determine execution authority. -For a linked Gitea issue, the default claim is the owner's transition from `status/ready-for-agent` to `status/in-progress` after it proves that the exact Plan reached the Implementer. Finalize the Plan through the assigned interface, then use owner-reported handoff and issue-claim outcomes when explaining progress to the Human. An accepted Plan alone does not establish that claim. The operator may disable or replace the status transition through owner policy; follow the effective policy recorded for the task. +For a linked Gitea issue, this package declares the owner's transition from `status/ready-for-agent` to `status/in-progress` after it proves that the exact Plan reached the Implementer. Finalize the Plan through the assigned interface, then use owner-reported handoff and issue-claim outcomes when explaining progress to the Human. An accepted Plan alone does not establish that claim. A user may replace this skill with a Planner fork whose package declares a different target status or disables the claim; follow the selected package's policy recorded for the task. diff --git a/packages/planner-scope/relayview-skill.json b/packages/planner-scope/relayview-skill.json index 1cb7061..54a5963 100644 --- a/packages/planner-scope/relayview-skill.json +++ b/packages/planner-scope/relayview-skill.json @@ -1,15 +1,18 @@ { - "schemaVersion": 1, + "schemaVersion": 2, "source": { "kind": "first-party", - "reference": "v0.4.0" + "reference": "v0.5.0" }, "role": "planner", "step": "planning", + "issueClaim": { + "statusLabel": "status/in-progress" + }, "files": [ { "path": "SKILL.md", - "sha256": "bfac8352adffa8e2aaa246a6b5f11a333045a82aeefe5d0e42e76eaa776a6a70" + "sha256": "c274c9908cce71b131ba5665091c3ee3c8382953909ccd3c69a80ec7279349bd" }, { "path": "scope.md", diff --git a/tests/test_packages.py b/tests/test_packages.py index 118c4d7..bcca0b9 100644 --- a/tests/test_packages.py +++ b/tests/test_packages.py @@ -65,7 +65,9 @@ class PackageTests(unittest.TestCase): package = ROOT / "packages" / name manifest = json.loads((package / "relayview-skill.json").read_text()) self.assertEqual((manifest["role"], manifest["step"]), (role, step)) - self.assertEqual(manifest["source"], {"kind": "first-party", "reference": "v0.4.0" if name == "planner-scope" else "v0.2.0"}) + self.assertEqual(manifest["source"], {"kind": "first-party", "reference": "v0.5.0" if name == "planner-scope" else "v0.2.0"}) + if name == "planner-scope": + self.assertEqual(manifest["issueClaim"], {"statusLabel": "status/in-progress"}) self.assertIn(resource, [item["path"] for item in manifest["files"]]) self.assertIn(marker, (package / "SKILL.md").read_text() + (package / resource).read_text()) diff --git a/tools/packages.py b/tools/packages.py index fc42c7c..a46e4e5 100644 --- a/tools/packages.py +++ b/tools/packages.py @@ -44,15 +44,26 @@ def check_package(path): manifest = json.loads(_text(raw_manifest, str(sidecar))) except (ValueError, TypeError) as error: _fail(f"{sidecar}: invalid JSON: {error}") - if not isinstance(manifest, dict) or set(manifest) != {"schemaVersion", "source", "role", "step", "files"}: + if not isinstance(manifest, dict) or ( + (manifest.get("schemaVersion") == 1 and set(manifest) != {"schemaVersion", "source", "role", "step", "files"}) or + (manifest.get("schemaVersion") == 2 and set(manifest) != {"schemaVersion", "source", "role", "step", "files", "issueClaim"}) or + manifest.get("schemaVersion") not in (1, 2) + ): _fail(f"{path}: unsupported manifest fields") source = manifest["source"] if not isinstance(source, dict) or set(source) != {"kind", "reference"}: _fail(f"{path}: invalid source") if source["kind"] not in ("first-party", "user") or not isinstance(source["reference"], str) or not re.fullmatch(r"[A-Za-z0-9._:-]{1,128}", source["reference"]): _fail(f"{path}: invalid source identity") - if manifest["schemaVersion"] != 1 or (manifest["role"], manifest["step"]) not in (("planner", "planning"), ("implementer", "implementation"), ("reviewer", "review")): + if (manifest["role"], manifest["step"]) not in (("planner", "planning"), ("implementer", "implementation"), ("reviewer", "review")): _fail(f"{path}: incompatible role or step") + if manifest["schemaVersion"] == 2: + claim = manifest["issueClaim"] + label = claim.get("statusLabel") if isinstance(claim, dict) else None + if (manifest["role"], manifest["step"]) != ("planner", "planning") or not isinstance(claim, dict) or set(claim) != {"statusLabel"} or ( + label is not None and (not isinstance(label, str) or not re.fullmatch(r"status/[a-z][a-z0-9-]{0,63}", label) or label == "status/ready-for-agent") + ): + _fail(f"{path}: invalid issue claim policy") files = manifest["files"] if not isinstance(files, list) or not 1 <= len(files) <= 16: _fail(f"{path}: invalid file list") -- 2.54.0