From 304d87787f3b08fb46659a8b8fb22cf4c8787e5a Mon Sep 17 00:00:00 2001 From: "operator-stack-publisher[bot]" Date: Sun, 2 Aug 2026 19:54:31 +0000 Subject: [PATCH] Sync Yield from Intelligence Flow @ 985b2fcfcb2f --- UPSTREAM.json | 11 +-- cmd/yskill/main_test.go | 100 +++++++++++++++++++++- cmd/yskill/scaffold.go | 13 ++- docs/testing-fixtures.md | 4 + evals/results/latest.json | 4 +- release-notes/2026-08-02-final-dx-pass.md | 6 ++ 6 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 release-notes/2026-08-02-final-dx-pass.md diff --git a/UPSTREAM.json b/UPSTREAM.json index d581533..51b0b0b 100644 --- a/UPSTREAM.json +++ b/UPSTREAM.json @@ -6,11 +6,11 @@ "cmd/yskill/agents.go": "8129c073b7b56ca7dcd50c5d90640ad80522c88822c629d1bdc79a386a31a8f4", "cmd/yskill/agents_test.go": "d6202d208036be6b2b50f7d8f9c3a9b4c2b3eea9556477ef9ad4d56201c6b832", "cmd/yskill/main.go": "5eefea8fdd3e040a940bde80aac793fb97c61c18586bd993dab0395f57e5a81f", - "cmd/yskill/main_test.go": "af22b208ffcb6f2ae692132fadd879218d51899c21285bc8e914face61a42ad9", + "cmd/yskill/main_test.go": "09e792e818a410906120225ff3d559adeccfb5288bcbbddfdf446149d5da852e", "cmd/yskill/registry/README.md": "fab385921ee7972f76deb94a3b7ae41184735598ea4145c9d8ccdde2c2b876c4", "cmd/yskill/registry/VERCEL_SKILLS_LICENSE": "779258e329008bdb9330e6c1daad644ff867f164d11c4cc4404350479f3e92ee", "cmd/yskill/registry/agents.json": "6edfee31cdc0390516adbb107fc97fc3531eb02e1a28b9d62b5b86fdccaed7c1", - "cmd/yskill/scaffold.go": "404f8418a721b3e179fcd6d814640628a56930f54a9473a5f28dcae433e74ac7", + "cmd/yskill/scaffold.go": "a9d0572f7a139dcff478265c016c64038b03cbf9d123054e5274e3a352488e9a", "cmd/yskill/skillmeta.go": "12b0c25689bf825d676404af7b3fb74fb92db3d9e222d4e553d8800d86aea45e", "docs/README.md": "b4103bc38ca5b3b313d6cc7eae5f11276c03a8bd6123cfa101cb7372ff43fa72", "docs/agent-setup.md": "72d2f7af6ca93b3865c8d7095479320127adc5edfdcce940494adf28f3abe7fc", @@ -43,7 +43,7 @@ "docs/reference/guarantees.md": "fce2006ad46a83c4ae34d221a83d68bc06a776e7c4f54d938bdcdac91977aa50", "docs/reference/sdk-parity.md": "b8c3d6d6efad246b9212db760bb72cba7182b04da321cfd667783000ae75f733", "docs/skill-workflows.md": "c0049f6b7db00fc45f15d649e56e381962ddf637dd5d20309a4f79801a28d80d", - "docs/testing-fixtures.md": "9f809ced072cc9a55dada36019a552cc7da2eac76fef85fc289e1c3e8e0dc6d1", + "docs/testing-fixtures.md": "792d0129e072ff6718130e88b1214c53fbe1690d0da11d5aa35bc9e35da87b39", "docs/tutorials/README.md": "f35641cab5aa9739b22a7f9c75a5fe7966b26f492879a70545c4cb6a780129b3", "docs/tutorials/approval.md": "d0144288e12488627fce085d3053b1c9b79cc1dac2d45157c214062de3375a9c", "docs/tutorials/bounded-debugging.md": "903b876fb13fa5bff6789c500f56c1b97c5ae1bc0059147d845a8f327d553340", @@ -68,7 +68,7 @@ "evals/package.json": "84ac7bd7d1c0d1db2233dab54754296195b267e5e907d9f6c402d0d44d569adb", "evals/results/README.md": "65b74bfab83dfc51fc5b8a3a4824c37b722fb3a358a5dcfb217dc2af199f4801", "evals/results/latest-agent.json": "1484174819ae29bbcffeb615036fa165518669525e6d664123f6a38ca3614b69", - "evals/results/latest.json": "54cbe6c70e57a38735ccbb60aaccbb23dad952b80cd852a3b3c1013baa207815", + "evals/results/latest.json": "6e839ac363e74a31523b2f519e3f5c2b54c26624fdd10a2e1f047b7be195a285", "evals/scripts/run.mjs": "17a62cd0f7fda73d84a9bfa21c9c3253ba52a4a23eece09523cfea244e3f9940", "evals/scripts/validate.mjs": "42d51ea75f9418e45dd1797d1fe02cbc046752d3e3c37d41bc8d3a47d5865d4d", "examples/convert-skill/SKILL.md": "e6376f34365d4ac030d316db55e91f0c606a501668099f4ecf1e27d43ec806a2", @@ -310,6 +310,7 @@ "release-notes/2026-08-01-remove-stray-analysis-traces.md": "0567f78ee97ffd23b3f26b5c39606e9ff6659c50a3fdef04ed9b3aa86cfa99af", "release-notes/2026-08-02-cross-agent-registration.md": "8f4296fae36468bda08370bad8d13b7fc1e893b682f8c73015311ab01ace853f", "release-notes/2026-08-02-dx-hardening.md": "801d85ad760545140415000a95cbd6a681c32462f0578c1ac1ce802893bee303", + "release-notes/2026-08-02-final-dx-pass.md": "40c451db6ed72c0f0042b1184edb1dfb32a57076c453213326a43c5adf005f95", "release-notes/2026-08-02-go-sdk-pin-detection.md": "d835d0a9efebba16ab782a7b5e8730a873545b395e644b6e7b21cf93bed7d1a4", "release-notes/2026-08-02-skill-workflows.md": "8cd0cded1922387ef8e81b3e084c12e440f729aa778672f9ec91ffa153df5297", "release-notes/2026-08-02-v0-1-23-dx.md": "538aaf62beeb15e132cfde35f47217edce7d1e4ef5c4036fd00ed96f8362b2b7", @@ -334,7 +335,7 @@ "generator": "operatorstack/yield:project", "schema_version": 1, "source": { - "commit": "ad137fc9d418a58336dfb6faaaccb47fbd8f018b", + "commit": "985b2fcfcb2f8ae84fbcd01da8dc2f9f46417042", "path": "labs/22-yield", "repository": "operatorstack/intelligence-flow" } diff --git a/cmd/yskill/main_test.go b/cmd/yskill/main_test.go index 4bbf1be..26042f2 100644 --- a/cmd/yskill/main_test.go +++ b/cmd/yskill/main_test.go @@ -5,6 +5,7 @@ import ( "flag" "io" "os" + "os/exec" "path/filepath" "runtime" "runtime/debug" @@ -210,8 +211,10 @@ func TestScaffoldSkillWritesLanguageSpecificEntrypoints(t *testing.T) { t.Fatalf("skill.json = version %d language %q", generated.Version, generated.Language) } skill := readTestFile(t, filepath.Join(dir, "SKILL.md")) - if !strings.Contains(skill, tt.command) { - t.Fatalf("SKILL.md does not contain %q:\n%s", tt.command, skill) + workflow := shellQuoteForPlatform(dir, runtime.GOOS) + command := strings.Replace(tt.command, " .", " "+workflow, 1) + if !strings.Contains(skill, command) { + t.Fatalf("SKILL.md does not contain %q:\n%s", command, skill) } entrypoint := map[string]string{ "typescript": "main.ts", @@ -244,6 +247,97 @@ func TestScaffoldSkillWritesLanguageSpecificEntrypoints(t *testing.T) { } } +func TestPackageScaffoldsPrintCreatedWorkflowInNextCommands(t *testing.T) { + previousVersion := version + version = "0.1.28" + t.Cleanup(func() { version = previousVersion }) + + for _, tt := range []struct { + language string + launcher string + }{ + {language: "typescript", launcher: "npm exec -- yskill"}, + {language: "python", launcher: "python -m yieldskill"}, + } { + t.Run(tt.language, func(t *testing.T) { + root := filepath.Join(t.TempDir(), "project with space") + dir := filepath.Join(root, "skills", "safe-change") + output := captureStdout(t, func() { + if err := scaffoldSkill(dir, tt.language, "", "Check a safe change before applying it."); err != nil { + t.Fatal(err) + } + }) + workflow := shellQuoteForPlatform(dir, runtime.GOOS) + for _, line := range []string{ + "test: " + tt.launcher + " doctor " + workflow + " --test", + "then: " + tt.launcher + " register " + workflow, + } { + if !strings.Contains(output, line) { + t.Fatalf("init output does not contain %q:\n%s", line, output) + } + } + }) + } +} + +func TestShellQuoteForPlatform(t *testing.T) { + for _, tt := range []struct { + name, value, goos, want string + }{ + {name: "relative", value: "skills/review", goos: "linux", want: "'skills/review'"}, + {name: "explicit relative", value: "./skills/review", goos: "linux", want: "'./skills/review'"}, + {name: "absolute", value: "/tmp/project/skills/review", goos: "linux", want: "'/tmp/project/skills/review'"}, + {name: "nested with space", value: "workflows/team one/safe change's", goos: "linux", want: `'workflows/team one/safe change'"'"'s'`}, + {name: "windows", value: `skills\safe change's`, goos: "windows", want: `'skills\safe change''s'`}, + } { + t.Run(tt.name, func(t *testing.T) { + if got := shellQuoteForPlatform(tt.value, tt.goos); got != tt.want { + t.Fatalf("quote = %q, want %q", got, tt.want) + } + }) + } +} + +func TestRustScaffoldNamesPrimaryBinary(t *testing.T) { + previousVersion := version + version = "0.1.28" + t.Cleanup(func() { version = previousVersion }) + dir := filepath.Join(t.TempDir(), "safe-change") + if err := scaffoldSkill(dir, "rust", "", "Check a safe change before applying it."); err != nil { + t.Fatal(err) + } + manifest := readTestFile(t, filepath.Join(dir, "skill.json")) + if manifest != "{\"version\":1,\"language\":\"rust\",\"run\":[\"cargo\",\"run\",\"--quiet\",\"--bin\",\"safe-change\"]}\n" { + t.Fatalf("skill.json = %q", manifest) + } +} + +func TestRustScaffoldRunsPrimaryBinaryWhenFixtureAddsAnotherBinary(t *testing.T) { + if _, err := exec.LookPath("cargo"); err != nil { + t.Skip("cargo is not installed") + } + dir := filepath.Join(t.TempDir(), "safe-change") + if err := scaffoldSkill(dir, "rust", "", "Check a safe change before applying it."); err != nil { + t.Fatal(err) + } + writeTestFile(t, filepath.Join(dir, "Cargo.toml"), "[package]\nname = \"safe-change\"\nversion = \"0.1.0\"\nedition = \"2021\"\n") + writeTestFile(t, filepath.Join(dir, "src", "main.rs"), "fn main() { println!(\"workflow\"); }\n") + writeTestFile(t, filepath.Join(dir, "src", "bin", "fixture-helper.rs"), "fn main() { println!(\"fixture\"); }\n") + manifest, err := readSkillManifest(dir) + if err != nil { + t.Fatal(err) + } + cmd := exec.Command(manifest.Run[0], manifest.Run[1:]...) + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("generated run command failed: %v\n%s", err, out) + } + if strings.TrimSpace(string(out)) != "workflow" { + t.Fatalf("generated run command selected %q", out) + } +} + func TestGoScaffoldCanResolveItsPinnedModuleOnFirstRun(t *testing.T) { previousVersion := version previousTidyGoModule := tidyGoModule @@ -285,7 +379,7 @@ func TestLocalGoAndRustScaffoldsKeepTheInvokedRuntime(t *testing.T) { } skill := readTestFile(t, filepath.Join(dir, "SKILL.md")) launcher := repositoryRuntimeLauncher(filepath.Join(".yield", "bin", filepath.Base(localRuntimePath(repo))), runtime.GOOS) - workflow := shellQuote("skills/safe-change") + workflow := shellQuote(filepath.Join("skills", "safe-change")) for _, command := range []string{ launcher + " run " + workflow, launcher + " respond --value --skill " + workflow, diff --git a/cmd/yskill/scaffold.go b/cmd/yskill/scaffold.go index fdbde06..10cb362 100644 --- a/cmd/yskill/scaffold.go +++ b/cmd/yskill/scaffold.go @@ -45,7 +45,7 @@ func scaffoldCommand(language, dir string) (launcher, workflow string) { "go": "yskill", "rust": "yskill", }[language] - workflow = "." + workflow = shellQuoteForPlatform(dir, runtime.GOOS) if language != "go" && language != "rust" { return launcher, workflow } @@ -65,7 +65,14 @@ func scaffoldCommand(language, dir string) (launcher, workflow string) { if err != nil { return launcher, workflow } - return repositoryRuntimeLauncher(runtimeRel, runtime.GOOS), shellQuote(filepath.ToSlash(skillRel)) + return repositoryRuntimeLauncher(runtimeRel, runtime.GOOS), shellQuoteForPlatform(skillRel, runtime.GOOS) +} + +func shellQuoteForPlatform(value, goos string) string { + if goos == "windows" { + return "'" + strings.ReplaceAll(value, "'", "''") + "'" + } + return shellQuote(filepath.ToSlash(value)) } func scaffoldSkill(dir, language, sdkPath, description string) error { @@ -155,7 +162,7 @@ func scaffoldFiles(name, language, sdkPath string) map[string]string { ".cargo/config.toml": "[registries.operatorstack]\nindex = \"sparse+https://get.operatorstack.systems/cargo/index/\"\n", "Cargo.toml": fmt.Sprintf("[package]\nname = %q\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nyieldskill = { version = \"=%s\", registry = \"operatorstack\" }\nserde_json = \"1\"\n", name, v), "src/main.rs": mainRust, - "skill.json": "{\"version\":1,\"language\":\"rust\",\"run\":[\"cargo\",\"run\",\"--quiet\"]}\n", + "skill.json": fmt.Sprintf("{\"version\":1,\"language\":\"rust\",\"run\":[\"cargo\",\"run\",\"--quiet\",\"--bin\",%q]}\n", name), } default: gomod := fmt.Sprintf("module %s\n\ngo 1.26.5\n\nrequire github.com/operatorstack/yield v%s\n", name, v) diff --git a/docs/testing-fixtures.md b/docs/testing-fixtures.md index f8f4c17..c6b9e64 100644 --- a/docs/testing-fixtures.md +++ b/docs/testing-fixtures.md @@ -24,3 +24,7 @@ first. `teardown` runs after success or failure. Every hook receives Keep hooks small and repeatable. They should prepare or clean fixture state, not replace the workflow behavior being tested. + +For Rust, a fixture helper may add another binary under `src/bin/`. New +workflows name the primary workflow binary in `skill.json`, so Cargo still +runs the workflow without asking you to choose a binary. diff --git a/evals/results/latest.json b/evals/results/latest.json index df3310f..45b828e 100644 --- a/evals/results/latest.json +++ b/evals/results/latest.json @@ -1,8 +1,8 @@ { "schema_version": 2, "methodology_version": "1.1", - "generated_at": "2026-08-02T16:31:06.974Z", - "source_digest": "7e61b35562440679e70f58f61a73d06e13d6b823b72b36498a48a9156775dc20", + "generated_at": "2026-08-02T19:51:34.931Z", + "source_digest": "e5b21f13b239b762064fa3a4bd3d2354fba3dc46feb324f9bddbccad72827c24", "status": "passed", "workflow_conformance": { "passed": 40, diff --git a/release-notes/2026-08-02-final-dx-pass.md b/release-notes/2026-08-02-final-dx-pass.md new file mode 100644 index 0000000..2b77b24 --- /dev/null +++ b/release-notes/2026-08-02-final-dx-pass.md @@ -0,0 +1,6 @@ +# Yield 0.1.28 final DX pass + +- Print the created workflow path in TypeScript and Python `init` follow-up commands. +- Quote generated workflow paths for the active shell. +- Name the primary Rust binary in new workflow manifests so fixture helpers do not make `cargo run` ambiguous. +- Keep runtime and SDK behavior unchanged.