E2E contract-corpus pilot — convert scaffold's tests to a txtar corpus (Qwen3.6-27B) - #25
Closed
A wants to merge 57 commits into
Closed
E2E contract-corpus pilot — convert scaffold's tests to a txtar corpus (Qwen3.6-27B)#25A wants to merge 57 commits into
A wants to merge 57 commits into
Conversation
…receipt and per-file write semantics
…nts the same diff receipt and types its scalars
…from core.groom_playbook.scaffold
…d plan frontmatter partials
…nd the shared stdout contract
…e accepted groom snapshot baseline
`type:` rendered raw, so a missing `--set type=` produced a YAML null with no
error and the run continued with a plan carrying no task type. It now goes
through the same `default('') | tojson` seam `title:` already used.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t suites Scalar typing, frontmatter-update diff receipts and scaffold stdout receipts were one function per case. Each block is now a single parametrized test over an input/expected table, so a new case is a row. Assertions got stronger, not weaker: scalar rows check the reloaded value's exact type, and the append and removal rows assert the full `--- / +++ / @@` header. `import yaml as pyyaml` moves to module level; the eight function-local copies are gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The review artifact, the plan's `code_reviews:` back-link, and two lessons the run produced — top-level imports over lazy ones, and code-only review scope. The back-link is hand-corrected: `frontmatter-update --append` into an empty inline `[]` list emitted the old `[]` as the first element. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… list The exit hook parses plan frontmatter with a regex — it is stdlib-only by design — and its inline-value branch knew only `null` and `~`. A plan carrying `code_reviews: []` had that `[]` inserted as the list's first element; a populated `[a]` would have landed as one element literally named `[a]`. `_inline_items()` now reads the flow form: `[]` yields nothing, `[a, b]` yields its elements, `null`/`~` mean unset, anything else is a lone scalar. The hook has no tests, so the new module drives it as a subprocess the way `playbook-transition` does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`0014` broadens from lazy imports alone to the project's code style practices, picking up the parametrized-tests preference the same review raised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tree keys go through the same Jinja env and --set globals as seed bodies, with the filesystem-safety check re-run on the rendered name so an unsafe render fails before any write. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
core.groom_playbook.milestone_scaffold seeds one file per milestone with a fixed identity frontmatter and the three required headings; the shared core.plans.milestones key describes where they live and how index.md's table projects them. Ids are quoted so zero-padded values do not read back as octal ints and scramble sorting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each template's Milestones section is now a generated table and a new Milestone files section giving the per-file body shape for that surface; checklists check the plan directory instead of a single file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
draft-plan scaffolds one file per milestone and writes its body in place, then generates index.md's table with booping query; present and cross-review read the same source. sp moves to develop's refresh script. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A milestone state machine addressed per instance writes status via
playbook-transition only; each edge runs refresh-milestone-table, which
rewrites index.md's Milestones table and re-sums the plan's sp from the
milestone files. develop-loop moves into a one-step milestones subgraph,
which is what makes the {instance} artifact legal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
develop-loop composes one literal briefing block naming the milestone files as contract and index.md as context; provision groups from a query over milestone frontmatter and verify reads the milestone files. The worker contract states which path binds it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Uncommitted output of the docs-refresh run, captured before the milestone-file sprint rewrites the same surfaces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
retro, code-review and the lesson-check partial read index.md plus the milestone files; the vault, develop and groom pages, README and CLAUDE.md describe the plan directory, the milestone file contract and the generated table. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each hermetic fixture plan gains a milestones/ directory with two files and an index table generated by refresh-milestone-table; cache-warmup keeps none so the missing-sp branch stays covered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A milestone becomes a directory whose file is named after it, so a runner-written feedback.md can sit beside the contract and a bare wikilink still resolves. The worker now runs its own Verify and commits; the runner validates the diff, keeps the bookkeeping and records failed attempts in feedback.md, routing fixes to a fresh fallback_agent. playbook-state strips the matched segment cleanly so instances key the same in filename and directory positions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…via local pi worker wire pi-developer agent into develop via vault config
Plan: scaffold tests become a txtar contract corpus under booping-python/e2e/ with a standalone runner; superseded unit tests deleted. Drops the retired reshape lesson pair (0008/0009). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y and e2e/README.md
…e, just e2e recipe, CI wiring
…ar cases covering destination semantics, seed rendering, error paths, real config trees, multi-command chain, vault logging, and declaration-order preservation
Remove tests/commands/scaffold_test.py and tests/context/scaffold_test.py (superseded by e2e corpus). Clean stale scaffold_test references from two e2e case descriptions. Update CLAUDE.md: add just e2e to Commands, note e2e/ in Layout.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
booping scaffold's pytest suite described the Python implementation —Nodeobject graphs, exception fields, exact message text — so it broke on no-op refactors and would not survive a rewrite of the CLI in another language.This PR replaces it with a contract corpus: one txtar file per case carrying its fixture tree, the invocation, and everything asserted about the result (stdout, stderr, exit code, files left behind), executed by a standalone runner that shells
bin/boopingas a black box.scaffoldis the pilot; the harness and format are what later plans convert the remaining subcommands onto.booping-python/e2e/_txtar.py— vendored txtar parse/serialize (Go semantics, stdlib only).README.md— the format contract, written as the document a future non-Python runner is built from: section vocabulary, sandbox and env bindings, multi-linecmd,{CWD}/{HOME}/{XDG}tokens and the[..]wildcard, exit codes, the--updateloop.run.py— freshhome//xdg//cwd/sandbox per case, subprocess invocation, labeled unified diffs,{N} passed, {M} failed, exits 0 pass / 1 fail / 2 malformed.Wired as
just e2e [pattern…], added tojust ciand the CI python job; basedpyright now includese2e.Model & Host — implementer worker
Milestones were coded by a local Qwen worker (
llama-developeragent) rather than a hosted Claude model:unsloth/Qwen3.6-27B-MTP-GGUF--tensor-split 26,14(not 24,16)Qwen3.6-27BKnown issue — not fixed in this PR
Verify surfaced that
--updateis not a byte-level no-op on an already-green corpus: it silently downgradesreal-logging.txtar's[..]timestamp wildcard to a literal value and drops a trailing blank line inreal-playbook-authoring-scaffold.txtar. Contradicts M02's DoD ("--updateon an already-green corpus is a byte-level no-op"). Opening as draft pending a fix milestone.Sprint metrics
Development only — the grooming session is excluded, since the plan is shared with #24. The Qwen row is the four local milestone sessions the
llama-developerwrappers drove; the two runs that died on a gateway 500 are excluded, they produced no work.IN (uncached)is only the input that escaped prompt caching — the real input volume is the cached column.llama-developerwrappers — 4 milestones23.44M Claude tokens + 14.99M Qwen tokens = 38.42M total. Story points: 18 across 4 milestones.
The Qwen row's uncached IN is three orders of magnitude above the Claude rows because llama-swap reports a reused prefix as a cache read but bills each turn's new content as plain input — it has no cache-write bucket at all (
cache_creationis 0 across all four sessions).🤖 Generated with Claude Code