Skip to content

Releases

v4.2.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 16:32

Blueprint 4.2 reports executable gates, structural conformance, source reach, and semantic review as independent evidence. Passing machine checks does not establish correct ownership, presentation placement, or complete adoption.

Ordinary module roots now have one canonical index entry; direct root siblings are private. Layer-only modules and reserved app do not need an empty root barrel. Upgrading from an earlier release activates review-module-root-unit: review public exports, external imports, and presentation placement, then explicitly complete the operation. Blueprint does not choose the application's public API or move presentation code automatically. Existing module-first projects may need source and test changes to satisfy this contract.

Executable mustNot declarations, explicit external consumers, and direct module-dependency reconciliation add opt-in controls without turning review-only strings into machine gates. React and Vue presentation guidance follows responsibility, including feature-private and single-consumer presenters. Source discovery and assessment retain explicit evidence limits, and large piped reports and distinct Doctor prohibition probes preserve their complete results.

Minor Changes

  • 78f4c1b: Report adoption as independent executable gates, structural conformance, source reach, and semantic review. Doctor and Inspect expose the shared assessment without treating green checks as proof of ownership or topology correctness. Preserve baseline debt, non-blocking violations, unavailable evidence, and bounded workflow scopes.

    Add Doctor gateVerdict and deprecate verdict as its gate-only compatibility alias for 4.2; preserve existing exit semantics. Update generated Agent guidance, CLI help, and English and Traditional Chinese documentation to distinguish verified evidence from remaining review.

  • bdadd20: Add opt-in executable mustNot constraints for layer capabilities, package references, globals, and syntactic calls. ESLint enforces the declared checks while Rules, Inspect, Doctor, and generated guidance distinguish detector availability, file reach, wiring, and warning observations. Existing prose entries and presets remain review-only and introduce no new source failures.

  • 1f4db85: Add opt-in architecture.consumerFiles for explicit external consumers in module-first projects. Declared files outside sourceRoot may consume governed source without an uncovered-source finding; governed source depending back on them remains an error. Omitted and empty declarations preserve existing behavior. No preset infers consumers from tool names.

  • e1e05ac: Enforce one index entry for ordinary module roots with governed source. Direct siblings are private and Inspect reports proven unreachable root members. Layer-only modules and reserved app need no root barrel. Existing adopters must manually review entries, exports, imports, and presentation placement; upgrade never chooses their public API. Reverse topology mapping retains original per-member LF identities and custom entries, and stops on unproved provenance.

  • 137ca74: Expose source-observed direct module dependencies, deterministic evidence, reconciliation, and importer-scoped uncertainty in deps and inspect. Add the opt-in Inspect moduleDependencySync gate while preserving transitive import permission. Existing configurations and presets gain observability without new synchronization failures until owners enable the gate; no config migration or automatic dependsOn edits are required.

  • 3c2c26c: Bound module-first outside-source discovery to application-local Git visibility, explicit consumer search roots, and governed dependencies. Source-only commands no longer scan outside their source root. Inspect, Doctor, and transformation verification share scope evidence and refuse complete conformance when required import analysis is incomplete, even under a baseline. Non-Git applications retain an explicit complete filesystem fallback.

Patch Changes

  • 1fe33e4: Keep large CLI reports complete when piped to another program, and preserve each executable-prohibition probe in Doctor assessments without conflicting evidence identities.
  • 445c687: Classify React and Vue components by presentation responsibility, including feature-private and single-consumer presenters. Clarify local UI state and shared/public promotion in module-first authoring, both topology transformations, and the architecture guides.

v4.1.0

Choose a tag to compare

@github-actions github-actions released this 22 Sep 17:03

Blueprint 4.1 separates proven-empty greenfield adoption from brownfield adoption. A proven-empty React or Vue application, Next.js aside, can enter layer-first or module-first directly from the framework preset's complete canonical governance, and module-first opens with modules: [] instead of an invented domain model. Existing projects keep the source-sensitive adoption rules: measured brownfield source can enter authoring, while the existing layer-first preset paths remain available where their current conditions apply.

Adoption is no longer one-way. blueprint upgrade and blueprint remove are first-class lifecycle operations backed by explicit evidence: upgrade resolves the repository-wide lifecycle plan before it starts the upgrade, while remove computes the complete de-adoption plan before applying it. Both fail closed when required facts cannot be proven instead of guessing.

Deterministic migration still does everything Blueprint can prove, and the semantic judgment that remains is an explicit, recorded step for a person or coding Agent. An upgrade completes only when blueprint inspect --baseline and blueprint doctor pass in every adopted application, never because lifecycle state says it should.

Minor Changes

  • 374582e: Distinguish greenfield from brownfield adoption. A proven-empty React or Vue application now starts from the framework preset's complete canonical governance under either topology: init --topology module-first scaffolds reactPreset / vuePreset with modules: [] instead of an authoring playbook, and invents no domain module; a Next.js application is not part of this runway and still receives the authoring playbook under module-first. Existing source keeps the conservative authoring path that translates house thresholds and ratchets pre-existing debt; as before, layer-first source below the 10-file brownfield threshold, or initialized with --preset, scaffolds the preset directly.

    architecture.modules: [] is now a valid module-first runway. Topology follows the presence of modules across validation, resolution, lint emission, inspect, deps, doctor, impact, handbooks, Agent contracts, and guarded transformation. reactPreset() and vuePreset() accept a modules option that projects the same canonical governance onto module-first, with each module root at the container position and route composition in the reserved app module.

    Generated module-first handbooks and Agent contracts carry a module growth protocol for future requirements: a temporary layer-first projection for reasoning only, container/use-case seeds, merge and split decisions, then Module → Layer → Unit with dependsOn derived from real imports. The documentation explains greenfield and brownfield posture and includes a measurement-first prompt for tightening a brownfield config.

    The @kekkai/blueprint/operational-contract renderers stay source-compatible. renderFreshScaffoldNote also accepts { files, threshold, topology }, renderAuthoringFlowBanner also accepts forcedExit, and renderVerdict accepts an optional next. The positional renderFreshScaffoldNote(files, threshold) form and forcedBelowThreshold still work and are deprecated.

  • 51ade58: Add a first-class post-adoption lifecycle: blueprint upgrade and blueprint remove.

    npx @kekkai/blueprint@latest upgrade uses the running package as the only target authority. It reads the repository's lifecycle checkpoint from the new .blueprint-lifecycle.json, or proves an exact source from the installed package on a repository adopted before lifecycle state existed, and resolves the structured upgrade operations of every release across the whole source → target interval before anything runs: canceled and superseded operations are removed, the rest are ordered by their requirements. An overlapping legacy config shape alone never proves an exact release. It then records the pending upgrade, moves @kekkai/blueprint to the target through the detected package manager, continues with that installed copy, runs deterministic migrations by reconciling every adopted application through blueprint init, writes one resolved blueprint-upgrade.md playbook when semantic work remains, and records the new lifecycle only after blueprint inspect --baseline and blueprint doctor pass in every adopted application. --dry-run shows the complete plan without mutation, --complete <operation-id> records verified semantic work, and a pending upgrade resumes without repeating completed operations.

    Starting an upgrade requires a Git worktree without uncommitted changes, and no upgrade runs while an authoring or topology-transformation workflow is unfinished. Downgrades and sources before 3.2.0 are refused, and so is an application that resolves a newer package than the running release, or a mix of package versions. When the record already matches the target but an application resolves an older package or none, upgrade reinstalls the target instead of reporting it current: the lifecycle reads current only when the record and every install agree. Lifecycle state that is unreadable or points outside the repository stops every command that needs it, and so does state missing where the installed release always writes it, once it entered Git history or when that history cannot be read; Blueprint never rebuilds that history, and an upgrade that finds the state changed underneath it leaves the checkpoint where it was. Missing state that no commit reachable from any ref ever contained is not treated as lost, because the history Git can still show does not prove a lifecycle existed. The repository is dated only from an exact installed pre-lifecycle package; if a newer installed package sits beside the overlapping legacy 3.x shape, Blueprint refuses to guess and directs the adopter to establish a known 3.2 checkpoint first. Commit .blueprint-lifecycle.json so a later loss has history to restore from. A package install that stops part-way across several applications resumes on the next run without repeating the installs that landed, while any install mix the recorded upgrade could not have left is refused. init records ownership as soon as it writes but establishes the lifecycle checkpoint only when adoption completes, so a failed run, an install deferred by --no-install, or an authoring handoff leaves it unestablished until the init run that finishes. Every lifecycle state write replaces the file atomically, and completed operations stay readable history after a later release retires them from its supported window, where a newer operation can still supersede them. Re-planning a pending upgrade for a newer release keeps the operations it already completed, so the next run resumes the new plan without repeating them.

    npx blueprint remove plans the complete de-adoption before changing anything, and --dry-run stops at that plan. init now records which files Blueprint generated or created, which managed sections it owns, and the exact shared-file edits it made, so removal deletes only proven Blueprint artifacts, strips only managed sections, reverses only recorded edits that are still intact, deletes a Blueprint-owned file such as a migrated blueprint.config.mjs instead of restoring it from its recorded edits, keeps alias wiring that application source still imports, and uninstalls @kekkai/blueprint last, together with the ESLint packages Blueprint recorded installing that nothing else still uses. A package script that holds neither what Blueprint wrote nor what it replaced, a recorded edit that now appears more than once or records only removed text, broken managed-section markers, and remaining Blueprint wiring stop the removal before any change, each reported once however Git and Node spell the repository root; a recorded edit whose inserted text is gone, or a script back at its original value, counts as already reversed, and a mention of blueprint.config.mjs or @kekkai/blueprint inside a tool config's comment is not wiring; a shared Agent document is deleted after losing its managed section only when the lifecycle records prove Blueprint created it, and otherwise survives, even empty, with a note to review it; a file Blueprint created and later edited is reversed edit-first and then deleted when it matches what Blueprint created; a shared package stays installed while sibling applications remain adopted; removing part of a repository is refused while an upgrade is pending; repositories adopted before lifecycle records exist lose only provable artifacts and get a report of what could not be proven.

    Generated Agent contracts, the packaged agent contract, CLI help, and the English and Traditional Chinese documentation now route "upgrade Blueprint" and "remove Blueprint" requests to these commands.

Patch Changes

  • 8132be4: blueprint doctor now proves the Vite alias that init writes. The bundler-runtime and test-runner alias checks read '<alias>': fileURLToPath(new URL('<dir>', import.meta.url)) and path.resolve(__dirname, '<dir>') / resolve(__dirname, '<dir>'), resolving <dir> from the configuration file's own folder. When Vitest is installed and no vitest.config.* exists, the test-runner check reads the Vite config that Vitest uses instead of reporting the alias unverified. A check that still cannot prove an alias names the file it read, or says that no configuration file was found, and lists the alias forms it can prove.

  • 45a5d55: defineBlueprint() and config loading now enforce two requirements the documentation and types already stated: framework must be 'vue', 'react', or 'auto', and every layer needs a non-empty does. A hand-written JavaScript config that omitted either, or set framework to another value, used to load; it now fails with an error naming the field to fix.

    blueprint init --help no longer tells a multi-application workspace to pass `--sour...

Read more

v4.0.0

Choose a tag to compare

@github-actions github-actions released this 14 Sep 16:05

Blueprint 4.0 promotes architecture topology from an implicit folder convention to an explicit repository architecture contract. Layer-first and module-first are both supported models, and module-first preserves the same layer discipline inside ordinary modules while keeping the reserved app surface focused on router composition.

Adoption, supported legacy migration, and topology transformation now start from repository facts Blueprint can prove. When semantic ownership or a safe transformation cannot be inferred, Blueprint fails closed and hands the judgment to a person or coding Agent instead of guessing.

Resolved architecture facts, mechanical enforcement, human guidance, and Agent operational guidance now have distinct authorities. They share the same resolved architecture without claiming that every product or public-information surface is unconditionally compiled from one config.

Major Changes

  • 04a40ea: Require architecture.alias for cross-layer and cross-module imports, and apply the same architecture checks to statically resolvable dynamic imports. Inspect and deps now disclose runtime-dependent targets they cannot prove.

  • 9e7af73: Add optional module-first topology through architecture.modules, repeating the declared layers beneath every module while preserving layer-first projects.

    Move unit layout and entry configuration directly onto each layer, rename the flat layout to file, and retire architecture.module, layers[].module, and module.private with targeted migration errors.

    Update lint, inspect, dependency reports, generated docs, and agent contracts to use Module → Layer → Unit identities consistently.

    Declare direct module dependencies with optional architecture.modules[].dependsOn. Blueprint rejects invalid module graphs, resolves transitive reachability, and requires governed imports to pass both the module DAG and the existing inner layer flow.

  • 0f29981: Require an explicit topology for first adoption, inherit one topology across repository applications, and coordinate configured topology changes repository-wide. Source-tree survey evidence no longer classifies unmanaged projects, and presets are limited to layer-first adoption.

  • dd085ce: Add init --topology layer-first|module-first and require an explicit choice for first repository adoption. Existing configurations remain authoritative, module-first initialization enters authoring, and unsupported or unsafe topology paths abort before writing.

    Align dependency reports with the canonical architecture resolver so governed recursive app/** routes are never also reported as outside the architecture.

  • 22c3987: Let init --topology module-first guide a configured layer-first React, Vue, or Next.js App Router application through a Git-preflight-guarded Agent transformation. The generated playbook carries neutral container/page dependency evidence, requires semantic ownership decisions and tracked git mv operations, derives the final module DAG after movement, and reviews architecture debt before baseline regeneration. Next.js Pages Router continues to abort before mutation.

  • c4d7c74: Reserve an optional module-first app module for framework-independent router composition. Nested app source now uses container semantics instead of repeating the shared layer contract.

Minor Changes

  • 3c40eef: Centralize Blueprint's CLI, authoring, transformation, Agent, handbook, diagnostics, and field
    guidance under one runtime-backed operational contract. Add checked-in artifact verification and
    require field adoption to select an explicit topology.
  • 0fb37ff: Let init --topology layer-first guide a configured module-first React, Vue, or Next.js App Router application back to layer-first. The Git-guarded playbook reports complete structural destinations, collisions, module/import evidence, router constraints, and baseline review steps while leaving semantic placement, tracked moves, import rewrites, and the synchronized cutover to the Agent.
  • 78d2b9e: Resolve nested application, workspace toolchain, and containing Git repository context consistently, require the normal lint entrypoint to reach ESLint before doctor reports complete adoption, and share structural pattern normalization between emission and doctor verification.
  • b962241: Let blueprint survey expose repeated direct-child folder names across sibling folder instances in text and JSON, including the measured parent and participation counts, without classifying or recommending an architecture.

Patch Changes

  • 53945b5: Isolate release-time consumer verification from npm publisher authentication so packed Yarn 1 checks run without inheriting the publishing npmrc.

  • 76bbc3a: Refresh Blueprint's public documentation with its new cyan-to-blue brand system and replace documentation tables with mobile-readable lists.

  • 076decc: Keep Codex-only authoring and topology transformations free of Claude-specific command files. The neutral authoring playbook now follows the selected agent or configured emit.agents policy across fresh, single-application, and repository-wide flows.

  • 35963d9: Make blueprint doctor verify the safely reachable project-local ESLint leg live before reporting adoption complete.

  • dcbed49: Keep architecture.testFiles behavior and guidance aligned across lint, inspect, dependency reports, CLI help, generated Agent contracts, and authoring playbooks.

  • 1c1d53c: Make adoption evidence fail closed: preserve ancestor ESLint policies for nested applications, scope emitted rules to the application, reject nested folder-unit relative escapes and conflicting alias identities, and report lint wiring and alias consumers independently. Impact reports unsupported ESLint versions as unavailable and import parse failures as partial, instead of implying complete verification.

    Retain layer-first to module-first transformation origins in application-scoped Git authority independently of source, configuration, and decision-file edits. Completion requires reserved app routing, consumed container seeds, one-to-one member mappings with Git content identity, matching requested topology, and final architecture errors resolved without baseline suppression; ordinary hand-authored module-first adoption remains separate.

    Repair packed public declarations and verify external npm and Yarn 1 consumers, including pinned internal Vue and TypeScript parsers with Node 18-compatible dependency trees. Normalize Git inventory line endings and filesystem aliases across platforms.

  • 947647c: Cover Vue JSX and TSX files in architecture nets, escape leading-hash aliases in emitted ESLint import patterns, and deduplicate overlapping alias restrictions. Clarify coverage output when files sit outside declared architecture nets.

  • eb51c2d: Parse JSX and TSX syntax inside Vue SFC script blocks when analyzing statically resolvable dynamic imports.

v3.2.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 11:13

This release improves adoption of existing projects with root-level or nested application
sources and makes generated rules and guidance follow the configured source scope more
consistently. It also closes enforcement gaps for bare layer imports and literal $
characters, and prevents malformed brace patterns from hanging commands.

The minor API addition is optional stack facts for document emitters; existing calls remain
compatible. Architecture ownership and the application scope of a multi-app workspace remain
owner decisions. Upgrading can expose previously missed imports; review the upgrade notes
below and regenerate contracts to receive the corrected enforcement guidance.

Minor Changes

  • 72b684b: Add the exported StackFacts type and optional stack facts to emitHandbook and
    emitAgentFiles. Existing calls remain compatible.

    blueprint init now supplies the detected TypeScript capability, so generated handbooks and
    agent contracts no longer claim that explicitAny is enforced in JavaScript-only projects.
    TypeScript projects keep their existing output.

Patch Changes

  • 8479e54: Preserve $ sequences literally when generating layer globs and updating an existing
    package.json lint script. Configurations such as price$$tag now govern the intended
    folder, and blueprint init no longer risks corrupting valid scripts containing $.

    After upgrading, projects with $ in a layer name may surface architecture findings that
    were previously missed. If an earlier scaffold run modified a lint script containing $,
    check that script once because upgrading cannot repair an already-written manifest.

  • c4908ad: Enforce architecture bans against a layer's bare entry import, such as ~app/pages, in
    addition to imports below that entry. This closes gaps in dependency-flow, same-layer, and
    selfOnly re-export enforcement across every configured alias.

    Upgrading may reveal imports that inspect already considered invalid but ESLint previously
    missed. Projects that manually merged Blueprint's generated no-restricted-imports groups
    should refresh those copied groups and run blueprint doctor.

  • bad63ea: Prevent commands from hanging when a configured glob or .gitignore pattern contains an
    unmatched {; it is now treated as a literal brace.

    inspect, rules, doctor, and deps now also identify testFiles entries that match no
    files, while doctor reports ineffective layerFilesIgnore entries. These diagnostics are
    informational and do not change exit codes. JSON output gains the corresponding conditional
    testExemption details.

  • 70b463a: Discover root-level TypeScript applications and aliases declared in referenced configs during
    brownfield survey, preserve conventional src/ scopes, require an explicit application scope
    for multi-app workspaces, resolve aliases that target individual architecture layers, and use a
    nested application's own TypeScript and Vite configuration when architecture.sourceRoot
    scopes Blueprint below the repository root. Keep authoring output and CLI help consistent with
    the required application-scope decision.

  • 717d2ac: Align inspect with the generated ESLint scope: files matched by
    architecture.layerFilesIgnore no longer produce lint-backed findings or count toward lint
    coverage. Inspect-only checks continue to include them, and coverage output now identifies
    deliberately ignored layer files.

  • be37bfa: Add an informational owns-not-installed finding when a layer declares ownership of a
    package that is not installed. It names the package and layer without changing exit codes or
    baseline contents.

  • e4d1def: Describe blueprint inspect as scanning the configured source root, with src/ as the
    default, and export DoctorVerdict so consumers can name the public doctor result type.

  • 89905a2: Honor architecture.sourceRoot consistently in init scaffolding, generated agent guidance,
    dependency targets, relative-import and typedef-only enforcement, and inspect messages.
    Projects using a source directory other than src now receive paths and rules for the
    configured root.

  • 32be831: Include the configured architecture.testFiles patterns in full generated agent contracts,
    so agents know that test-support files may be exempt from normal placement rules. Compact
    CLAUDE.md and AGENTS.md contracts are unchanged.

  • 2096a9d: Make full generated agent contracts describe enforcement accurately: unavailable
    testFilename and deepWatch gates are no longer called hard lint gates, while cycles is
    identified as enforced by blueprint inspect --baseline rather than ESLint. Regenerate the
    contract to receive the corrected wording.

v3.1.0

Choose a tag to compare

@github-actions github-actions released this 07 Aug 12:22

What this release moved, and what it did not. Worth stating plainly, because most of the
entries below act on one axis and it is not the obvious one.

Adoption did not get more likely to succeed, because it was already succeeding. Every
entry here came out of live adoption runs — a real agent CLI taking a real repo through
init → inspect → impact → doctor — and across every one of those runs doctor
finished green. It finished green before these fixes too. Nothing here rescues a failing
adoption.

What moved is whether an adopting agent reaches a true conclusion and writes it into its
report.
That is the axis: output an agent reads and acts on, where a confident sentence the
tool could not back turns into a claim in a handoff nobody re-checks. One measured example —
the build step used to assert that tsc -b type-checks your vite config edit; an agent
disproved it by injecting a type error there, and the next run's agent reported that the
rewritten passage stopped it from claiming an alias edit it had not verified.

Six were the tool doing the wrong thing rather than explaining itself badly, and those
are the ones to read first if you skim: an overwritten hand-written contract, a config path
reaching outside the repo, an interrupted install stranding the alias, testFiles: []
emitting a config ESLint refuses, impact demanding a plugin no gate would use, and a CRLF
tsconfig silently skipping the alias edit.

The largest change to how hard this is to break is not in this changelog. Mutation
testing arrived after 3.0.0 — that tag has no stryker.config.json — and the suite roughly
doubled under it. Most of that found places where a wrong edit to the source would have
shipped with every test green. It produced no behaviour you can see, so it has no entry, and
it is still the reason this version is sturdier than the last.

And the honest bound: the wording fixes are reasoned, not measured — with one exception,
which is also the reason to hold the rest loosely. The lint-merge instruction was reasoned
too, and one day later a field agent followed it into the wrong edit to its own config;
correcting it took an eslint --print-config probe per direction on each of the two ESLint
majors, and the answer was the opposite of what the paragraph advised. Several other fixes
repaired sentences this project had written one or two releases earlier, and those net close
to nothing — they closed gaps their predecessors opened. The per-item paper trail for all of
it, including what was judged not worth fixing and why, is in this repo's closed field-run
issues rather than here.

Minor Changes

  • 5aa12b8: Action required, once: run npx blueprint inspect --update-baseline.

    If you have a .blueprint-baseline.json, this release refuses it and prints that
    instruction. Re-keying records the same debt — nothing that was suppressed stops being
    suppressed.

    Why it has to happen: baseline identity used to include the finding's message text,
    the one part of a finding that changes while the violation does not. Rewording any
    finding silently retired every baseline entry for that rule — the old debt came back as
    fresh, the recorded entries counted as stale, and a brownfield CI went red on an
    upgrade that changed no code. Identity is now rule + path + subject, and the
    message is prose the ledger keeps for whoever reads a diff.

    The old baseline is refused rather than reinterpreted: read under the new key it would
    match nothing, which is the wall of red the ledger exists to prevent, arriving with no
    stated cause.

    For --json consumers: every finding gains subject (what inside path the finding is
    about — the import specifier, a cycle's members, empty where the rule and path already
    identify it), and the baseline file is "version": 2.

  • 5aa12b8: An interrupted install now leaves a tree that one command finishes.

    init installs dependencies as part of its work, and the alias edits to tsconfig /
    vite.config used to sit below that step. On a machine that cannot reach the registry,
    stopping the install left a config, an agent contract and an ESLint config with no alias
    anywhere in the toolchain — and doctor then reported ~app resolves nowhere, which reads
    as a broken tool rather than as an install that never finished. Two toolchain files had to
    be hand-edited to clear the final gate.

    Every filesystem effect now lands above the install, so what an interrupted run leaves
    behind is a complete tree minus node_modules.

    And the install announces itself before it runs. It is the one step that can sit for
    minutes — a package manager with no route to the registry retries in silence — so the line
    above it now carries the command it is about to run, that quiet is normal, that minutes of
    quiet means stopping it and running that line yourself or re-running with --no-install,
    and what stopping leaves behind:

    → install: eslint, @kekkai/blueprint, …
          npm install -D eslint @kekkai/blueprint …
          This is the one step that needs the registry. Silence while it works is normal;
          minutes of silence means it cannot get there — stop it and run the line above
          yourself, or re-run init with `--no-install`. …
          Stopping is safe: this is the last step, so every file above is already on disk.
          What stopping omits is these packages in `package.json` — this line is the only
          thing that records them there, so until it runs, a failure naming one of them is
          that gap and not a broken adoption.
    

    No version list to go find: these are your project's dependencies. Which ESLint major
    that resolves to, and what backs it, is its own entry.

  • 5aa12b8: doctor has three outcomes now, not two — and a check that could not run is no longer
    counted as one that passed.

    The merge-survival check — the only one proving your emitted rules are alive in the config
    ESLint actually resolves — skips rather than fails when that config will not resolve,
    because a red you cannot appease is worse than no check. It used to ride in the pass
    count anyway, so the output read ✓ … (skipped) above ✓ Adoption complete — all 7 checks passed.

    What you see instead:

    ⊘ emitted rules survive the merged eslint config (skipped — could not resolve …)
    ⊘ Adoption unverified — 6 of 7 checks passed, 1 could not run (⊘ above).
      Nothing failed, and nothing here proves what those checks cover.
    

    Exit status is unchanged — a skip is not a failure. Gate on the JSON instead of the
    exit code when the difference matters:

    {
      "ok": true,
      "verdict": "unverified",
      "summary": "⊘ Adoption unverified — 6 of 7 checks passed, 1 could not run …",
      "counts": { "total": 7, "passed": 6, "failed": 0, "skipped": 1 },
      "checks": [{ "label": "…", "ok": true, "skipped": "why it could not run" }]
    }

    verdict is complete / unverified / incomplete and is on runDoctor's return value
    too. ok keeps the meaning the exit code needs — nothing FAILED — so counts.skipped or
    verdict is what a CI gate should read.

    Three more things doctor now says out loud:

    • A green banner on a repo with no version control says what it leaves out — nothing
      adoption wrote is committed, and a ratchet living only in an uncommitted working tree is
      not installed.
    • The survival check states its reach: it compares config text and never executes
      ESLint, one probe per layer, and it does not compare thresholds, package ownership, or a
      merged entry covering only part of a layer. Its ✓ says so.
    • When it cannot resolve your config it quotes the loader, so the missing package is on
      screen instead of one npm run lint away — and it names the case where that package is
      also absent from package.json, which means an install step never finished.
  • 5aa12b8: More of what you need to author a config is now something the tool computed, not
    something you have to go and check.

    blueprint survey names the specifiers an owns clause can be verified with. owns
    takes { package: 'react', imports: ['createContext'] }, and the survey's evidence was
    package-granular — react reads as "half the layers use it" whichever specifier the clause
    names, so there was nothing to check such a clause against. There is now a section for
    specifiers that appear in exactly one folder, from a package that appears in several:

    Named imports in ONE folder, from a package in several (specifier-level ownership
    candidates — `owns: [{ package, imports: […] }]`; the rows above cannot support one).
    Read from brace clauses only: a member reached through `import * as` is invisible
    here, so a folder using one is not counted against the "only":
      react → createContext — contexts only
      react → useContext — hooks only
    

    blueprint rules --json carries jsLiteral beside selectors. The selectors you are
    told to copy when folding blueprint's no-restricted-syntax entry into a house one escape
    their path separators — and JavaScript resolves that same escape when it parses a string
    literal, so pasting the rendered value into '…' produced a regex that ends early. No parse
    error, lint still green, and the ban silently matching nothing. jsLiteral is the selector
    as JS source, quotes included; the text output prints that form, since that line exists to
    be copied. selectors is unchanged for programs that build config rather than paste it.

    One inspect run inventories every import cycle, not just the first one it meets — one
    per strongly connected component, ordered by content so an unrelated module does not
    reshuffle the report. Enforcement never changed (one cycle fails the gate), but "how many"
    is the question anyone sizing a migration is asking.

    **inspect's cove...

Read more

v3.0.0

Choose a tag to compare

@github-actions github-actions released this 03 Aug 04:44

Major Changes

  • a2107fd: Folder layout is entry-only. ../Sibling — one module reaching another
    inside the same layer by its public entry — is legal now, on both gates.
    Reaching past that entry (../Sibling/internals) is not, and the alias
    spelling (~app/{ownLayer}/Sibling) stays banned, so a same-layer edge has
    exactly one shape.

    What changed is a reading, not a principle. Folder layout previously banned
    the entry too, which left a folder layer with no legal way to share at all:
    not relatively, not by alias. The only advice the output had left was
    "extract shared code to a lower layer" — and a shared unit sunk with nothing
    to name it lands in the folder that names nothing, one honest decision at a
    time. That message now points at ../Sibling instead.

    It also closes a name collision that cost a real adoption. structure-lint's
    moduleLayout: 'folder' always meant entry-only; blueprint's folder meant
    the neighbour is untouchable. An agent migrating between them carried the
    stricter reading across on the strength of the shared word, and filed fifteen
    imports that had always worked as pre-existing debt. The two now mean the
    same thing.

    Under the hood the two gates stopped being two implementations. inspect's
    relative-escape finding and the embedded blueprint/relative-escape rule
    both call one relativeVerdict, because they claimed to agree by sharing
    resolution primitives and did not — the same ../Sibling could be legal to
    one and illegal to the other, with no test positioned to see it. The rule
    also receives each layer's entry filename, so a layer whose entry is not
    index no longer reads every entry import as reaching past one.

    Same-layer edges now exist, so cycles among them are possible. Nothing new is
    emitted for that: cycles are a property of the graph and inspect walks it
    once, where a per-file lint rule re-walks it for every file. A project that
    wants the cycle red at edit time can add import-x/no-cycle with
    ignoreExternal: true — the carrier already ships.

  • 35a7923: importBlock now rides eslint-plugin-import-x. The emitted rule ids
    change from import-lite/first / import-lite/no-duplicates to
    import-x/* — a merged config that overrides either one by name stops
    matching, which is the whole of the breaking surface.

    import-lite was chosen when the adoption baseline still included repos that
    import-x could not install into: it peers on @typescript-eslint/utils@^8.56
    for its resolvers, and a repo pinned below that failed the install as a whole
    (field issue #41), while the original eslint-plugin-import caps its eslint
    peer at 9 (#37). On an ESLint 10 baseline that trade reverses. No tree reaches
    ESLint 10 while holding typescript-eslint below 8.56 — older typescript-eslint
    refuses ESLint 10 as its own peer — so the population the guard protected is
    now the repos already installing with --legacy-peer-deps, whose installs do
    not abort on peer conflicts in the first place. ALLOWED_CARRIER_PEERS records
    that as a deliberate entry rather than a widened hole, and says which
    conformance fixtures would prove it wrong.

    What the resolvers buy is the whole-graph family a resolver-free plugin
    structurally cannot express, no-cycle above all. This release does not emit
    it: cycles are a property of the graph, and inspect already walks that graph
    once where a per-file rule re-walks it for every file. But a project that wants
    the cycle red at edit time can now reach for it without adding a plugin — and
    with ignoreExternal set, since the rule's default walks into node_modules
    and a project with no cycles pays the most, having no early exit to find.

Patch Changes

  • deb61a4: The early-exit checklist says where version control stops. It closes with
    doctor, and said nothing about committing — while the full method has the
    clause: commit what adoption wrote, and where that is impossible, leave the
    files and say so rather than initializing version control for the owner.

    A field agent on that path went and found the clause in the full method, then
    followed it correctly. It got the right answer by leaving the checklist that
    had just told it "nothing else in this file applies" — a handoff you have to
    go looking for is one the next reader can miss, and the miss here is an agent
    running git init inside somebody's directory.

    The checklist now carries it, and says it is a repetition and why.

  • e6696af: rules hands over the whole entry, not just its selectors. Flat config
    replaces same-key entries rather than merging them, so the merge guidance asks
    an adopter to fold blueprint's no-restricted-syntax selectors into their own
    entry, and points at rules --json for the exact strings. That output had the
    selectors and nothing else — while the emitted block also carries
    ignores exempting test files. An entry rebuilt from selectors alone has no
    exemption, so it starts governing tests.

    A field run lost it and spent a debug cycle on 34 errors in one test file. That
    was the loud version, and only because the adopter's own rule happened to
    collide on the same layer. Where nothing collides, the entry that quietly
    reaches test files is blueprint's own selfOnly ban, behind a lint that stays
    green — and doctor compares selectors, not scope, so nothing downstream
    notices.

    rules --json now carries testExemptions beside the selectors on every
    layer, the text output prints the ignores line to paste under the selectors
    to copy, and the playbook's merge section states that an entry is more than
    its selectors, naming both how the loss shows up and how it hides.

v2.2.0

Choose a tag to compare

@taco3064 taco3064 released this 30 Jul 09:01

Minor Changes

  • 4caf8fd: ESLint now owns formatting in the emitted config, and maxLines finally has a unit. Five new gate ids, all riding a caller-injected plugin so the library keeps zero runtime dependencies. All land at error in every preset.

    The load-bearing one is statementsPerLine → @stylistic/max-statements-per-line at a hard-wired { max: 1 }. maxLines counts code lines with blanks and comments skipped, so a line budget with no cap on line content is satisfiable by collapsing statements onto one line instead of splitting the file — the evasion an agent under budget pressure reaches for first. codeStyle closes the rest of that route: @stylistic's own customize() set (read from the factory, not hand-listed — a hand-picked subset is what left zero-indentation legal) plus max-len at 90, linebreak-style: unix, and core curly, which matters because without it if (x) return; counts as one statement and slips past the line gate. Knobs: indent, quotes, semi, maxLen.

    Also explicitAny → @typescript-eslint/no-explicit-any (the mechanical half of the narrow-interfaces principle — any is the hole that lets illegal states be expressed), statementPadding → @stylistic/padding-line-between-statements with a fixed 17-entry list, and importBlock → import-lite/first + import-lite/no-duplicates, which catches the two import mistakes an incrementally-editing agent makes routinely and which no formatter can fix.

    emitLint grows options.stylistic and options.imports beside options.typescript; the generated config passes all three, init installs @stylistic/eslint-plugin and eslint-plugin-import-lite, and impact loads both from the project so its per-rule counts cover the new gates instead of reporting a silent zero. A gate whose carrier plugin is absent emits nothing while lint still passes — indistinguishable from a clean merge — so the merge snippets, the gate catalog, the playbook and the generated config each state that the options object travels whole. codeStyle throws rather than emitting an empty bundle when handed a plugin without configs.customize().

    Three behaviors worth knowing before adopting. The shape family is the one gate family test files are not exempt from — a metric threshold screams on a test, but a duplicate import or a collapsed line is no easier to read there. codeStyle is nearly all auto-fixable (about 5 of ~68 rules are not), so eslint --fix clears most of a first run: the playbook asks for that pass as its own commit, since it rewrites whitespace repo-wide, and notes it cannot push a file over maxLines. What --fix cannot clear is named too — max-len has no fixer and deliberately does not exempt plain strings, because a cap a line escapes by containing a string is not a cap; and a linebreak-style red usually means git's autocrlf or a missing .gitattributes, not the file, so fixing the source just gets undone by the next checkout. Two rules stay rejected on their own merits, and the carrier that ships does not carry them anyway: import/no-cycle (a per-file re-check of a graph inspect already walks, measured at 92s on 850 files) and import/no-unused-modules (cannot run under flat config). Both need a resolver, which is exactly what the shipped plugin leaves out. On a JavaScript project explicitAny leaves inspect's coverage denominator entirely — any is a TypeScript construct, and a gate nobody can open is not a gate, the rule deepWatch already follows on React.

Patch Changes

  • cbd8ae8: Three field-run corrections to what the tool says about itself. No behavior changes — but each of these sent an adopting agent somewhere it did not need to go.

    The playbook no longer invites impact into the drafting loop. "Let the tools correct you: inspect and impact are read-only and cheap" sat above the Method as a licence to run both while drafting, and both brownfield agents of the run took it — impact lints with the emitted config, so it needs the plugins init installs and greeted them with a load error instead. One agent filed it as a self-contradiction, the other blamed itself; the same wall twice is the playbook's, not theirs. The paragraph now names inspect alone as the config-only loop tool and states where impact joins (Method step 9, after init) and why it cannot come earlier. The load error's own pointer to blueprint init — the thing that got both agents unstuck in one hop — is now asserted, not incidental.

    A deletion in init's output no longer wears the writes' ✓. − rm: marks it instead. The note always carried the cause (pristine preset scaffold — removed; the playbook authors the real one), but a destructive line stamped with the success mark and buried among writes skims past as one more thing created: a field agent filtered init's output for write and missed its own config being reclaimed, then spent commands hunting the disappearance. The mark says which direction the effect went; the note still says why.

    And the install line stops stuttering its own kind — ✓ install: install eslint … is now ✓ install: eslint ….

  • c420e2a: A finding now names the rule that carries it, and the --agent flag says which path launches anything. Two field observations, both cases of a correct state reading as a broken one.

    One violation went by three names. inspect reported [deep-import], impact and the lint run called it no-restricted-imports, and an adopter verifying the merge searched the resolved config for blueprint/deep-import and found nothing — because that ban, like flow-violation and package-ownership, folds into the single no-restricted-imports entry rather than standing alone. Inspect's migration steps now carry both halves: each names its finding and the ESLint rule that enforces it, and the findings no lint run will ever show — cycle, undeclared-folder, no-entry — say so instead of leaving the reader to wonder where they went. relative-escape is called out as the one structural ban that is a standalone rule, since a ../ escape cannot be written as a literal pattern.

    The playbook's --print-config guidance gains this as its fourth "know this before reading the output": finding names are not rule ids. It sits beside the three added last release — prefixed keys, rules absent because their layer holds no files, and selfOnly resolving on the importer layer.

    Separately, the README's security bullet was imprecise about --agent and two field runs stopped to check. It presented the flag as the one explicit opt-in that runs an agent, without saying that this is true only on the authoring path; on the preset path (init --preset --agent claude) nothing is launched and the flag only narrows which contract file is written. Both meanings are stated now, with init --help and --dry-run named as the authoritative check — which is what both agents reached for.

  • 21eeb2f: The generated handbook stops promising a gate that does not exist. Its Rules table printed a tier beside every declared rule under one legend — "error fails lint" — which is true of most rows and false of two kinds. cycles is inspect's finding and deliberately emits no ESLint line; deadCode is documentation-only and asks for knip. A preset declares both at error, so a stock handbook told its reader that two rules gate their lint run when neither does.

    A field agent caught it by cross-checking against blueprint rules and trusted the catalog — at no cost to itself, but it named the real problem: the handbook is the artifact meant to outlive the adoption, read later by people who will not have the catalog open beside it. Two generated artifacts from one source disagreeing is the thing the README claims cannot happen.

    The table gains an Enforced by column — lint, `blueprint inspect`, or documentation only — and the legend now separates the tier (what the enforcing machine does with a violation) from which machine that is. Unknown ids resolve to documentation, matching how the agent contract already treats them.

    The classification comes from the catalog rather than a second hand-kept list: GateSpec gains an optional runtime marker, cycles carries it, and enforcedBy() derives the three-way answer. Adding a runtime-backed gate needs no edit in the handbook, and a test asserts that correspondence rather than the current membership.

  • c423c4e: The last carrier stops constraining the adopter's tree, and doctor's merge check stops over-promising. Two field findings, both about a green that meant less than it said.

    importBlock now rides eslint-plugin-import-lite (import-lite/first, import-lite/no-duplicates). The two predecessors each walled a real repo: eslint-plugin-import caps its eslint peer at 9, and eslint-plugin-import-x peers on @typescript-eslint/utils@^8.56 for resolvers these two rules never use — an optional peer is still version-checked when the package is present, and no nested copy can satisfy a peer, so a repo pinned at 8.47 could not install it at all. Since npm resolves the required-deps list as a unit, either one failed the whole install. import-lite is those same rules ported without the resolvers: zero dependencies, eslint as its only peer, and behaviour verified identical across eleven cases including the TypeScript one that matters (import type beside a value import from the same module stays legal). No release ever carried the old ids.

    The peer guard widened with it. It already asserted every carrier admits each supported ESLint major; it now also asserts no carrier peers on anything else the adopter owns, since that is the constraint that actually failed. typescript-eslint ↔ typescript is allowed by name; a new peer is a test failure to review, not a surprise in someone's adoption.

    Doctor's merge-survival check no longer walks past the failur...

Read more

v2.1.0

Choose a tag to compare

@taco3064 taco3064 released this 24 Jul 07:18

Minor Changes

  • 003cacc: Drop the "backend boundary" posture from the emitted handbook. The respect-backend core belief and the whole "Data integrity & backend boundary" playbook section (no-fake-fallback, drift-guard-framing, no-fe-workaround, preserve-locale-shape) no longer ship in the presets, the handbook, or the agent contract.

    No API surface changes — PrincipleDef and PlaybookSection are untouched. The preset data is simply nine beliefs and a three-section playbook now (was ten and four). Re-run init to regenerate a handbook and agent contract without the section; a repo that authored its own principles / playbook is unaffected.

  • 7dcbe82: Drop CI scaffolding. Blueprint no longer emits .github/workflows/blueprint-ci.yml, and the emitCi export, the CiOptions type, and the emit.ci config field are removed.

    Verification strategy is the adopter's call — a git hook, GitHub Actions, GitLab CI, whatever you already run. Blueprint still ships the verification commands (inspect --baseline, doctor) that exit non-zero on new findings; wire them into your own gate.

    Migration: remove emit.ci from any blueprint.config.mjs (it is now an unknown key, so init rejects it), and move the two steps the emitted workflow ran — eslint and blueprint inspect --baseline — into your existing pipeline.

    Shipped as a minor by the owner's call: strictly this removes public surface, but that surface existed for days on 2.0.0, nothing in the field ever used it, and a config still carrying emit.ci fails loud with the pointed unknown-key message above — not silently.

  • ab134f4: architecture.module is optional — the playbook's "flat default" is now real (field issue #23): omitting the block, or any of its keys, resolves to { layout: 'flat', entry: 'index' } through one shared reader, and an empty entry still fails loud with the default named as the way out. Validation used to demand module.entry while Method step 5 said plain files take the flat default — the field agent burned two edit-run cycles proving the tool contradicted itself.

Patch Changes

  • ab134f4: Judgment calls agents kept making unaided, now stated where they arise (field issue #23): rules says the fold's message text is yours to write (doctor verifies selectors, never messages); a house rule under a different KEY with the same semantics never collides — keep one gate per semantic; an owns candidate the intent documents never mention is a proposal for the report, not a clause to encode; annotating a bare disable's reason is a comment edit inside the adoption boundary; everything the adoption produced — config, artifacts, both ledgers — is meant to be committed, and initializing VCS is never the agent's call; the CLAUDE.md discipline pointer notes the contract ships inside the package.

  • ab134f4: init's vite alias instruct respects a tsconfig-paths bridge plugin (field issue #25): with vite-tsconfig-paths in the vite config, the tsconfig side init already wires covers the bundler, and doctor's alias check passes that state — init no longer demands a redundant resolve.alias the check never asks for, and the instruct itself names the bridge-plugin escape hatch when it does fire.

    Contract references ship WITH their BLUEPRINT:START/END markers (field issue #26): the reference's header said "init rewrites only between the markers" while the file carried none — the agent probed raw bytes, guessed headings were markers, and the integrated block went permanently stale (a wrong layer flow in CLAUDE.md, invisible to doctor). Pasting the reference verbatim now keeps the block refreshable by any later init; both integration instructs state the trade — keep the markers and init refreshes the block, strip them and the file is yours to update by hand, forever. Also from the same batch (#24–#26): the build-verification artifacts are named as the build's normal output, not adoption leftovers; ongoing enforcement is stated as deliberately unscaffolded (the gate commands are the deliverable, the pipeline is the owner's); linearization prefers the smallest-relaxation position among equally legal ones; a skipped parser block's package is dependency wiring to keep, not waste.

  • ab134f4: Closing-round polish from field issues #27–#28 (zero blockers across four scenarios): the survey's "Same-folder imports via the alias" section always prints — an explicit 0 (none found) instead of a silently absent row the playbook cites; the one-gate-per-semantic rule now spans gate layers (a house import/no-cycle and the inspect-side cycles gate are one semantic — pick one detector); the owns sketch states that repeating the same entry across layers IS the shared-allowance syntax (same-signature entries merge).

  • 049b1fa: The playbook states doctor's leftover criterion — exact file families (the playbook, the command file, *.blueprint.* references, marker-bearing contracts outside emit.agents), never other files whatever their names. Two field runs verified this experimentally by writing a file and re-running doctor (issues #27, #32); the answer now sits in the semantics section instead.

  • 325379e: The anti-bypass guard's files glob scopes to the detected stack's extensions, like the parser blocks — a react repo's guard used to carry .vue, and four field runs trimmed it by hand (field issue #30); an unknown stack keeps the full set, and the glob honors sourceRoot. The guard's comment also states that its position relative to the emitLint spread does not matter (the rule sets never intersect) — the one merge-order doubt agents kept resolving themselves.

  • ab134f4: The merge instruction meets the agent at the point of need (field issues #21–#22): an existing eslint.config.ts gets the TS7016 caveat — importing ./blueprint.config.mjs needs allowJs on the tsconfig covering the config, or a one-line blueprint.config.d.mts; defineConfig([...]) arrays are named spread-equivalent; the generated jsx parser block carries its own skip criterion (dormant on a TS-only repo).

    "Only a real run proves it" now covers the alias wiring (both runs added the build check by hand): the early-exit checklist and the merge step order one build run after init edits tsconfig/vite, stating why — doctor's alias check reads wiring as text, never as a compile. The contract header now says hand-written notes live outside the marker block, the convention an agent had to infer when the generated file became the repo's only CLAUDE.md.

  • ab134f4: An additional alias whose target sits above the source root now really joins the bans (field issue #29): '~root': '.' used to emit ~root/<layer> patterns no real import ever used — the whole ~root leg of every structural ban was a silent no-op, inspect was equally blind to ~root/src/<layer> imports, and a closing report almost claimed a protection that did not exist. Emit and inspect now derive each alias's layer base from one shared helper with the target offset baked in (~root/src/views/**, selector ^~root\u002Fsrc\u002F…), so patterns and findings cannot disagree; an alias into a subfolder has no layer surface and carries no layer bans, and the playbook says so.

  • ab134f4: Playbook verdicts agents had to invent, now stated (field issues #18–#20): the report is a message — the closing reply or the PR description, never a committed file — and the early-exit path says its one-line verdict IS the report; the early-exit wiring step closes by running the project's own lint once (doctor's wired check reads config text and never executes eslint, so a load-time crash sailed through all seven checks); the owns sketch notes pattern: true globs match any import specifier, internal alias paths included; init's preset hint carries --agent, matching the checklist it hands off to.

  • ab134f4: selfOnly re-export selectors encode / as the \u002F regex escape: esquery below 1.7 has no \/ escape in its regex literal, so the emitted selector truncated to a broken pattern and crashed ESLint — and blueprint impact — on every file of the selfOnly importer's layer, wedging doctor's survival check against the lint gate (field issue #19). Doctor's survival red now names both possible causes (a replacing flat-config entry, or a hand-folded copy that drifted from this version's output) and points at rules --json for the exact selectors, instead of asserting a merge collision that may not exist.

    rules (text and --json) carries the exact no-restricted-syntax selector strings a selfOnly merge fold needs, per layer and target: the playbook demands "combine both option sets into ONE entry", and the only source for the selectors used to be an emitLint dump — exactly the bundle archaeology the same playbook forbids (field issue #20).

v2.0.0

Choose a tag to compare

@taco3064 taco3064 released this 22 Jul 08:29

Major Changes

  • 33ed541: 2.0.0 — the field-hardened release. Fifteen autonomous adoption rounds
    (closed field-run issues #1–#15) drove every change since 1.14; the
    loop converged when a full round reported zero tool defects, with every
    suspicion an agent raised verified against the channels and retracted.

    Breaking changes:

    • Config validation rejects unknown keys across the structural path
      (blueprint, architecture, layers, allowedImporters entries, module,
      owns entries, emit). A key nothing reads was a silently dead
      declaration — a field repo shipped a selfOnly ban that never existed
      because the key sat one level too high. Configs carrying stray keys
      now fail loud with a pointed message.
    • architecture.flow and emit.lint.path are removed (both were
      inert), along with the retire command, the --framework flag on
      impact / doctor / rules, and the never-documented entry exports
      emitAgentContract / AgentContractOptions / injectBetweenMarkers /
      StructuralRule.
    • doctor is stricter: leftover authoring artifacts
      (blueprint-authoring.md, the command file) fail the leftover check —
      "Adoption complete" and the playbook now define done identically; a
      marker-bearing contract outside emit.agents already failed since 1.14.
    • The emitted CI workflow gates with inspect --baseline (a missing
      ledger is an empty one) — locked brownfield debt no longer turns the
      tool's own CI permanently red.
    • architecture.module.private is optional (omitted = none) — the
      one loosening.

Minor Changes

  • 4fe2d55: Product-wide chicken-rib sweep — dead surface is gone, not deprecated:

    • architecture.flow removed. It was a required field that nothing ever
      read — the layer ORDER is the flow. Existing .mjs configs keep working
      (the runtime ignores unknown properties); TypeScript-typed configs delete
      one line.
    • emit.lint.path removed for the same reason: never consumed, while its
      doc claimed a consumer.
    • emitAgentContract (and AgentContractOptions) left the package entry:
      the supported agent targets are the ones emit.agents names, and a
      render-it-yourself hatch for unsupported tools is surface without a
      mission. emitAgentFiles remains the one distribution API.
    • injectBetweenMarkers left the package entry — an internal merge utility
      with no documented story.
    • plugin stays, and its story is now stated in the reference: the escape
      hatch for hand-wiring a blueprint/* rule without emitLint.
  • 538578b: doctor and the playbook now define "done" identically (field issue #13):
    the leftover-files check also flags blueprint-authoring.md and
    .claude/commands/blueprint-author.md — "Adoption complete" over a live
    playbook was a second, contradicting authority, and a careless agent
    stopped there. The check's detail says a mid-authoring doctor run is
    EXPECTED to fail on it; the early-exit checklist and acceptance gates now
    order cleanup BEFORE the final doctor run. Gate turns stricter: repos
    that finished wiring but skipped the playbook's cleanup step go red until
    the two files are deleted.

    Also: the missing-layer note carries the keep-is-default verdict in
    place ("runway, not a todo … slimming is the owner's call") — six of
    these read as a todo list and pointed an agent at dismantling the preset
    skeleton the playbook says to keep.

  • 6c5af1c: Field issue #4 (489-file brownfield run, structure-lint retired per the
    playbook): unknown CLI flags now fail loud instead of being silently ignored
    — inspect --verbose read as a broken no-op to an agent; init re-includes
    its gitignored artifacts by appending ! negations to .gitignore itself
    (with the parent-directory caveat stated in the note) instead of handing the
    fix back; the rule catalog now states what two agents had to eval the bundle
    to learn — owns covers named-import granularity (a house "only composables
    may inject" rule maps to it), no-restricted-syntax is emitted only when a
    selfOnly importer exists, and additionalAliases join every structural ban;
    the playbook's retirement clause says to DELETE the retired tool's config
    and that source-comment pointers may outlive the sweep under no-source-edits;
    impact's echoes block leads with "NOT blueprint findings, NEVER counted".

  • f7bc5d9: Field issue #5: init --agent claude now PERSISTS the choice — a scaffolded
    config carries emit: { agents: ['claude'] }, so the first run emits one
    contract and the next plain init cannot grow the second one back; the
    chicken-and-egg (config must exist before you can narrow it) is gone. The
    catalog closes the last bundle-eval of the round: unusedVars is named as
    TWO rule keys on TypeScript, every optional gate is stated to scope to the
    layer globs only, and merge guidance says collisions are decided by rule
    KEY (blueprint rules --json names them), not by hit count.

  • 5337f77: Field issues #2–#3 (automated harness runs): doctor's leftover check now
    also flags stale agent contracts — a marker-bearing CLAUDE.md/AGENTS.md/…
    outside the emitted emit.agents set cannot hide behind green (init removes
    a wholly-generated one itself; a hand-touched one only got an instruct, and
    nothing gated the orphan afterward). A preset that introduces the repo's first import
    alias names it as a decision instead of letting a new convention pass
    silently. init --help stops promising layer folders unconditionally and says
    plainly that --agent never launches anything on the preset path.

  • a3aa5a8: Field issues #7–#8: blueprint rules gains the resolved per-layer view —
    what each layer may not import, which owned packages (down to named imports)
    and globals are banned there — ending the eslint --print-config
    archaeology behind "0 hits: wired-and-clean, or not applying at all?". The
    playbook's early-exit verdict becomes a complete self-contained checklist
    ("following this verdict IS executing the playbook fully"; impact's zero
    gates the suppress-all run; trivially-true acceptance gates are named as
    such; the now-empty command directory goes too), the config schema's full
    owns/additionalAliases shapes live in the playbook so nothing exists
    only in dist, the test-file exemption is named as a deliberate relaxation
    versus tools that police tests, and a below-threshold --authoring run
    says up front that the playbook's own verdict will be the early exit. All
    adoption prompts state that an early exit the playbook prescribes counts
    as full execution.

  • 99a0727: Init now lives by its own doctrine (field batches 10–11): empty layer folders
    are scaffolded only into an empty source tree — where code already lives, an
    unbuilt layer's absence is its true state, no .gitkeep shells. Narrowing
    emit.agents makes the next init remove a stale agent contract that is
    wholly its own output (nothing outside the marker block; own-strategy rule
    files by construction) — a hand-edited file only gets told. And
    validateBlueprint now returns the blueprint instead of void, so a passing
    call is visible at runtime.

  • 1e077f1: A config key nothing reads now fails validation instead of dying silently
    (field issue #14): a 489-file field repo declared selfOnly: true on the
    layer object — where nothing reads it — and the intended re-export ban
    never existed while every gate stayed green. Validation now rejects
    unknown keys across the structural path (blueprint, architecture, layers,
    allowedImporters entries, module, owns entries, emit), and the misplaced
    selfOnly gets a pointed error naming its real home
    (allowedImporters: [{ layer: 'views', selfOnly: true }]).

    blueprint rules also answers "does THIS config emit it?" for the
    structural family: each rule carries a per-config ✓ emits / · not emitted annotation (active in --json), so nobody has to probe
    emitLint to learn whether their no-restricted-syntax will collide with
    a house rule. A test pins the annotation to emitLint's real output.

  • 514eb53: Presets take emit directly and merge it over their day-1 default
    (ci: 'github'). Declaring the agent tool in use is the first customization
    nearly every adoption makes — it no longer costs the one-line preset form,
    and no longer silently drops the CI workflow the way a spread-level emit
    override does.

  • 62ddb7e: architecture.module.private is now optional — omitting it means no
    private parts ([]). A draft-first config that never mentions private
    sub-parts validates instead of failing with "must be an array" (field
    issue #11). Explicit private: [] keeps working unchanged; a non-array
    value still fails loudly. The playbook's schema sketch marks the field
    optional.

  • 0ee89bf: The tool now answers for itself (field batch 12): blueprint rules prints
    the emitted-rule catalog — always-on structural rules, optional gates with
    metric defaults, documentation-only ids — annotated with the config's
    declared tiers and whether each gate emits today. inspect gains the tenth
    finding, declaratory-self-only: a selfOnly ban protecting a layer with no
    files is named as a blank round until code lands. The authoring playbook
    additionally licenses draft-first authoring — write the config early and let
    read-only inspect/impact runs correct it, instead of studying the archive
    first — and carries the retirement sweep for a consolidated-away tool.

Patch Changes

  • impact's zero-hit line states its own reach: "no red today" is an
    emitLint claim, and the anti-bypass guard rides in the generated config
    outside impact's scope — its findings (bare eslint-disables) surface in
    the project's own lint. The playbook's "zero hits → skip
    --suppress-all" carries the same carve-out (field issue #17: 0 hits,
    then five guard findings in the real lint run).
  • e59c662: Init...
Read more

v1.14.0

Choose a tag to compare

@taco3064 taco3064 released this 31 Jul 01:14

Minor Changes

  • bc55a63: Doctor's merge-survival check no longer goes blind on empty repos — the anti-false-green gap field batch 7 called ironic. A layer with no files yet gets a synthetic probe derived from its own globs (calculateConfigForFile resolves by pattern and never touches the filesystem), so a gutted config turns red with zero files on disk; globs the synthesis cannot honor simply yield no probe. The authoring playbook gains an early-exit clause: on a repo at or below the preset threshold whose shape a preset fits, init --preset is a legitimate verdict — walking the full method on a starter is ceremony, not judgment. Docs show the { ...reactPreset(...), emit: {...} } spread, since emit is a top-level blueprint field, not a preset option.

Patch Changes

  • 5d45120: Layer names carrying glob or path characters are rejected at validation — a layer literally named * (field batch 9's root-files workaround) widened every file glob into a wildcard and scaffolded a literal src/*/ folder. The playbook states the doctrine the workaround violated: an empty net on a root-only app is the true state, not a failure — never invent a layer to make coverage non-zero; root-file hygiene belongs to the project's own lint.
  • 9a5b15a: Batch-8 field fixes, all with repros. usePrefix: 'off' no longer validates its target layer (an off rule has nothing to target — it used to throw on repos without a hooks layer). impact stops contradicting itself: parse-error and unused-disable-directive move out of the wiring-red total into an "Isolation caveats" section that says which kind vanishes after the merge and which survives. The handbook diagram renders order-only spine edges dotted and declared importer relations solid with inline labels — consecutive leaf layers chained solid read as dependencies they never were. The playbook's overlapping-tool guidance gains its missing exception: when the existing tool sets the same ESLint rules emitLint emits, coexistence is mechanically impossible and consolidation becomes a wiring precondition, not a scope decision to flag.
  • 6464b43: Scanned paths normalize to forward slashes at birth — on Windows every downstream glob net (inspect, coverage, survey, survival probes) silently matched nothing. Layer-name validation also rejects characters that corrupt the emitted mermaid diagram (whitespace, quotes, parens, &, %, ;) while keeping real conventions like @core valid; pipe-bearing edge descriptions render as / instead of breaking the inline-label syntax; and the playbook's deliverables and acceptance gates go zero-debt-conditional — a clean repo's absent baseline is the correct outcome, not a stalled checklist item.