Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [0.14.10] — 2026-09-25

From round 5, the first measured trial round: seven trials on 0.14.9, all isolated. The
full-package trial generated every ESS output and ran `go test` against the synthesized suite: 13
passed, 0 failed, 0 skipped.

- `agentplugins-check trial-report` recognises every spelling of `go test` (`go -C <dir> test`,
behind environment assignments, after `cd … &&`) and only in command position; it had reported the
full-package run as "not run".
- `b10x setup plan` with no selection keeps every installed product, optional ones included; it
proposed uninstalling an installed `connectors`.
- `ess:specifying`: every `--kind` of `ess generate` (`schema`, `openapi`, `asyncapi`, `docs`,
`site`, `docs-ir`) and `ess generate types`; one `--out` per kind side by side, because nested
output directories are refused; which kinds add their own folder (`site` does not). OpenAPI and
AsyncAPI need a component that says how the service is reached, or they write `0 artifact(s)`
without a refusal (beyond10x/ess#102).
- `ess:testing-conformance`: generate the Go suite inside the implementation's module; return payload
numbers as `float64` (beyond10x/ess#101).
- Trials: `cargo` is allowed (the Rust type library can be compiled); the aep, worktree and upgrade
prompts say nobody will answer questions.

## [0.14.9] — 2026-09-25

Trials become measurable: their prompts live in the repository, and each run is scored against a
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/agentplugins-check", "crates/b10x"]

[workspace.package]
version = "0.14.9"
version = "0.14.10"
edition = "2021"
rust-version = "1.85"
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ tasks:
- |
(. '{{.T}}/env' && dir="$(cat '{{.T}}/workdir')" && mkdir -p "{{.T}}/work/$dir" && cd "{{.T}}/work/$dir" &&
timeout 2400 claude -p "$(cat '{{.T}}/prompt.txt')" --permission-mode acceptEdits \
--allowedTools 'Bash(b10x:*)' 'Bash(ess:*)' 'Bash(aep:*)' 'Bash(worktree:*)' 'Bash(git:*)' 'Bash(go:*)' \
--allowedTools 'Bash(b10x:*)' 'Bash(ess:*)' 'Bash(aep:*)' 'Bash(worktree:*)' 'Bash(git:*)' 'Bash(go:*)' 'Bash(cargo:*)' \
'Bash(mkdir:*)' 'Bash(ls:*)' 'Bash(cat:*)' 'Bash(find:*)' 'Bash(curl:*)' 'Bash(tar:*)' \
'Bash(sha256sum:*)' 'Bash(mv:*)' 'Bash(chmod:*)' Read Write Edit Glob Grep Skill Task Agent \
--strict-mcp-config --output-format stream-json --verbose > '{{.T}}/run.jsonl' 2> '{{.T}}/run.err'
Expand Down
57 changes: 56 additions & 1 deletion crates/agentplugins-check/src/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,44 @@ fn go_counts(output: &str) -> Option<GoTest> {
Some(counts)
}

/// Whether a shell command runs `go test`, however it is spelled: `go test`, `go -C <dir> test`,
/// behind environment assignments or after `cd … &&`.
fn runs_go_test(command: &str) -> bool {
let words: Vec<String> = shlex::split(command)
.unwrap_or_else(|| command.split_whitespace().map(str::to_owned).collect());
words.iter().enumerate().any(|(index, word)| {
if word != "go" && !word.ends_with("/go") {
return false;
}
// In command position: every word since the last separator is an environment assignment.
let start = words[..index]
.iter()
.rposition(|w| matches!(w.as_str(), "&&" | "||" | ";" | "|" | "then" | "do"))
.map_or(0, |separator| separator + 1);
if !words[start..index]
.iter()
.all(|w| w.contains('=') && !w.starts_with('-'))
{
return false;
}
let mut rest = words[index + 1..].iter();
while let Some(next) = rest.next() {
match next.as_str() {
"-C" => {
rest.next();
}
flag if flag.starts_with('-') => {}
verb => return verb == "test",
}
}
false
})
}

fn go_test(run: &Run) -> Option<GoTest> {
run.shells
.iter()
.filter(|shell| shell.command.contains("go test"))
.filter(|shell| runs_go_test(&shell.command))
.rev()
.find_map(|shell| go_counts(shell.output.as_deref()?))
}
Expand Down Expand Up @@ -657,6 +691,27 @@ mod tests {
std::fs::remove_dir_all(&sandbox).unwrap();
}

#[test]
fn every_spelling_of_go_test_is_recognised() {
for command in [
"go test ./...",
"cd impl && go test -v ./...",
"go -C /work/impl test -count=1 -v ./... 2>&1 | tail -20",
"ESS_REPORT_OUT=r.json go -C impl test ./...",
"/usr/local/go/bin/go test ./...",
] {
assert!(runs_go_test(command), "{command}");
}
for command in [
"go vet ./...",
"go -C impl build ./...",
"echo go test",
"cargo test",
] {
assert!(!runs_go_test(command), "{command}");
}
}

#[test]
fn go_test_counts_leaf_tests_of_the_last_run() {
let sandbox = scratch("gotest");
Expand Down
38 changes: 29 additions & 9 deletions crates/b10x/src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,9 @@ pub struct Context<'a> {
pub fn plan(context: &Context<'_>, inventory: &Inventory) -> Plan {
let catalog = context.catalog;
let present = present(catalog, inventory);
let selected: BTreeSet<String> = context.selection.clone().unwrap_or_else(|| {
present
.iter()
.filter(|id| {
context.only || catalog.product(id).is_some_and(|product| !product.optional)
})
.cloned()
.collect()
});
// With no explicit selection, what is installed stays installed, optional products included:
// leaving one out would uninstall it (connectors, trial 5).
let selected: BTreeSet<String> = context.selection.clone().unwrap_or_else(|| present.clone());
let offers = catalog
.products
.iter()
Expand Down Expand Up @@ -1360,6 +1354,32 @@ mod tests {
plan(&context, inventory)
}

#[test]
fn an_installed_optional_product_stays_when_nothing_is_selected() {
let mut claude = HostState::default();
claude.marketplaces.push(market());
claude.plugins.push(installed("b10x", "b10x", "user", None));
claude
.plugins
.push(installed("connectors", "b10x", "user", None));
let inventory = Inventory {
claude: Some(claude),
..Inventory::default()
};
let plan = run(&inventory, None, &[Host::Claude]);
let commands = commands(&plan);
assert!(
!commands
.iter()
.any(|c| c.contains("uninstall connectors@b10x")),
"{commands:#?}"
);
assert!(plan
.offers
.iter()
.any(|offer| offer.id == "connectors" && offer.selected));
}

#[test]
fn upgrade_installs_the_replacement_of_a_legacy_plugin_it_removes() {
let mut claude = HostState::default();
Expand Down
2 changes: 1 addition & 1 deletion plugins/aep/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "aep",
"displayName": "AEP",
"description": "Plan governed work in the AEP artifact store and deliver it in reviewed waves: decomposition, plan critique, reverse engineering, story scoping, implementation and adversarial review.",
"version": "0.14.9",
"version": "0.14.10",
"author": {
"name": "Beyond10x"
},
Expand Down
2 changes: 1 addition & 1 deletion plugins/aep/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aep",
"version": "0.14.9",
"version": "0.14.10",
"description": "Plan governed work in the AEP artifact store and deliver it in reviewed waves.",
"author": {
"name": "Beyond10x"
Expand Down
2 changes: 1 addition & 1 deletion plugins/b10x/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "b10x",
"displayName": "Beyond10x",
"description": "Set up, upgrade and check the Beyond10x plugins and binaries, route work to them, and create portable plugins.",
"version": "0.14.9",
"version": "0.14.10",
"author": {
"name": "Beyond10x"
},
Expand Down
2 changes: 1 addition & 1 deletion plugins/b10x/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "b10x",
"version": "0.14.9",
"version": "0.14.10",
"description": "Set up, upgrade and check the Beyond10x plugins and binaries, route work to them, and create portable plugins.",
"author": {
"name": "Beyond10x"
Expand Down
2 changes: 1 addition & 1 deletion plugins/connectors/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "connectors",
"version": "0.14.9",
"version": "0.14.10",
"description": "Set up, inspect, and invoke governed integrations through the connectors CLI.",
"author": { "name": "Beyond10x" },
"license": "Apache-2.0",
Expand Down
2 changes: 1 addition & 1 deletion plugins/connectors/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "connectors",
"version": "0.14.9",
"version": "0.14.10",
"description": "Set up, inspect, and invoke governed integrations through the connectors CLI.",
"author": { "name": "Beyond10x" },
"license": "Apache-2.0",
Expand Down
2 changes: 1 addition & 1 deletion plugins/ess/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "ess",
"displayName": "ESS",
"description": "Write, retrofit, validate and project Executable System Specifications, and hold implementations to them with conformance suites.",
"version": "0.14.9",
"version": "0.14.10",
"author": {
"name": "Beyond10x"
},
Expand Down
2 changes: 1 addition & 1 deletion plugins/ess/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ess",
"version": "0.14.9",
"version": "0.14.10",
"description": "Write, retrofit, validate and project Executable System Specifications, and hold implementations to them with conformance suites.",
"author": {
"name": "Beyond10x"
Expand Down
15 changes: 10 additions & 5 deletions plugins/ess/skills/specifying/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,18 @@ edit generated output around it.
Use the projection command, never a handwritten parallel generator:

```console
ess generate --path <specification> --kind schema --out <directory>
ess generate project openapi --path <specification> --out <directory>
ess generate --path <specification> --kind <kind> --out <directory>
```

Each writes into its own subdirectory of `--out`: `--out generated` gives `generated/schema/…` and
`generated/openapi/…`. Pass the parent, not `generated/schema`, or the files land in
`generated/schema/schema/`.
`--kind` is `schema` (JSON Schema), `openapi`, `asyncapi`, `docs` (Markdown pages), `site` (the
same pages as HTML) or `docs-ir`. Give each kind its own `--out`, side by side (`out/schema`,
`out/openapi`, …): an output directory inside another is refused. `schema`, `openapi`, `asyncapi`
and `docs` add a folder named after the kind below `--out` (`out/schema/schema/…`); `site` writes
into `--out` itself. Type libraries come from `ess generate types --target rust|go|typescript`.

**OpenAPI and AsyncAPI need a component** that owns the domain and says how it is reached
(`components.yaml`, `reached_by`). Without one, `openapi` writes `0 artifact(s)` and prints no
refusal: that is a missing declaration, not a clean result.

The same typed IR must produce the same ordered files and bytes. Compare a regenerated temporary
tree with the committed tree before replacing anything. A stale committed file is drift; a file no
Expand Down
6 changes: 5 additions & 1 deletion plugins/ess/skills/testing-conformance/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,11 @@ ess verify conform synthesize --path <specification> --target go --out <dir>
ess verify conform synthesize --path <specification> --target typescript --out <dir>
```

The package's `README.md` lists the methods and the wiring test. A method you cannot answer returns
Generate the Go package **inside the implementation's own module** (`--out <impl>/conformance`) so
its test can import it; a copy outside the module has no `go.mod` to import from. Numbers in a
payload you return compare equal only as `float64`: an `int64` or a `json.Number` fails although
the type check accepts it (beyond10x/ess#101, until fixed). The package's `README.md` lists the
methods and the wiring test. A method you cannot answer returns
`ErrUnsupported` (a skip), never a made-up result. Keep a suite for a domain nobody has implemented;
run it once something answers it.

Expand Down
2 changes: 1 addition & 1 deletion plugins/worktree/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "worktree",
"displayName": "Worktree",
"description": "Create, lease, finish, audit and safely clean isolated Git worktrees through the worktree CLI.",
"version": "0.14.9",
"version": "0.14.10",
"author": {
"name": "Beyond10x"
},
Expand Down
2 changes: 1 addition & 1 deletion plugins/worktree/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "worktree",
"version": "0.14.9",
"version": "0.14.10",
"description": "Create, lease, finish, audit and safely clean isolated Git worktrees through the worktree CLI.",
"author": {
"name": "Beyond10x"
Expand Down
2 changes: 2 additions & 0 deletions trials/aep-backlog/trial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ prompt: |
Our backlog for this little tool is just `TODO.md`. I'd like to plan it properly with AEP from
now on: turn what's there into epics and stories, leave out what's already done, and make sure
the result validates. Don't delete `TODO.md`.
Nobody will answer questions during this run: where you would ask, decide, write down what you
decided and why, and go on.

When you're done, list the skills and agents you used, quote any instruction text that confused
you, and paste the last validation output verbatim.
Expand Down
61 changes: 60 additions & 1 deletion trials/baseline.json
Original file line number Diff line number Diff line change
@@ -1 +1,60 @@
{}
{
"aep-backlog": {
"tool_calls": 313
},
"ess-full-package": {
"tool_calls": 68,
"validate": "valid",
"synthesis": {
"scenarios": 13,
"refusals": 0
},
"unmapped": 0,
"outputs": [
"asyncapi",
"conformance",
"docs",
"implementation",
"openapi",
"schema",
"site",
"types-go",
"types-rust"
],
"go_test": {
"passed": 13,
"failed": 0,
"skipped": 0
}
},
"ess-new": {
"tool_calls": 12,
"validate": "valid",
"unmapped": 7
},
"ess-pipeline": {
"tool_calls": 19,
"validate": "valid",
"synthesis": {
"scenarios": 12,
"refusals": 0
},
"outputs": [
"conformance",
"docs",
"schema"
]
},
"ess-retrofit": {
"tool_calls": 29,
"validate": "valid",
"synthesis": {
"scenarios": 16,
"refusals": 2
},
"unmapped": 7
},
"worktree-onboarding": {
"tool_calls": 19
}
}
3 changes: 2 additions & 1 deletion trials/upgrade-seeded/trial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ name: upgrade-seeded
kind: upgrade
prompt: |
I installed the Beyond10x plugins a while ago and haven't touched them since. Bring everything up
to date, and tell me what changed.
to date, and tell me what changed. Nobody will answer questions during this run: where you would
ask, decide, write down what you decided and why, and go on.

When you're done, list the skills and agents you used, quote any instruction text that confused
you, and paste the last command output verbatim.
Expand Down
2 changes: 2 additions & 0 deletions trials/worktree-onboarding/trial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ prompt: |
Set up worktree for this repository so I can work on changes in isolated checkouts. Then use it
to fix the typo in `README.md` in an isolated checkout on a branch, commit it there, and tell me
where the checkout is. Leave this checkout as it is.
Nobody will answer questions during this run: where you would ask, decide, write down what you
decided and why, and go on.

When you're done, list the skills and agents you used, quote any instruction text that confused
you, and paste the last command output verbatim.
Expand Down
Loading