Repository navigation
Releases: gonimar/claude-web-studio
Release list
v0.14.0
Nothing new to run: this release verifies and slims what 0.13 shipped, and adds the four technology groups the audit
found missing. Audit of 2026-09-26 (plugin-validator, skill-reviewer on the eleven heaviest skills, the
skill-development checklist on the other thirty-nine, claude-md-improver, automation-recommender): 7 blocking defects,
10 false semantics, all closed here; the full report and the reviewers' notes stayed in the maintainer's dev/.
- Behavioural evals (
evals/,claude plugin eval). 21 cases for dev-story, code-review, story-done, init,
adopt, refactor, hotfix, update, help and impact, each derived from a spec case intesting/skills/, graded
deterministically (the skill's verdict or gate phrase in the final message, a tool that must or must not run, no file before the
gate); scaffold helpers build thetesting/e2efixtures in the run's workspace. CI gains anevalsjob
(workflow_dispatchwith a case glob, nightly, cost ceiling, results as an artifact);claude plugin validate .claude-plugin/plugin.jsonjoins the marketplace validation in CI,tests/run-all.shand the kit's CLAUDE.md —
.alone validated onlymarketplace.json.testing/README.md§ Evals says how an eval differs from a spec;
catalog.yamlcarrieslast_eval/last_eval_result. A deliberately broken grader was shown red (exit 1) before
it was removed. Local finding: the eval sandbox does not start Bash as root (seccomp), so the green run is CI's. agent:was never doing anything. Twenty-nine skills declaredagent:; a live probe with a sentinel in the
agent showed no subagent was started and the agent's prompt never applied — the field acts only with
context: fork, which no skill uses (and cannot: a forked skill has noAskUserQuestion, so no gate). The field is
gone; the linter rejectsagentwithoutcontext: forkand requires theweb-studio:namespace on it.model:
does act — on the whole turn, the closing hand-off included — so 43 sonnet/haiku pins are gone too;helpkeeps
haiku, the six opus skills keep opus, and the linter refuses a model pin on a skill that edits files.help's
undocumentedcontext: |block is deleted. CONTRIBUTING § 3 states the field semantics as Claude Code applies them.- The eleven heaviest skills read shorter and carry every rule. Shared fragments (language,
<hooks>,
namespaces, gates, the documents-lane commit gate, theSkilltool, CI wait, verdict and next step) are defined
once in coordination-rules § Skill conventions and named by one line; per-stack and per-mode detail moved verbatim
toskills/<name>/references/(checks-go/php, baseline-go/php, apply, routing, interviews, close edits, DoD
recipes, signals…), read on the step that needs it. On-invoke perclaude plugin details: refactor 7.8k → 4.3k,
dev-story 6.9k → 5.0k, code-review 5.3k → 3.2k, adopt 5.1k → 4.0k, update 4.5k → 3.7k, hotfix 4.4k → 3.8k, help 4.4k →
3.2k, story-done 4.3k → 3.7k, impact 3.7k → 3.2k, init 3.6k → 2.9k, setup-stack 3.6k → 2.7k — 53.6k → 39.7k tokens
(−26 %); the same graders old-vs-new on every case: no case lost, three gained./skill-test static all: 50 COMPLIANT.
Defects closed on the way: session state now records the namespaced/web-studio:code-review(a resumed plugin-mode
session ran Claude Code's built-in review); story-done's studio-review check recognised only leads and engineers
(a technical-director or accessibility-specialist review read as "no studio review"), a Done story with an open PR
re-ran the close, a pull_request-only CI with no PR deadlocked DONE; setup-stack cited a layout ADR nothing wrote and
could lower a brownfield stage; init had no<root>in copy mode and let an installed plugin turn a copy-mode
project into plugin mode; update's restart check came after the seed gate; hotfix's chore lane had no verdict;
impact'ssolo/leanmodes had no defined outcome; adopt copied rule 7 verbatim. - One collaboration protocol, not thirty copies. The 380-word block every agent carried (55 % of the agents'
text) is the preloaded skillcollaboration-protocol(user-invocable: false); each agent keeps a one-paragraph
pointer andskills: [collaboration-protocol, …]; a forked agent was shown to receive it. The agent-development
pass: directors drop the inertTask/AskUserQuestionpre-grants and return questions to the caller; the four
leads that "routed work" name the specialist the session dispatches and say they do not spawn;## Neverfor
seo-specialist, appsec-engineer, network-security-engineer; acolorper tier; protocol item 10 says what agent
memory holds and thatMEMORY.mdis read first; the plugin-mode memory directory is named. templates/CLAUDE.md.templatecut to what its imports do not already say (audit of 2026-09-26: 80 lines, about 45 of
them restating the@-imported coordination-rules). Principles keep their numbers and names and point to the rule or
section that owns them;directory-structure.mdandsecurity-baseline.mdare referenced by path, not imported (principle 4 keeps the
four baseline items in one line); merge-policy line under principle 5,/help commands//help guidepointer, "managed by/update"
on the two studio sections. Principle 3's package-health checks move torules/dependencies.md(manifests and lockfiles);
principle 9's "neighbouring code stays as it is" reaches the TypeScript, Vue and Angular rules as it already did Go and PHP.
Principle 7 loses its incident: catalog artifacts authored in the chat, and product code written right after/init
because the goal "looked trivial", skipped the steps' gates and templates — that is why the first code goes through/start.- Kit
CLAUDE.mdnames the commit gate (tests/run-all.sh), the kit ↔ project path mapping,tests/vstesting/vs
evals/, the hook contract and the rule that the template's principle numbers are never renumbered. - Observability and Kubernetes references (
observability.md,kubernetes.md): JSON logs, Prometheus/metrics,
OpenTelemetry over OTLP,/healthz+/readyz; the kubernetes deploy target gets its shape (one chart per service,
values per environment, Gateway API, probes on those endpoints, requests/limits, PDB, HPA, external-secrets, images
by digest, rollout and rollback). Wired through technical-preferences, setup-stack, stack-update,rules/kubernetes.md
and the Go/PHP/TS rules, devops-lead/devops-engineer and the backend engineers,/adoptdetection,/deploy. - Supply chain (
supply-chain.md): syft SBOM, cosign keyless signing verified by digest before deploy, GitHub
provenance attestations, Renovate with a release age, lockfile and registry hygiene, Actions pinned by SHA; templates
(renovate.json,release-attest.yml,verify-image.sh);/hardengets a supply-chain check group,/release-checklist
three items with commands,/deployverifies the signature,/dependency-auditscans the SBOM,/adoptdetects it. - LLM in the product and MCP for the studio (
llm-integration.md): the Anthropic SDKs and Messages API
essentials, MCP as a product feature, evals, cost, and the security table mapped to the OWASP GenAI LLM Top 10;
/threat-modeladds the prompt-injection surface (trust boundaries, tool side effects, RAG, MCP) when a project
declares or imports an LLM SDK;rules/security-sensitive.mdand impact-guard coverprompts/,llm/,mcp/.
.mcp.jsondeclares one optional server, Playwright (a11y-audit, perf-audit and pentest drive the live page through
it when present; the CLI path stays the default; the playbook says what it costs and how to switch it off). The
GitHub MCP server is not declared — a plugin server starts for every user and this one fails without Docker or a
token — the playbook says how to add it (remote OAuth or Docker, toolsetspull_requests,actions); story-done and
hotfix may use it instead ofghwith the gates unchanged. Two hooks watch it whatever its toolsets: the new
github-mcp-guardblocks its file-writing tools (commits go through git and its hooks) andsecret-guardblocks a
token or key in any of its inputs — a PR body or an issue as much as a file. - Symfony and Laravel are full references (107 and 118 lines, from the frameworks' documentation repositories):
versions and support, the studio's defaults, idioms per layer with theFRAMEWORK_NAMESPACESkeys, testing,
performance, security, upgrade paths, review checklists; corrections to the stubs (Symfony 8.1 shipped 2026-05-29,
Laravel 13 on 2026-03-17). php-engineer reads the framework file first; code-review and refactor gain framework checks. - Descriptions say what and when. Twenty-eight rewritten from the audit (refactor 678 → 349 chars; fourteen gain
the "use when …" sentence with the phrases a user types; story-done names the PR open and merge, init is "the
studio's init, not Claude Code's", hotfix names--chore, code-review is "the studio's review, not the built-in").
The description optimizer (skill-creator, 20 queries × 3 runs × 4 iterations) on help, backlog and impact: precision
100 %, recall 11–33 % whatever the text — the model answers "what now" itself in a bare project; the descriptions that
scored best on the held-out set are the ones shipped. Seven descriptions shrink and twenty-two grow. What that
costs is not tokens: Claude Code caps the skill listing at about 1 % of the context window (≈ 8,000 characters at 200k;
SLASH_COMMAND_TOOL_CHAR_BUDGETraises it) and drops the least-used descriptions to name-only beyond it — the 50
descriptions total 18.8k characters (16.8k on 0.13), so with no usage history 18 of 50 keep theirs in the listing
(23 before), the rest until they are used. README § 1 documents the cap; on-invoke is where the budget went. - Roadmap "Toward 1.0": condition 5 gains its C...
v0.13.0
Two sources: the lab's traces of two real projects (the sprint cycle, comments, agent memory — WS-131, WS-136…139), and
the skill pass with its follow-ups (#62, #64–#67).
- Agent memory is committed with the work that produced it. Thirty agents write
.claude/agent-memory/<agent>/**
on every run and no lane owned those files: a Haiku/story-donehid 16 of them from ten stories ingit stash push -u,
where they sat for 28 hours until the owner asked what was hanging (WS-136). git-workflow gains the Agent memory
lane;/dev-story,/refactor --apply,/code-review, the audits and/team-*stage.claude/agent-memory/with their commits;
/story-donerequiresgit status --shortandgit stash listempty and offers one more commit, never a stash;
the hooks treat.claude/agent-memory/as the documents lane (a memory-onlydocs:commit of an audit on the
default branch passes); coordination-rules § Subagents says it in one sentence. - The retrospective closes the sprint — and nothing else does. A sprint had no act of closing:
/retrospectivewrote
its section,/sprint-planfolded the previous block only as a side effect and only when every story was[x], so a
sprint with one carried-over story never closed, sprints closed with no retrospective at all, and one block was folded by
hand. Now/retrospectivePhase 4 moves unfinished stories back to the Backlog with their markers, folds the block
(closed · N stories · K carried over), writesStatus: closed YYYY-MM-DDinto the sprint file and reconciles its
## Storiestable from the roadmap;/sprint-planisBLOCKED (sprint NN open — run /retrospective NN first);
/story-donerecommends the retrospective after the last story;/sprint-statusprints the sprint's status and
OVER (not closed);/helpprints anAttention:line. Templatesprint-plan.mdgains theStatus:header;
/migrate sprintsadds it to older files. - The sprint file's Stories table is kept in step.
/sprint-statusread story states from a table that only
/sprint-planever wrote (six closed stories still read Ready on one project, the owner synced it by hand)./dev-story
and/refactor --applyset the row to In Progress,/story-donetoDone · ⏱ Nh · PR #N(third place in its proof
step),/sprint-statusreads states from the roadmap and counts a differing row assprint file ≠ roadmap. - Actual time in one clock.
Started:carries its offset (date +%FT%H:%M%z),/refactor --applywrites it too
(four refactor stories had their times read offgit reflog), and/story-donenever takes "now" from a CI or GitHub
UTC stamp — 10.5 h became 8 h and 2 h became 8 h that way, and the calibration ratio ran on those numbers. - Comments are the contract and the reason, not the story's history (
rules/comments.md). Agents knew the godoc
form (228 of 229 doc comments start with the name) and nobody had said what goes inside: on two projects the rendered
go doc -allcarried 426 and 315 lines of story ids, "pre-S-050", "used to" and review rounds, one file was 35 % comments,
and the projects had 0 and 1 TODOs — the gaps lived in prose ("left as a follow-up if a future story wants"). The rule
names two kinds (doc comment = contract, body = reason), what never goes in code, the tests-only exception for ids, and
TODO(S-NNN):/TODO(I-NNN):as the only TODO./code-reviewchecks the added lines (TODO-NOID,COMMENT-HISTORY,
COMMENT-LONG),/story-donechecks every new TODO's id exists,/tech-debtlists TODOs with closed or unknown ids and
comment history per package. Templates:revive(exported,package-comments) +godotingolangci.yml, Slevomat
comment sniffs inecs.php(slevomat/coding-standardadded by/test-setup), an ESLint snippet intypescript.md.
New code only — existing files are achorestory per package, never a drive-by.
Skill pass and follow-ups (#62, #64–#67)
A pass over all 50 skills: bodies rewritten as numbered steps in the order they run, and the defects that hid in
the long paragraphs fixed. No rule was dropped; every spec case was re-checked against the new text. The style the
pass followed is now written down in CONTRIBUTING § 3 ("How the body reads").
- Consent is collected by the parent. Eight skills told delegated agents to "follow the same protocol" or to
"show code before writing (the user approves)" — aTasksubagent cannot ask the user, so the rule either stalled
or was silently skipped. The plan / Proceed answer now names the agents and the files each will write and covers
those files; a specialist that needs more stops and reports, and the parent asks. On a/dev-storyfixture run
this cut the happy path from five questions to three with the same result. - Steps that ran before their inputs existed.
/dev-storywaited for CI in Phase 5, before the Phase 6 push;
/code-reviewre-reviewed a fix commit it had not made yet;/test-setupgenerated configs before asking to
write them;/sprint-statusranagent-stats --sincebefore it knew the sprint's start date;/story-doneasked
"CI is green, merge?" before the PR that starts apull_requestworkflow existed. /story-donepushes its close commit before the merge (gh pr mergemerges the remote head, so the
docs: closecommit was lost with the local branch), waits for CI on the PR's latest commit, and marks a draft PR
from/dev-storyready beforegh pr merge, which refuses drafts. The PR number is read (or the PR created) before
the close edits that cite it; "close without the PR" skips the merge phase; red CI on the close commit leaves the
close in place and says "closed, not merged".git pull --ff-only origin <default>in/dev-story,/story-done,/release-checklistand git-workflow:
a local default branch without an upstream refused the bare pull in both fixture runs.- Gates and modes the specs expected but the skills lacked:
/release-checklist's first-release
observability gate, adocs: releasecommit before the tag so the tag contains the release document, and the
tag as a separate question onREADYwith-m(no editor), pushed to origin;/deploy rollback
(the playbook and the delegate contract already relied on it);REJECTEDin/architecture-decision; BLOCKED
stops in/feature-spec,/data-model,/create-stories,/sprint-plan,/api-contract,/stack-update,
/docs,/migrate,/hotfix,/test-setupthat the verdict lines named but no step produced; write gates for
/impact's findings and backlog rows,/perf-audit,/a11y-audit,/security-audit,/harden's in-repo
config edits,/setup-stack's stage change and compose-ssh files. - Commands that did not do what they said:
/migrate's ID check used ERE syntax withgrep -oand matched
nothing;/update --dry-runranclaude plugin updatein plugin mode;/skill-improve's revert could discard
kept iterations and the user's uncommitted edits;/changelogfailed in a repository without tags;
/team-release's "any FAIL stops" never matched/perf-audit'sOVER BUDGETor/dependency-audit's
ACTION REQUIRED. - Plan-level consent is in the rules, not only in the skills: coordination-rules rule 7 says the caller's plan
answer approves the files the plan names (with.write-consenttouched before eachTaskbatch), and the agents'
protocol item 4 says the brief's files carry the "yes". - The run-evidence numbers that explained individual rules ("35 %", "126 vs 5", "966 k", …) left the skill bodies under
the new CONTRIBUTING rule; the rules stay, the numbers remain in the 0.12 section of this changelog. - Namespaces:
/team-feature,/team-game,/hotfixhand off to/web-studio:code-review,/startto
/web-studio:initin plugin mode (the bare names are Claude Code built-ins). /refactor: thego doc ./pkg/...API baseline never worked (go doctakes no package patterns) and was never
captured before Phase 6 compared against it — now saved in Phase 2 withgo list | xargs go doc -all;--apply
has one start question before the branch and a push question; PHP gets its own check column and engineer./architecture-reviewcould not do what it asked: it runsgo list,make arch-checkand records findings, yet
had neither Bash nor Write/Edit —allowed-toolsgains them, with the findings row behind a gate./helpapplies the findings and game-gate overrides while choosing NEXT, not after printing it;/adoptmoves the
Go items that sat under PHP, names unrecorded HIGH items in the verdict and gates its Phase 3–4 writes./game-conceptno longer carries stack versions (they live indocs/stack-reference/);/skill-testtakes spec
paths from the catalog and acceptsPLANNEDlike the linter.install.shwithoutrsynccopied the excludedhooks.jsoninto a fresh install (the fallback protected the file
only when it already existed);tests/installer.shfailed on every machine without rsync./updatein copy mode
compared the kit's version with the skill directory's name, which carries none — it now compares with the version the
gate named and re-asks when the kit moved on./story-done's "the studio's review ran" grep missed reviewers with two
hyphens (web-game-engineer)./setup-stackno longer carries stack versions in its interview; the numbers come from
stack-reference/index.mdat question time, as the rule for version facts says.tests/run-all.shlints
testing/e2e/*.shandtesting/e2e/fixtures/*.shtoo.- Testing:
testing/e2e/fixtures/make-story-fixture.shbuilds a story-ready synthetic project (happy,
no-strategy,merged-spike) for comparing two versions of a pipeline skill; dev-story spec gains two cases;
validate-structurechecks hooks called as<hooks>session-state.sh …too.
#...
v0.12.0
Findings of a week-long manual run on a real project (Web Studio 0.11.1, ten sessions, 250 agent runs) and of an
external PHP project. Nothing new to learn: the same commands, the same agents — cheaper, and harder to bypass.
- One story per session (coordination-rules rule 13). 80 % of the week's spend was the parent's context, not the
agents' work: sessions ran to 966 k tokens with two compactions in ten sessions, and 85 % of requests re-read more than
200 k./story-donenow hands off to/clearbefore the next story (continuing in the same conversation is no longer
an offered option),/dev-storyrefuses to start a story past 50 % context without offering/clearfirst, and
/code-reviewsends a re-review after fixes to a fresh session past that mark. Playbook § 10.25 says the same in two languages. - Commands carry their namespace. A bare
/code-reviewin a hand-off ran Claude Code's built-in review skill of the
same name — nine general-purpose agents, no routing table, no appsec — and its findings travelled under the studio's name;
once a security-sensitive story was closed on its word. Every hand-off indev-story,refactor,code-review,
story-doneand every line/helpprints now names/web-studio:<command>in plugin mode; coordination-rules § Subagents
says why, next to the same rule forsubagent_type. /helpno longer starts the next command. It runs on Haiku, and a skill started from its closing question inherited
that model: a/code-reviewand a/story-doneran on Haiku and closed a story with invented numbers. After the answer
/helpprintsRun: /web-studio:<command>and ends the turn.- The studio's review has to be in the log.
/story-doneDoD: a reviewer'sSubagentStopafter the branch's first
commit,appsec-engineeramong them for a security-sensitive story — otherwiseNOT DONE (no studio review). - A verdict is the reviewer's. After fixes
/code-reviewsends the fix diff to the reviewer that raised each finding
and moves NEEDS CHANGES to APPROVED only on its answer — never on a green CI (five of five verdicts had been flipped by
the parent). A BLOCKING is downgraded only by the reviewer that raised it or bytechnical-directorthrough/impact.
Reviewers get the diff as one file and open every verdict withRead: N/M files;N < MisPARTIALand never
counts towards APPROVED (35 % of lead reviews had been cut off mid-diff). - Engineers write the code, in every mode. The rule lived in
dev-storyandrefactor; on an external PHP project
the session drifted into "faster to fix it myself" from the third review round. It is now in coordination-rules
§ Subagents, principle 8 of the CLAUDE.md template,code-reviewPhase 5 andhotfix— and it has an observer:
hooks/parent-write.sh(PreToolUse Write|Edit, warn-only) names the engineer the roster has for a code file the
session writes itself and leaves aParentWriteline inagent-audit.log;agent-stats.shand/sprint-statusprint the count. - Turn budgets and briefs.
maxTurns30 → 60 for the implementing Tier-3 engineers, 25 → 40 for the leads and
appsec-engineer(go-engineer hit its limit on 55 % of runs, test-engineer on 50 %, backend-lead as a reviewer on 35 %).
Thedev-storybrief has six fixed lines (Story · Readwith line ranges ·Write · Check · Skip · Report) because
half of an engineer's turns went intogrep/sednavigation of files the parent had already read; every agent's
protocol gains point 8 —Read/Grepon the brief's paths, shell search only when the path is unknown, and a
Checkpoint:line kept in the result so a cut-off hands the caller the point to resume from. After a cut-off the parent
sends one resume line and runs no checks of its own until theSubagentStop(50 of 64 cut-offs had been answered with
the parent's owngit statusand builds). - The roster says what the tiers do. Leads review, design and name the specialist; the session dispatches. The
Task
tool and "routes work to …" leave the seven leads — measured on real projects, no lead ever spawned an agent. - Hooks, four false positives closed and one gap:
validate-commitblocks provider-shaped credentials everywhere but only
warns onpassword = "…"shapes in test code and agent memory (three test files had been rewritten to please it);
validate-pushreads the commit from agit commit … && git pushcall instead of an emptyrev-list(docs commits
on the default branch cried wolf three times a day);written-pathsresolvesp='…'; open(p,'w')and
Path(p).write_text(…)(27 document writes in one session had passed every guard);consent-guardstays silent on a
one-lineStatus:/Started:/Actual:edit, on a command that sets the marker before writing, and on
docs/ops/measurements/(rule 12 asks for those).hooks/session-state.shis executable — skills call it as a
command and one call failed withPermission denied; the structure linter checks the bit for every hook a skill calls.
v0.11.1
- Review follow-ups of 0.11.0.
/refactorwithout an argument is a full interview: every architecture and tooling choice is asked with the current value first, a kept value produces no step and a changed one becomes an ADR-first plan step — this is how the owner changes the approach of a running project; with an argument only that mode's questions are asked. Thego_layers/php_layersfields are gone: the use-case shape is the tree the layout ADR shows, and/architecture-review codealready compares the tree with the ADR./code-reviewlints a layered Go diff with--new-from-rev=<default-branch>only; the whole-module run belongs tomake ci.stack-reference/index.mdhas aVerifiedcolumn carrying each file's ownupdated:date, so a partial refresh no longer has to lie with one header date;/stack-updatefills the row.rules/*.mdfollow CONTRIBUTING §5 again — short imperative bullets with a link — and the Go, PHP and test rules stop restating the stack-reference tables (thecmd/contract and the numbers a story reports now live inrules/go-code.md, which every*.goedit loads; go-engineer and php-engineer keep the role, the mechanics only they add, the Never list and the Reference line).post-edit-checkhas onesmellhelper for Go, PHP and Vitest (setTimeoutwaits,toThrow("string")) so the patterns cannot drift apart.coverage-gate.phpstreams the clover report withXMLReader, so a large project's report fits the defaultmemory_limit. Therefactorrow sits with the pipeline rows intesting/catalog.yaml.
v0.11.0
- PHP is separated from its framework.
stack-reference/php.md(new) holds the language (8.5 the target, 8.4 the floor for property hooks and asymmetric visibility — the fixture and every tool ran on both 8.5.10 and 8.4.25), the two architecture styles, tests by layer and the tooling;yii3.md(renamed fromphp-yii3.md— technology files carry no language prefix, likeangular.md) keeps only what is Yii3;symfony.mdandlaravel.mdare stubs (versions, where the layers live) to be filled by/stack-updateand the first project on them.php_framework(yii3 · symfony · laravel · slim · none) is asked in/setup-stackand detected by/adoptfromcomposer.json; where the reference is a stub,php-engineerworks from the official documentation and says so in every result — the framework is an Infrastructure detail, never the shape of the code. security-lead, performance-engineer and backend-lead readphp.mdfirst. php_architecture: layered(src/Domain→src/Application→src/Infrastructure,php_layersper-context or flat) is enforced bydocs/templates/php/deptrac.yaml, whoseFrameworklayer holds the framework's namespaces (the exact value is in each framework file) and is allowed only from Infrastructure — so/refactor framework --dry-runcan inventory what a framework change would touch; theVendorlayer isPsr\plus thephp_domain_allowlist, nothing pre-approved. Rich models use PHP 8.4 asymmetric visibility (public private(set)), verified to block writes from outside. graphql-php'sBuildSchematakes an entity namedSubscriptionas the root subscription type exactly as gqlgen does, so the root-name rule and theGQL-ROOThint cover both.- Recorded tool choices replace hard-coded ones:
php_static_analysis(PHPStan level 9 · Psalm level 1),php_cs_tool(ECS withperCs: true· php-cs-fixer@PER-CS) with a template for each, PER Coding Style 3.1 named as the current edition, PHPUnit 13 as the baseline (Pest 5 on top of it when the project prefers it),scripts/coverage-gate.phpreading the clover report per layer (--root=for anapp/tree), andcomposer ci(validate → lint → stan → arch-check → tests with coverage → coverage-gate;composer ci-fulladdscomposer audit, the network step). Verified on aphp:8.4-clifixture: deptrac fails on aDomain → Infrastructuredependency, PHPStan 9 and ECS run clean after--fix, the gate printsDomain 100.0% >= 90% OKand fails atApplication=100, the template chain runs end to end.post-edit-checkhintsTEST-SLEEP/TEST-ERRSTRon*Test.phpand formats through ECS when it is installed;/code-review,/architecture-review code,/test-setup,/dev-story,/tech-debtand/refactor(modes<namespace>,layout,tests,framework) carry the PHP twins of the Go rules. - Migrations are named explicitly: schema changes are migration files, never DDL in a class or a story (
rules/database.mdsays so;rules/php-code.mdandphp-engineerrepeat it for PHP). - Go has two architecture styles and the studio can tell them apart.
stack-reference/go.mdgains "Architecture style" and "Layered architecture":go_architecture: layered(internal/domain→internal/usecase→internal/infrastructure, dependencies inwards only, ports declared in the domain, one struct per use case withExecute, rich models with unexported fields and a validating constructor, DTOs or bound domain types pergraphql_models) next tomodular(internal/<domain>/). The rule is a linter rule:docs/templates/go/golangci.yml(v2 — a v1linters-settings:file is rejected by golangci-lint 2.x) carriesdepguardlists for the two inner layers, and thearch-checktarget indocs/templates/go/Makefile.snippetadds the transitive check over the package graph (go list -deps -test) that per-file depguard cannot do; the two are complementary, not the same test. Verified before shipping: a domain file importing an infrastructure package fails both; theusecaselist runs inlist-mode: laxbecause depguard's default mode deniedgithub.com/google/uuidin a use case./setup-stackasks the style, the directory shape (go_layers,go_composition_root), the router (go_router: chi or ServeMux), the GraphQL model mode, the value libraries the domain may import (go_domain_allow) and the coverage thresholds;/adoptrecords the style from the tree and offers/refactor layoutinstead of restructuring;technical-preferences.mdhas the fields.go-engineer,graphql-engineer,backend-lead,/code-review(LAYER,RICH-MODEL,COVERAGE,TEST-LAYERfindings by command output) and/architecture-review code(dependency direction from the graph) read them. - Tests have a layer and a gate.
go.md"Tests by layer": domain tests without doubles, use-case tests with func-field fakes (≤ 3 methods) ormoqand no I/O, infrastructure against containers;docs/templates/go/coverage-gate.sh(installed asscripts/coverage-gate.shby/test-setup, called bymake coverage-gateon the profilemake testalready wrote) fails the build belowgo_coverage_domain(90 %) /go_coverage_usecase(80 %) — two explicit technical-preferences fields that/test-setupwrites into the Makefile variables, the one place the build reads them — and prints one line per layer;make layout-checkcovers every binary undercmd/;make ciis the local gate andmake ci-fulladdsgovulncheck. Rules every Go test now follows, inrules/tests.mdandrules/go-code.md:errors.Is/errors.Asagainst a sentinel — nevererr.Error() == "…", which breaks on the first%wwrap; notime.Sleepto wait for a result —testing/synctestor a polling helper with a deadline; case names in English.post-edit-checkhints on a_test.gowith a sleep (except insidetesting/synctest, where sleeping on the fake clock is the recommended form) or an error compared to a string, andGQL-ROOTon a.graphqlswhosetype Subscriptioncarries anidfield and noschema {}block;/code-reviewreports them asTEST-SLEEP/TEST-ERRSTRfromgrepoutput. /refactor— the studio can change the shape of the code it maintains without changing what it does. Dry-run by default: a baseline by numbers (build, vet, lint, tests, coverage per layer, layout numbers, dependency direction, package sizes, test smells) before any question, the architecture choices as in/setup-stackwhen the mode islayout, a step plan whose first step is always characterisation tests and whose second is the tooling that will fail on purpose, then a plan document indocs/ops/refactor-<date>-<scope>.mdand stories through/create-stories. Code moves only with--apply S-NNNonrefactor/S-NNN-<slug>: every step throughgo-engineer,go build && go test -raceafter each, one green step perrefactor(S-NNN): …commit, a red step fixed or reverted and never committed, and a before/after table that must not regress (tests, coverage,depguard, layout, the public API ofpkg/). Modes<package>,layout,tests; no argument runs every applicable mode after asking all questions up front. Go in this version. Catalog step, spectesting/skills/pipeline/refactor.md, playbook § 5.5.- The GraphQL SDL of a Go module lives at
api/schema.graphqlsand is read in place.api_contract_pathin technical-preferences names the one schema file (docs/architecture/api/schema.graphqlfor PHP/Node projects);/api-contract,graphql-engineer,rules/api-contracts.mdanddocs/templates/go/gqlgen.ymlpoint at it, anddocs/architecture/api/api-contract.mdlinks to it instead of holding a copy. A schema type namedQuery,MutationorSubscriptionthat is not the root is a BLOCKING finding before generation: gqlgen treats those names as the root operation types, and an entity calledSubscriptioncame out as a set of channel-returning subscription resolvers when the example from a widely shared guide was generated.
v0.10.6
- The agent summary counts agents, not events. 0.10.5 paired starts and stops by
aidand printed "K starts without a stop"; a project session recounted by name, got 126 where the tool said 5, and reported it. Both numbers were answering different questions and one of them is not a question worth asking:SubagentStartfires on every resume of the same agent — oneaidin a real log carries eight starts and no stop — so starts minus stops counts resumes, not losses. What the log does support is the agent: anaidstarted and never closed (five on that project, seven on another) means an agent cut off at its turn limit or still running, and the story it was given is where to look. The summary now says that, and says when it cannot: lines written before the id fields existed are named as unpairable, so a silent zero no longer reads as "clean" when it means "blind" — 139 of that project's runs are in that state. (WS-117)
v0.10.5
/sprint-statussays who did the work.hooks/agent-stats.shsummarises the traillog-agentalready keeps: runs all-time and within the sprint, the top five agents (the plugin'sweb-studio:go-engineerand copy mode'sgo-engineercounted as one agent, not two), and two lines that were previously invisible without opening the log — starts with no matching stop (an agent cut off at its turn limit, or still running) and runs of non-studio agents (general-purpose,Explore), which mean routing went around the roster. Both go under Risk to the goal when they are not zero. On real projects the second line reads 106 of 260 runs and 22 of 255 — the oldest open defect in the register turns from an observation into a number the project sees itself. Costs nothing: one pass over a few hundred local lines.
v0.10.4
hooks/session-state.shnever destroys a state file again, and converts one on request. Shipped in 0.10.3, it rebuiltactive.mdfrom its own seven fields and silently dropped everything else: on the first project that used it, a 148-line working state — sprint status, fifteen lessons, tech debt, a cleanup plan with an open item — became nine lines, in a gitignored file with no archive behind it; the content came back only because the session had read it earlier. The writer now refuses any file it cannot round-trip, says how many lines it does not understand and what to do instead, and backs the file up before every write it does make (last five kept).session-state.sh migratedoes the conversion once and deliberately: the whole previous file is archived toproduction/session-state/archive/active-<timestamp>-premigration.md, the fields it recognises — including the bold**Task**:spelling — are carried over, and the output says where the rest belongs (sprint file, agent memory, findings, backlog). The template says what lives in this file and what does not. Thepost-edit-checkhint no longer points at the call that did the damage. (WS-115)/impactno longer writesNext:.Next:is where the session was heading and a triage is usually a detour from it, so the skill's own instruction contradicted coordination rule 7 — a project session hit exactly that, withNext: /sprint-plan 04pending while/impactwas told to overwrite it. The verdict's commands go into the dated note and the closing question;Next:changes only when the user picks one there, because that choice is what makes it the task. (WS-116)/dev-storystates that the parent does not write the story's code. The skill said "implementation via Task to the right engineers" and named them, but nothing said what happens when an agent is cut off at its turn limit — and across four stories in a row the implementing agent was spawned once, wrote nothing, and the parent wrote 687 lines itself while the stories passed. The rule is now explicit: every file throughTaskwith an explicit studiosubagent_type, a truncated agent is resumed rather than replaced, twice-truncated work is split into smaller calls, and a parent-written fallback is recorded in the story result in one line. Phase 3 plans in agent-sized steps and names the agent per step; Phase 5 compares the agents that actually started inagent-audit.logagainst the ones the plan named. (WS-087)- CI no longer cancels runs on the default branch:
cancel-in-progressis now conditional, after three PR merges went in while the run that was checking them was cancelled by the next push. (CWS-07) - The settings templates stop auto-approving
pnpm dlx <any package>:Bash(pnpm *)is replaced by the same narrow verbs npm already had (install,run,test,audit,outdated). A permission rule that runs arbitrary code from the network is not a convenience. (CWS-12)
v0.10.3
/brainstormwrites its concept brief from a template that now exists. The skill createddocs/specs/concept-brief.mdand/product-specPhase 1 read it as input, butdocs/templates/had no brief — so each run invented a shape and the next skill looked for facts where they were not. Template, a row inrules/docs-format.mdand apost-edit-checkbranch; an empty section is writtenn/a — reasonrather than dropped. (WS-100)/architecture-decisionhas an outcome for "no ADR needed". A discussion that ended in rejection had nowhere to be recorded, and one ended instead with a productionDockerfileedited from a document session — bypassing the review, the branch and the DoD of the story that owns the file. The rejection is now aD-NNline inproduction/decisions.md(or an ADR markedRejected), and a change to code is handed to/impact,/hotfixor a story. (WS-106)- A finding ID is never minted without the finding.
/impactverdicts citedARCH-004for a deviation that existed in no file: the next session greps the ID and finds nothing. The row is now written intoproduction/findings.mdin the same turn under the same write gate, and/story-donechecks that everyARCH-NNN/SEC-NNNnamed in a story card exists there. (WS-084) /dev-storyno longer waits for a CI run that was never queued. Withon: pull_requestand no push trigger, a bare push starts nothing; the skill reads the triggers and opens a draft PR (gh pr create --draft --fill) when the PR is what starts the checks, so they run while the review happens and/story-doneturns the draft ready. (WS-083)- A spike has a place to live:
tools/spike-<slug>/, gitignored and deleted when the story closes. The plugin called a spike a way to verify a decision three times over without saying where one goes, so it landed in the repository root; Phase 6 now stages the story's files by name — nevergit add -A— and names any unplanned??entry before committing. (WS-096) /code-reviewprints its routing. Reviewers were chosen by eye and the report showed only findings, so a diff touching three layers could be reviewed by one specialist with nothing to show it. Phase 2 prints the table (path → required reviewer → spawned yes/no) before the reviewers run, a skipped role costs a line saying why, and Phase 5 repeats the table with each verdict. (WS-091)- Work outside a story has a lane. CI, linters, dependencies and developer tooling fell through the pipeline entirely: not a story, not a document, so an urgent "fix the runner" either rode a story branch or landed straight on the default branch, and what was learned was recorded nowhere.
git-workflow.mdgains the chore/infra lane (chore/<slug>,ci(…)/chore(…)commits, a PR with/code-review --diff, experimental commits squashed before the merge, the outcome recorded as a finding or a backlog entry),/hotfix --chorefollows it without the release machinery, and the catalog has the step. (WS-093) - A subagent running out of turns is treated as normal, not as failure. For a mid-sized story in a typed language the default limit is reached routinely; the caller used to spawn a second agent on the same task, which starts from an empty head and redoes the work. Coordination rule 7 now describes the turn budget — the signs of a truncated result, resuming the same agent rather than replacing it, and handing out work in pieces a budget holds: one layer per call, a result carrying decisions and numbers instead of a copy of the diff.
log-agentmarks a secondSubagentStartfor an agent whoseStopnever came asresume=1, so the pairs stay countable. (WS-088) - CI minutes are a budget the studio now plans against. A generated pipeline ran seven parallel jobs on every push, each with its own checkout and install and each rounded up to a minute — seven minutes per push on a 2 000-minute monthly tier, which a project burns through mid-month and then starts merging around.
tooling-devops.md§ CI states the rules (one job per toolchain,paths:filters, e2e and security on pull requests to the default branch,concurrency: cancel-in-progress,runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }}so a self-hosted runner is a variable rather than an edit of every workflow) and/test-setupreports the estimated minutes per run and per month. (WS-015) production/session-state/active.mdhas a writer instead of improvised one-liners. Skills updated it withsed -iandpython3 -cinside double quotes, and nothing read the result back: a broken quote left the file unchanged and the session carried on believing it had saved its state.hooks/session-state.sh set|note|show|clearkeeps every field of the template in order, fills untouched ones with—, dates each note, keeps the last ten and archives the rest toproduction/session-state/archive/, and prints the file so a failed write is visible./dev-story,/story-doneand/impactcall it;post-edit-checkwarns when the file is missing one of its seven fields. (WS-095)- Measurements have a home:
docs/ops/measurements/YYYY-MM-DD-<topic>.mdwith the command, the environment, the raw output and the number, linked from whatever document quotes it. Coordination rule 12 says a number in a document cites an artefact, neversession-state, which is gitignored working memory the next session may not have — until nowNotes:was the only place measurements were written down, so a decision's evidence disappeared with the session./architecture-decisionand/impactname the file; after a compactionsession-startprints the state's decision fields and a bounded tail of notes instead of the whole file. (WS-094) - The startup banner stops telling configured projects that their stack is not configured.
session-start.shkept the whole-file grep forTO BE CONFIGUREDthat 0.10.2 replaced in the installer, so every session of a fully configured project opened with "Stack not configured — run /setup-stack or /adopt" because the template's header comment mentions the placeholder. That is not cosmetic:/helproutes a project with code and an unconfigured stack to/adopt full, which would rewrite the very file WS-082 was about. "Configured" now has one definition,hooks/prefs.sh, shared by the banner and the installer, and/helpnames the**Type**field instead of the placeholder. (WS-113) validate-commitreads the message of agit commit -Ftoo. For-Fthe hook set the message to an empty string, so the documents lane was invisible — a commit staging nothing but.claude/docs/coordination-rules.mdwith the subjectdocs: …was warned about "committing directly to 'master'" — and the Conventional Commits check was skipped altogether: blind rather than lenient.-Fis not an exotic corner:git commit -mruns command substitution on backticks, so every multi-line message with inline code, which is what a documents commit looks like, goes through it. The hook now reads the file, or the heredoc body of-F -, andvalidate-push(which always read the subject from git) stops disagreeing with it. (WS-112)install.sh --seed-onlyno longer writes.claude/.web-studio-version. 0.10.2 put the rule in the skill ("the stamp is written only in copy mode") and left the installer stamping unconditionally, so a normal plugin-mode/updatere-created the file a project had deliberately removed — naming a version whose agents and skills are not in the project, and pointing the next run at the copy-mode branch. Found on a real project in the first run after the release. (WS-111)- The published 0.10.2 changelog section carried a stray
# Changelog/## Unreleasedpair from a merge resolution, so the release read as unassembled. Cleaned, and the GitHub release notes rewritten from the corrected section. /updatereads the plugin listing per project.claude plugin list --jsoncovers every project on the machine, and the skill took the firstweb-studiorow it saw — with four projects side by side on three different versions, a project on 0.10.0 read a neighbour's 0.10.1 and reported itself up to date. Phase 1 now selects the row byprojectPath, Phase 2 takes the scope from that row, Phase 3 re-reads it the same way, and a listing with no row for this project is reported as "not installed here" instead of being substituted. The form without--jsonis documented as unusable for this: its blocks carry Version, Scope and Status and no project path. Spec case update 12. (WS-110)
v0.10.2
/updateno longer overwrites a configuredtechnical-preferences.md. In plugin mode the skill had no seeding command, so a session copieddocs/wholesale (cp -r <cache>/docs/* .claude/docs/) and replaced a filled stack — type, pinned versions, the rationale for rejected upgrades, the ADR log — with the template; the run then reported "project data unchanged". Seeding is now a single command,install.sh <project> --seed-only(docs/ and rules/ only, agents/skills/hooks come from the plugin update), and Phase 3 verifies the result withgit diff --quietbefore it may claim the file was untouched, restoring it when it was not. The installer's own guard was broken as well: it looked forTO BE CONFIGUREDanywhere in the file, while the template's header comment mentions the placeholder — so every configured file looked unconfigured and was overwritten in copy mode too. "Configured" now means the**Type**field is filled, the same predicateworkflow-catalog.yamluses, and the installer printstechnical-preferences.md: kept (configured). Withoutrsyncthe exclusion is real instead of a warning after the fact. Installer tests cover a fixture that keeps the header comment, the no-rsync path and--seed-only; spec case update 9. (WS-082)/updatePhase 2 updates the plugin in the scope where it is installed:claude plugin update web-studio --scope <scope>, taken from theclaude plugin list --jsonoutput Phase 1 already reads (-yin a non-interactive run). The bare command assumesuserscope and stops the run withPlugin "web-studio" is not installed at scope userin every project installed with--scope local. Spec case update 10. (WS-108)/updaterecognises a third mode: a copy-mode install left in place under the plugin. Plugin and copy mode were described as exclusive, so a project could run last month's skills from.claude/skills/for weeks — project copies shadow the plugin by name, and only theweb-studio:prefix reached the current version. Phase 1 now reportsHYBRID INSTALL, establishes the copy's real version by comparing it with the cached plugin versions instead of believing.claude/.web-studio-version, and lists the files that match no version — those are local edits and the project's own kit, everything else is removable without loss. Phase 3 offers the removal as its own gate, moves agent memory from<agent>toweb-studio-<agent>(appendingMEMORY.mdpointers), checks whether.claude/settings.jsonwires the copied hooks at all, and ends with "restart the session" — removing.claude/skillsempties the skill registry of the running session. The stamp is written only in copy mode: in plugin modeclaude plugin list --jsonis the source of truth, and a stamp naming a version whose files are not in the project is worse than none./helpprints one line when project copies shadow the plugin. Observed on a real project: a stamp reading 0.5.8 over files from 0.4.3, then 0.10.1 while onlydocs/andrules/had been reseeded. Spec case update 11. (WS-109)validate-commitno longer passes everything in silence when the staging happens in the same Bash call. The hook read the index, andgit add secrets.env && git commit -m "…"— the form the studio's own skills use — leaves the index empty at the moment the hook runs, so the secret-file block, the secret-string block, the lockfile and TODO warnings and the branch checks all did nothing. The hook now resolves what the command'sgit addsegments would stage (git add --dry-run) and reads the content from the working tree, so a staged-in-the-same-call.envor a leaked token is blocked exactly as before. (WS-101)validate-commitaccepts a story ID as the Conventional Commits scope: the class[a-z0-9/_-]rejected capitals, sofeat(S-019): …— the message/dev-storyitself generates — always warned, which is how a warning becomes noise. A capitalised type (Feat: …) is still flagged. (WS-102)validate-pushknows about the documents lane. Pipeline documents live on the default branch by design andvalidate-commitlets them through, but every push then warned "pushing directly to 'master'", including the push that carried exactly those documents. The push hook now checks the commits that would travel: all of themdocs:and touching documents only → no warning; anything else, or a range it cannot establish → the warning stands. The lane is defined once, inhooks/docs-lane.sh, so the two hooks cannot drift apart. (WS-103)log-agentstops calling built-in tooling a lost agent: a Stop event with no agent type is logged asbuiltinrather thanunknown, so Start/Stop pairs inagent-audit.logstay countable and an unpaired studio agent is visible again. (WS-089)/helpstops inventing commands. The hand-off named/dev-story complete S-007, which does not exist — a story is closed by/story-done— and a command shape invented by the studio's own orientation skill sends the user to a dead end with the studio's authority behind it. Every command in the answer now comes from the catalog or from a skill'sargument-hint, and the session-start context (merged branch, template placeholder, stale stack reference, an openGate:) is repeated as one line each instead of being left in the startup block the user already scrolled past. (WS-052)- Calibration is one sample with one name.
/sprint-planscaled the next sprint by a ratio over "every closed story" while/retrospectivecomputed its own over one sprint, and the plan's text claimed the two were the same number. Both now read the last six closed stories of the roadmap that carry~Nhand⏱ Nh; the retrospective additionally prints the sprint's own ratio, labelled, so the two are never confused. (WS-086) /sprint-planmoves the selected stories out of the Backlog block instead of copying them into the sprint: a story listed in both places made every<summary>count on the roadmap wrong from then on. The write step recalculates both counts and states them (Backlog: N → M, Sprint: 0 → K, overlap none). (WS-107)- A reviewer now gets something it can actually read.
/architecture-decisionrequired that "each reviewer receives the full draft text", which does not fit a Task prompt and was never once done: the draft goes to a session file and the reviewer receives its path plus the requirement to quote the ADR title and the two Decision lines back — the quote being the evidence of reading. A verdict with no quote is returned once, then reported as unread rather than counted. The same rule is incoordination-rules§ Subagents for every skill that reviews a draft. (WS-097) subagent_typeis always a studio agent. A three-round "final assessment" of an ADR draft was performed by a generic agent with a model override: no project rules, no agent memory, no role in the audit log. Wanting a stronger opinion changes the model of a roster agent, never the agent itself. (WS-104)- A
FAILthat was edited is reviewed again. A product spec went out asFAILwith four blocking factual errors, was rewritten "according to all eight comments" and recorded as done — checked by nobody. The same verifier now gets a short second pass, the document's verdict is the last review's, and a skipped re-review is written down asFAIL (edits unverified)instead of quietly becoming a pass. Same rule in/feature-spec. (WS-098) - An idea that leaves the backlog sideways is closed anyway.
/impactrun directly on a backlog entry leftI-003open for weeks while the work was already done, because only/backlog promotemarked entries./impactnow writes its verdict back into the entry under the same write gate, andbacklog reviewrecognises an entry whose text matches a spec, ADR or verdict written later. (WS-099) /story-doneproves the roadmap carries the numbers. For three closed stories in a row the⏱time and the🔗 PRlink travelled only as far as the chat answer: neither the sprint line nor the## Docsrow received them. The skill now re-reads both places after the edit and reports a mismatch as an open DoD item. (WS-092)go-engineerleaves a trace of the stack reference. "Readstack-reference/go.mdfirst" was followed in about half of the runs, and nothing could tell the difference. The result now starts withReference: stack-reference/go.md (updated: YYYY-MM-DD), carrying the date from the file that was opened; no line means the reference was not read. (WS-105)- Spec cases for all of the above: help, sprint-plan (2), retrospective, architecture-decision, product-spec, impact, story-done, and the go-engineer agent spec.
- The hooks see what the skills actually do.
consent-guard,secret-guard,impact-guardand the document-format part ofpost-edit-checkwere registered onWrite|Editonly, while the skills author documents withcat > file <<EOFandpython3 - <<PY— so the entire guard lane watched a tool nobody used. Measured on one real day in a project: 588 Bash calls, 15 Writes (all into a scratchpad), 0 Edits, 0 guard lines of any kind, andtouch .claude/.write-consentexecuted 176 times with nothing left to check it. The four hooks now also run onBash, andhooks/written-paths.shis the single place that answers "what would this command write" — redirections,tee,cp/mv/install,sed -i, and a heredoc body that writes a file itself (open('f','w'),writeFileSync). A configured.envwritten through a heredoc is blocked, a token pasted into a heredoc body is blocked, a roadmap written without a fresh consent marker warns, and a document written that way gets its format check. Code formatters stay on the Write/Edit path, where the call names one file and means it.coordination-rulesrule 7 now says documents are written with Write/Edit: the Bash lane is a safety net for literal paths, not a licence — a path built from a variable is still invisible. Nine hook tests, six...