Conversation
…amework gaps item 5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nerated plan Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es and build order Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… where it writes Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…only regular files
gitPinTree resolves the pin once to its full sha (git's own error on failure) and lists it with
one `git ls-tree -r -t --full-tree`, answering kind, children and files from memory: a git
failure is reported at construction instead of reading as absence, paths are top-level relative
whatever directory the tree was opened from, and ~one spawn replaces one per query. kind names a
symlink a link and a submodule a submodule, as children always did; read throws for anything
but a regular blob; files("") is the root and a directory never matches a sibling it prefixes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ecs init cannot read peerDependencies join both closures: pnpm's workspace graph follows them, and a package imports its workspace peers, so the build closure builds them. expandGlob refuses extglob characters, never lets * match a dot-directory, node_modules or bower_components, and refuses by name a symlink or submodule where a package directory could be. A workspace alias (workspace:@m/util@*), a workspace: path, and a link:/file: spec naming a workspace directory are refused by name instead of misreported or silently dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h a learnable promotion set Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d outDir
buildConfig now reads path options through the whole extends chain, config
packages included (every external file is recorded in externalExtends),
resolving each against the file that sets it. It computes tsc -b's default
build-info path by TypeScript's getTsBuildInfoEmitOutputFilePath rule
(confirmed against TS 7.0.2) and refuses it, a declarationDir or an outFile
outside the outDir, an outDir overlapping src/ or test/, ${...} substitution,
non-string path options and extends cycles; `extends: "./base"` falls back to
base.json as TypeScript does; the build config's references are returned.
buildScriptTsconfig reads a compile only as exactly `tsc -b <one config>`
followed by `&&` or the end, refuses cd anywhere in the chain, a tsc that is
not -b, a non-package-relative config, and unbalanced quotes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
parseVitestCommand accepts only run (first), --run, --no-cache, --passWithNoTests, --config and --exclude with values, and positional files; every other flag (-t, -c, --shard, ...) is refused by name, replacing the UNREAD list. --config values must be package-relative literals in both the script and the command, and withExcludes refuses any exclude that is not a literal, package-relative file path (vitest reads excludes as globs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…move The promotion and relink loops sit inside the RUN's top-level && list, where the shell ignores set -e, so a failed rm/mkdir/mv inside a loop was ignored and the build passed with the package gone. Each chain in the loops now ends in `|| exit 1`; the shell tests run under dash (the image's /bin/sh) where the host has it. Also: the nested-typescript check ignores a symlink; promoting a name the image relinks fails the build by name; renderDockerfile and withExpectedPromoted refuse a repeated promotion name; CAPTURED and the links are sorted by directory; a name starts with a letter or digit; and promotionMismatch reads the promoted marker only from the BuildKit step that printed the expected one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ifests at a pin Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g what was decided Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oes and names what the capture omits - Walk up from the package to the repository root for the vitest config, per directory then names (vitest's order); a root config is captured and kept immutable with a note, one between the package and the root is refused. - Note the target package's uncaptured top-level files, and every relative path the vitest config names that the capture does not hold (a workspace package once; `"../tooling"` and `"../../x"` read correctly). - Refuse a capture or runnerConfig entry that overlaps a snapshotIgnore prefix. - Read TypeScript from the package, then the root's devDependencies and dependencies; refuse a version whose major cannot be read. - Note a carried scope file the capture does not hold; suggest --with-dev-builds only for packages the flag would capture; drop a carried capture link or submodule with a note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gets, differences named Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tes proposals safely A target.json that is not JSON or fails the schema is now refused, naming the parse error, instead of skipping the ownership check and being replaced with defaults (which --write would have used to destroy decided resources, excludes and drafting notes). - writeProposal re-reads each file and refuses one changed since the proposal, refuses a symlinked target directory, and writes through a temporary file renamed into place - notes say "read from <dir>" (counts precede deriveTarget's drops), name the real catalog directory, omit empty draftingNotes/EXPECTED_PROMOTED, and say when only a Dockerfile's EXPECTED_PROMOTED is carried - a carried test scope replaces the "no test is excluded" note - initTarget validates the pin itself; a missing controller Biome is named - a relative --targets-dir resolves against INIT_CWD, else the cwd - the script prints a refusal as one `target:init:` line and exits 1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… build through the registry The devkit lane runs in test:sandbox; the cli case is opt-in under FACTORY_TEST_CLI_TARGET=1 (test:sandbox:cli) and learns the hand-written promotion set from its first build before building. The devkit lane reads the promotion step's printed set from its own BuildKit step, and accepts a cached step, which prints nothing (a second build of the same recipe on one host). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed note and the plan's The plan records, under Tasks 1-10, what review changed as each landed, and adds the shipped cli Dockerfile's ignored-failure loops (not changed, D14) to its follow-ups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed targets, setup files - --builders reads only the root's TypeScript, the tsc the image builds with (the Dockerfile shims nested copies onto it): packages/core, which nests npm:@typescript/typescript6@6.0.2, was refused and now generates. - A target whose resources are target:init's placeholders is never offered to the drafter, and a draft or task naming it is refused (unmeasuredProblem: "resources are placeholders: run target:measure"). recipeProblem is unchanged, so such a target still builds for measuring. - A vitest setupFiles/globalSetup entry the capture omits is refused by name: every test would fail. - Refusals no longer repeat target:init under the script's prefix, and start lowercase. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion and scope require The README and target-init.ts header no longer describe target:measure as available: a generated target must not be committed until measured. The README says a re-generation does not carry --with-dev-builds and that the scope needs a root packageManager pnpm@x.y.z and a build of exactly one tsc -b <file>. The plan records Task 11's final-review fixes and the proposalProblem/recipeProblem shared-core follow-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This branch was successfully deployed
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.
Framework-gaps item 5, PR 1 of 2. Plan:
docs/superpowers/plans/2026-09-25-targets-generated-from-a-package.md, Tasks 1-11. It includes the plan itself and its decisions D1-D16, which Brian accepted.What it adds
pnpm --filter @b4-example/software-factory-controller target:init <package> [--pin <sha>] [--id <id>] [--with-dev-builds] [--targets-dir <dir>] [--write]It generates
targets/<id>/target.jsonand itsDockerfilefrom a pnpm package at a pin. It reads only the git object store, never the working tree, and the output is deterministic. It prints a unified diff with notes, and writes only with--write. A target is an oracle input, so a person reviews that diff before committing it.--filter <pkg>.... Config packages are captured whole.tsc -bover the build closure in dependency order. It adds--builders 1when the root's TypeScript is 7 or later.extendschain is read. Any build output outside the recordedoutDiris refused, including TypeScript's default build-info location,declarationDirandoutFile.cliDockerfile generalised. It has fail-fast loops and a reviewed promotion tripwire.imageAssertResolves,capture.includeandrunnerConfigentries are carried as supersets. An unchanged target regenerates to an empty diff.target:measureis a follow-up and that a generated target should not be committed until measured.Proof
initreproduces the shippeddevkitandclitargets at their pins. The test compares whole objects with each named difference applied. A mutation to the build order or the resources makes it fail.devkitDockerfile builds insandbox-dockerwith the empty promotion set.cliby hand, before merge. This case is opt-in. Its first build learned exactly@hono/node-server commander hono typescript, the hand-written target's set, and its second build succeeded. The timing was on a warm cache, so treat the first CI run as the cold-build figure.config-biomeandconfig-typescripthave no test script,inspectorbuilds withnext build, andcreate-b4-app'sglobalSetupreads outside the capture, so every test would fail. Every generated target loads through the catalog and passestarget:prepare's CAPTURED check.Review
Each task had a spec review and a quality review, and the branch then had a final review. Every fix is recorded in the plan's per-task "As landed" notes.
The follow-ups are listed in the plan. One matters now: the shipped
targets/cli/Dockerfilehas the ignored-failure loop bug the template fixes, and it stays unchanged per D14.Verification (local, Node 24)
test:sandbox: 12 files passed and 1 skipped (the opt-inclicase); 31 tests passed and 5 skipped, in 219 s on a warm cache.pnpm lint,node scripts/check-docs.mjsand the factory typechecks pass.CI's
sandbox-dockerjob took 11 minutes 23 seconds of its 30-minute budget on this PR, building from a cold cache.Examples only, so there is no changeset.
🤖 Generated with Claude Code