Skip to content

Commit d190cfb

Browse files
authored
Merge pull request #59 from operatorstack/feature/semantic-disposition
Harden release transport and local onboarding
2 parents b99fe73 + 0ce5f2b commit d190cfb

90 files changed

Lines changed: 810 additions & 117 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/release-yield/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: release-yield
33
description: "Release Yield through its protected GitHub workflows and verify every public registry."
44
---
55

6-
<!-- generated-by: yskill; source: skills/release-yield; digest: sha256:85ef5fa8c0d09817ced3213451a72e211bd92736a9e851b567b2fd4034cc5c4c; version: 0.1.38 -->
6+
<!-- generated-by: yskill; source: skills/release-yield; digest: sha256:5193de687010c12767ddb81ef93d3d925b769ad46e67bf926d26bb66164a2bf6; version: 0.1.38 -->
77

88
This adapter exposes the canonical Yield workflow at `skills/release-yield`.
99
Read its SKILL.md, then run from the repository root:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@operatorstack/yield": patch
3+
---
4+
5+
Publish verified npm archives instead of extracted directories, require each workflow to declare the running Yield version, and keep Go and Rust workflow launchers local to the project.

.claude/skills/release-yield/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: release-yield
33
description: "Release Yield through its protected GitHub workflows and verify every public registry."
44
---
55

6-
<!-- generated-by: yskill; source: skills/release-yield; digest: sha256:85ef5fa8c0d09817ced3213451a72e211bd92736a9e851b567b2fd4034cc5c4c; version: 0.1.38 -->
6+
<!-- generated-by: yskill; source: skills/release-yield; digest: sha256:5193de687010c12767ddb81ef93d3d925b769ad46e67bf926d26bb66164a2bf6; version: 0.1.38 -->
77

88
This adapter exposes the canonical Yield workflow at `skills/release-yield`.
99
Read its SKILL.md, then run from the repository root:

.cursor/skills/release-yield/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: release-yield
33
description: "Release Yield through its protected GitHub workflows and verify every public registry."
44
---
55

6-
<!-- generated-by: yskill; source: skills/release-yield; digest: sha256:85ef5fa8c0d09817ced3213451a72e211bd92736a9e851b567b2fd4034cc5c4c; version: 0.1.38 -->
6+
<!-- generated-by: yskill; source: skills/release-yield; digest: sha256:5193de687010c12767ddb81ef93d3d925b769ad46e67bf926d26bb66164a2bf6; version: 0.1.38 -->
77

88
This adapter exposes the canonical Yield workflow at `skills/release-yield`.
99
Read its SKILL.md, then run from the repository root:

.github/workflows/npm-publish.yml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -135,20 +135,17 @@ jobs:
135135
cp "$directory/target/package/${name}-${VERSION}.crate" dist/crates/
136136
done
137137
test "$(find dist/crates -maxdepth 1 -name '*.crate' | wc -l | tr -d ' ')" = 6
138-
- name: Inspect npm tarballs
138+
- name: Pack immutable npm release unit
139139
shell: bash
140140
run: |
141141
set -euo pipefail
142-
for directory in dist/packages/npm/*; do
143-
(cd "$directory" && npm pack --dry-run)
144-
done
142+
node packaging/npm-release.mjs --source dist/packages/npm --output dist/release-unit/npm --source-sha "${{ needs.resolve.outputs.source_sha }}"
145143
- name: Assemble immutable release unit
146144
env:
147145
CHANNEL: ${{ needs.resolve.outputs.channel }}
148146
run: |
149147
mkdir -p dist/release-unit
150148
cp dist/packages/SHA256SUMS.json dist/release-unit/
151-
cp -R dist/packages/npm dist/release-unit/npm
152149
if [[ "$CHANNEL" == stable ]]; then
153150
cp -R dist/pypi dist/release-unit/pypi
154151
cp -R dist/packages/rust dist/release-unit/rust
@@ -186,12 +183,13 @@ jobs:
186183
shell: bash
187184
run: |
188185
set -euo pipefail
189-
for directory in dist/release-unit/npm/darwin-* dist/release-unit/npm/linux-* dist/release-unit/npm/windows-*; do
190-
package="$(node -p "require('./${directory}/package.json').name")"
186+
jq -c '.archives[] | select(.name != "@operatorstack/yield" and .name != "@operatorstack/create-yield")' dist/release-unit/npm/npm-release.json | while read -r archive; do
187+
package="$(jq -r '.name' <<< "$archive")"
188+
file="$(jq -r '.file' <<< "$archive")"
191189
if npm view "${package}@${VERSION}" version >/dev/null 2>&1; then
192190
echo "${package}@${VERSION} already exists"
193191
else
194-
(cd "$directory" && npm publish --tag "$DIST_TAG")
192+
npm publish "dist/release-unit/npm/${file}" --tag "$DIST_TAG"
195193
fi
196194
done
197195
- name: Publish SDK and CLI
@@ -204,7 +202,8 @@ jobs:
204202
if npm view "@operatorstack/yield@${VERSION}" version >/dev/null 2>&1; then
205203
echo "@operatorstack/yield@${VERSION} already exists"
206204
else
207-
(cd dist/release-unit/npm/yield && npm publish --tag "$DIST_TAG")
205+
file="$(jq -r '.archives[] | select(.name == "@operatorstack/yield") | .file' dist/release-unit/npm/npm-release.json)"
206+
npm publish "dist/release-unit/npm/${file}" --tag "$DIST_TAG"
208207
fi
209208
- name: Publish npm initializer
210209
env:
@@ -216,16 +215,16 @@ jobs:
216215
if npm view "@operatorstack/create-yield@${VERSION}" version >/dev/null 2>&1; then
217216
echo "@operatorstack/create-yield@${VERSION} already exists"
218217
else
219-
(cd dist/release-unit/npm/create-yield && npm publish --tag "$DIST_TAG")
218+
file="$(jq -r '.archives[] | select(.name == "@operatorstack/create-yield") | .file' dist/release-unit/npm/npm-release.json)"
219+
npm publish "dist/release-unit/npm/${file}" --tag "$DIST_TAG"
220220
fi
221221
- name: Verify complete npm release unit
222222
env:
223223
VERSION: ${{ needs.resolve.outputs.version }}
224224
shell: bash
225225
run: |
226226
set -euo pipefail
227-
for directory in dist/release-unit/npm/*; do
228-
package="$(node -p "require('./${directory}/package.json').name")"
227+
jq -r '.archives[].name' dist/release-unit/npm/npm-release.json | while read -r package; do
229228
for attempt in {1..12}; do
230229
if [[ "$(npm view "${package}@${VERSION}" version 2>/dev/null || true)" == "$VERSION" ]]; then break; fi
231230
test "$attempt" -lt 12

.github/workflows/release-finalize.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,16 @@ jobs:
123123
--source-sha "$SOURCE_SHA" \
124124
--attempts 3 \
125125
--delay-ms 10000
126+
node evals/scripts/run.mjs --check
126127
test "$(git rev-list -n 1 "$TAG")" = "$SOURCE_SHA"
128+
assets="$RUNNER_TEMP/release-assets"
129+
mkdir -p "$assets"
130+
node packaging/package-contract.mjs \
131+
--version "$version" \
132+
--source-sha "$SOURCE_SHA" \
133+
--npm-release "$RUNNER_TEMP/release-unit/npm/npm-release.json" \
134+
--evaluation evals/results/latest.json \
135+
--release-url "https://github.com/${GITHUB_REPOSITORY}/releases/tag/${TAG}" \
136+
--output "$assets"
137+
gh release upload "$TAG" "$assets/yield-package-contract.json" "$assets/yield-website-promotion.json" --repo "$GITHUB_REPOSITORY" --clobber
127138
gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --draft=false

.github/workflows/verify.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ jobs:
165165
sdk/rust
166166
examples/data-migration
167167
examples/library/rust
168+
- run: npm ci --ignore-scripts
168169
- name: Rerun first-party evaluations
169170
working-directory: evals
170171
run: |
@@ -178,7 +179,10 @@ jobs:
178179
run: npm run test:conversion
179180
- name: Run every example fixture
180181
run: |
181-
go build -o "$RUNNER_TEMP/yskill" ./cmd/yskill
182+
plan="$RUNNER_TEMP/example-release-plan.env"
183+
node scripts/release-plan.mjs --bump auto --output "$plan" --notes "$RUNNER_TEMP/example-release-notes.md"
184+
. "$plan"
185+
go build -ldflags "-X main.version=$version" -o "$RUNNER_TEMP/yskill" ./cmd/yskill
182186
"$RUNNER_TEMP/yskill" test examples/investigate
183187
"$RUNNER_TEMP/yskill" test examples/release-checklist
184188
"$RUNNER_TEMP/yskill" test examples/env-doctor

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ available for 73 more coding agents.
4747

4848
Run the command for your project:
4949

50-
| Language | Command |
51-
| ---------- | --------------------------------------------------------------------------------- |
52-
| TypeScript | `npm create @operatorstack/yield@latest` |
53-
| Python | `uvx --from yieldskill yskill bootstrap --language python` |
54-
| Rust | `cargo install yieldskill --locked`, then `yskill bootstrap --language rust` |
55-
| Go | `go run github.com/operatorstack/yield/cmd/yskill@latest bootstrap --language go` |
50+
| Language | Command |
51+
| ---------- | -------------------------------------------------------------------------------------------------------------- |
52+
| TypeScript | `npm create @operatorstack/yield@latest` |
53+
| Python | `uvx --from yieldskill yskill bootstrap --language python` |
54+
| Rust | `cargo install yieldskill --root .yield --locked`, then `.yield/bin/yskill bootstrap --root . --language rust` |
55+
| Go | `go run github.com/operatorstack/yield/cmd/yskill@latest bootstrap --root . --language go` |
5656

5757
Yield detects the repository, language, and installed coding agents. It shows
5858
every proposed file and dependency change. It asks before it writes. It then
@@ -159,6 +159,7 @@ workflow URL without publishing.
159159

160160
Every newly published canary runs the same contract tests in an isolated CI
161161
lane. Stable release execution remains pinned to an exact public version.
162+
162163
<!-- npm-exclude:end -->
163164

164165
## Advanced: build manually

cmd/yskill/agents.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,9 @@ func verifyWorkflowSDKVersion(manifest skillManifest, skillDir, repoRoot, expect
710710
if expected == "dev" {
711711
return nil
712712
}
713+
if manifest.YieldVersion != expected {
714+
return fmt.Errorf("workflow declares Yield %s, but the running supervisor is Yield %s", manifest.YieldVersion, expected)
715+
}
713716
var declared string
714717
if manifest.Language == "typescript" {
715718
root, err := findTypeScriptPackageRoot(skillDir, repoRoot)

cmd/yskill/agents_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func TestEveryRegistryAgentGeneratesAContainedAdapter(t *testing.T) {
6767
writeTestFile(t, filepath.Join(repo, "package.json"), `{"dependencies":{"@operatorstack/yield":"0.1.17"}}`)
6868
skill := filepath.Join(repo, "workflows", "review")
6969
writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: review\ndescription: Review the branch when the user wants code checked before shipping.\n---\n")
70-
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"language":"typescript","run":["node","main.ts"]}`)
70+
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"typescript","run":["node","main.ts"]}`)
7171
writeTestFile(t, filepath.Join(skill, "main.ts"), "export {}\n")
7272
registry, err := loadAgentRegistry()
7373
if err != nil {
@@ -225,7 +225,7 @@ func TestRegisterAllPreflightsAndWritesEveryWorkflow(t *testing.T) {
225225
for _, name := range []string{"review", "release"} {
226226
skill := filepath.Join(repo, "skills", name)
227227
writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: "+name+"\ndescription: Run "+name+" when the matching project workflow is requested.\n---\n")
228-
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"language":"typescript","run":["node","main.ts"]}`)
228+
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"typescript","run":["node","main.ts"]}`)
229229
writeTestFile(t, filepath.Join(skill, "main.ts"), "export {}\n")
230230
}
231231
output := captureStdout(t, func() {
@@ -256,7 +256,7 @@ func TestRegisterAllPruneRemovesOnlyOwnedAdapterFile(t *testing.T) {
256256
for _, name := range []string{"review", "release"} {
257257
skill := filepath.Join(repo, "skills", name)
258258
writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: "+name+"\ndescription: Run "+name+" when the matching project workflow is requested.\n---\n")
259-
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"language":"typescript","run":["node","main.ts"]}`)
259+
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"typescript","run":["node","main.ts"]}`)
260260
writeTestFile(t, filepath.Join(skill, "main.ts"), "export {}\n")
261261
}
262262
if err := cmdRegisterAll([]string{filepath.Join(repo, "skills"), "--root", repo, "--agent", "codex"}); err != nil {
@@ -285,7 +285,7 @@ func TestRegisterAllRefusesCrossDirectoryNameCollisionBeforeWrites(t *testing.T)
285285
makeSkill := func(parent string) string {
286286
skill := filepath.Join(repo, parent, "review")
287287
writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: review\ndescription: Review the project when a user asks for a check.\n---\n")
288-
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"language":"typescript","run":["node","main.ts"]}`)
288+
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"typescript","run":["node","main.ts"]}`)
289289
writeTestFile(t, filepath.Join(skill, "main.ts"), "export {}\n")
290290
return skill
291291
}
@@ -442,7 +442,7 @@ func TestWorkflowSDKVersionMustMatchRuntime(t *testing.T) {
442442

443443
func TestGoWorkflowSDKVersionAcceptsGeneratedAndBlockForms(t *testing.T) {
444444
repo := t.TempDir()
445-
manifest := skillManifest{Version: 1, Language: "go", Run: []string{"go", "run", "."}}
445+
manifest := skillManifest{Version: 1, YieldVersion: "0.1.23", Language: "go", Run: []string{"go", "run", "."}}
446446
for name, goMod := range map[string]string{
447447
"generated": "module review\n\ngo 1.26.5\n\nrequire github.com/operatorstack/yield v0.1.23\n",
448448
"block": "module review\n\ngo 1.26.5\n\nrequire (\n\tgithub.com/operatorstack/yield v0.1.23\n)\n",
@@ -462,7 +462,7 @@ func TestDoctorReportsSDKRuntimeAndAdapterVersionProblemsTogether(t *testing.T)
462462
writeTestFile(t, filepath.Join(repo, ".git", "keep"), "")
463463
skill := filepath.Join(repo, "skills", "review")
464464
writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: review\ndescription: Review code before it is shipped.\n---\n")
465-
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"language":"go","run":["go","run","."]}`)
465+
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"go","run":["go","run","."]}`)
466466
writeTestFile(t, filepath.Join(skill, "go.mod"), "module review\n\ngo 1.26.5\n\nrequire github.com/operatorstack/yield v0.1.22\n")
467467
writeTestFile(t, filepath.Join(skill, "main.go"), "package main\nfunc main() {}\n")
468468
writeTestFile(t, localRuntimePath(repo), "runtime")
@@ -491,7 +491,7 @@ func createTypeScriptSkill(t *testing.T, repo, name string) string {
491491
writeTestFile(t, filepath.Join(repo, "package.json"), `{"dependencies":{"@operatorstack/yield":"0.1.17"}}`)
492492
skill := filepath.Join(repo, "skills", name)
493493
writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: "+name+"\ndescription: Review the branch when the user wants code checked before shipping.\n---\n")
494-
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"language":"typescript","run":["node","main.ts"]}`)
494+
writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"typescript","run":["node","main.ts"]}`)
495495
writeTestFile(t, filepath.Join(skill, "main.ts"), "export {}\n")
496496
return skill
497497
}

0 commit comments

Comments
 (0)