Skip to content

Spec: architecture program — scaffold, sync table, deepened modules, core carve-out #44

Description

@diegohb

Problem Statement

As the suite's maintainer, every architectural friction found in the October 2026 reviews traces to one root: generated packages and the suite keep near-copies of the same install machinery with nothing comparing them, and the copy step itself is prose-driven (an agent hand-copying template bodies). PR #43 made both sides bigger at once. Meanwhile the generation step costs ~20 minutes of mechanical agent time and produced every field deviation we have reviewed. We also cannot answer "are the templates and the code in sync?" today except by reading files two at a time.

Solution

A seven-item implementation program, sequenced into five batches:

  1. scaffold CLI (issue Placeholder: scaffold subcommand for the bunx CLI #20): deterministic generation of a conformant package — fresh mode (questions or flags → rendered tree) and promote mode (existing project-local extensions → package, config reference ensured, payload verified, consent-gated retirement). The mechanical inventory the packager agent performs by hand becomes one CLI implementation; the agent keeps content authoring and merge decisions.
  2. The sync table: classify every mirrored template↔suite pair in one table — byte-synced (test asserts source equals template body), declared-divergent (a divergence manifest names the policy: suite-only clear-cache modes, suite-only status flags, generated-only migrate, both-scope generated status), and fixture-covered (generated-only files exercised by the rendered-fixture). Guidance (conformance checklist, publisher instructions) references the same table.
  3. One entry predicate: a single classifier owns "does this plugin registration key entry name our package" — form parsing, path-form/file:/// entries, scoped-name matching — consumed identically by the suite's status/detection and by generated packages.
  4. Split the config editor class: reader / registration detector / surgical splicer as three modules; the splicer's interface stays exactly as is; the detector becomes single-purpose; tests stop constructing an editor to read.
  5. Deepen the config reader: one interface answers "which config candidates exist for this scope base, what do they contain" for both parse modes (offset-preserving and lossless); today's config-entries re-copy dissolves into it.
  6. Inject the environment: scope-base lookup, the warn channel, and fetch become explicit adapters at each seam; the process environment is read in exactly one adapter at the edge.
  7. Core module carve-out (the SDK, reframed): the genuinely identical machinery (name matching, manifest io + hashes, config splice, entry predicate, effective-version pipeline, cache paths) becomes one deep module whose interface declares policy at the seam; the suite CLI and the generated CLI become its two adapters; consumer packages stop vendoring machinery; templates shrink to adapter renderings that scaffold emits. Existing consumer packages migrate via promote.

User Stories

  1. As a suite maintainer, I want scaffold to emit a conformant package in seconds, so that package creation no longer depends on mechanical agent work.
  2. As a suite maintainer, I want every template↔suite pair classified in one table, so that "are we in sync?" has a one-place answer.
  3. As a suite maintainer, I want deliberate divergences declared with their policy reason, so that accidental drift and intentional difference are distinguishable in review.
  4. As a suite maintainer, I want a fix in shared machinery to reach every generated package, so that I stop fixing the same bug twice.
  5. As a suite maintainer, I want the entry predicate to live once, so that the suite and a generated package cannot disagree about whether an entry is ours.
  6. As a suite maintainer, I want the config editor split by responsibility, so that read-side callers stop constructing an editor to read.
  7. As a suite maintainer, I want scope bases, warnings, and fetch behind adapters, so that tests stop mutating the process environment and intercepting the console.
  8. As a suite maintainer, I want the core carve-out designed twice before it is built, so that the policy seam is deliberate rather than inherited drift.
  9. As an AFK agent, I want scaffold fully flag-driven, so that I can produce a conformant package without interactive prompts.
  10. As a packager agent, I want mechanical scaffolding delegated to the CLI, so that my effort goes to content authoring, merge decisions, and judgment.
  11. As a packager agent, I want promote to ensure the config reference before retiring sources, so that I never orphan installed extensions.
  12. As an extension author, I want promote to require my explicit consent before deleting anything, so that my working files are never removed without my say-so.
  13. As an extension author, I want promote to abort retirement when no config reference can be established, so that the source stays in place and working.
  14. As a consumer developer, I want generated packages to stop carrying ~2,000 lines of vendored install machinery, so that package reviews and audits stay about content.
  15. As a consumer developer, I want a fix in shared machinery to arrive with a version bump, so that my packages stay correct without re-scaffolding.
  16. As a consumer developer, I want my hand-edited installed files to keep their current protection, so that the carve-out does not change consumer-modification semantics.
  17. As a consumer developer sharing packages via local file paths, I want the dependency story verified end-to-end, so that local sharing does not silently require registry access.
  18. As a plugin engineer, I want scaffold to hand merge decisions to me after copying, so that my routing role survives the mechanical automation.
  19. As a publisher agent, I want the divergence manifest to state the generated CLI's contract, so that I stop enforcing it from memory and prose.
  20. As a skills-market consumer, I want the discoverability and install-parity properties unchanged, so that market compatibility survives the program untouched.
  21. As an implementer of any item, I want each batch's items touchable independently, so that parallel agents do not fight over files.
  22. As a reviewer, I want drift to fail a test rather than surface in review, so that review time goes to judgment calls.

Implementation Decisions

  • Scaffold is one CLI module with two modes. Fresh: name, deployment target, shipped kinds, scope — every question overridable by flag. Promote: establish repo root, copy content to package-root directories (no assets wrapper), ensure the config reference via the surgical editor, run install, verify the payload against the manifest, print the removal list, delete only on explicit consent. The generation implementation is the template test harness's renderer, promoted from test code into the CLI.
  • The sync table is the interface of template sync. Three states per pair: byte-synced, declared-divergent (with the policy named: broad clear-cache and status path/package flags are suite-only; migrate and both-scope status are generated-only; the suite installer is a class while the generated installer is function-style), fixture-covered. Byte-sync enforcement widens to every pair whose bodies can genuinely be identical (at minimum the root entry pair that already matches but is unasserted). The conformance checklist and publisher instructions reference the table instead of restating policy.
  • The entry predicate is one module. It absorbs spec parsing, scoped-name policy, case folding, and path-form resolution; the registration detector and both status paths consume it. The generated side receives it through the same mechanism as the rest of the machinery (byte-sync where identical, core module after item 7) — never a second classifier.
  • The editor split preserves the splicer's interface exactly — it already passes the depth test. The detector becomes single-purpose; the reader feeds both; this split is internal seams only, external behavior unchanged.
  • The config reader's interface is entries(scope, projectDir) returning candidates with path, leniency, writability, and parsed entries; both jsonc strategies (offset-preserving for the splicer, lossless for readers) live behind it. The config-entries module dissolves into it.
  • Environment injection lands after items 3–5 settle, so each adapter is built once against final interfaces: scope bases arrive as parameters, warnings become part of the report interface, fetch becomes a parameter at the CLI seam.
  • The core module is designed twice before implementation (parallel interface designs, compared on depth, locality, seam placement). Preferred shape: a slim subpath export rather than the whole plugin as a dependency. Policy is declared at the seam (allowed commands, scope coverage), not parameterized ad hoc. A manifest-format compatibility rule is explicit before any generated package depends on it. Prove the dependency end-to-end through the rendered fixture before migrating any real consumer package. Record an ADR when the design lands: templates' role shifts from structural source of truth to adapter renderings — ADR-0005's resolution mechanics stand, its truth-source role is superseded.
  • ADR-0006 invariants are preserved everywhere: scope-aware, manifest-gated install; semantic entry matching; surgical config edits; hooks never throw. ADR-0004's mode exclusivity is untouched.

Sequenced plan (parallel batches):

  • Batch 1 (parallel): item 1 (scaffold) ∥ item 2 (sync table). Both touch the template test harness — scaffold extracts the renderer, the sync table edits the pair list; coordinate or land scaffold's extraction first.
  • Batch 2 (parallel): item 3 (entry predicate) ∥ item 4 (editor split). Disjoint seams.
  • Batch 3: item 5 (config reader) — needs the split's reader and the unified predicate.
  • Batch 4: item 6 (environment injection) — after the reshaped interfaces settle, so adapters are built once.
  • Batch 5: item 7 (core carve-out) — design-it-twice first, then implement, then migrate consumer packages via promote. The divergence manifest from item 2 defines the policy seam.

Opportunistic work riding along in batches 2–3: fold alias/tag-wrapper interface members into their owners and move rendering concerns across the seam (from the October 8 review's wrapper sweep). The October 8 parity-fixture candidate is absorbed by item 2's fixture assertions and obviated by item 7.

Testing Decisions

  • Good tests assert external behavior through the seams only: CLI argv → stdout + on-disk state; the rendered fixture exercised end-to-end (typecheck, its own test gate, install/status/uninstall under an isolated environment); the sync table asserting source equals template body for byte-synced rows and asserting the policy difference itself for declared-divergent rows.
  • Prior art: the rendered-fixture suite (a full generated package built from templates, then run against its own gates), the CLI test patterns with isolated environment directories, and the entry-resolution tests.
  • New seams earn their place by the two-adapter rule: the environment adapter (real environment in production, deterministic fake in tests) and the core module (suite adapter, generated adapter). The editor split adds internal seams only — its tests keep hitting external behavior.
  • Item 7 adds a fixture run where the generated package depends on the core module — install, status, and uninstall must pass before any consumer migration.
  • Environment-injection tests replace save/mutate/restore env dances with constructed fakes; warnings are asserted from the report interface, not console interception.

Out of Scope

  • Authoring skill/command/agent content (the creator agents stay).
  • The publish flow beyond its instruction updates (the publisher agent stays).
  • Re-scaffolding or migrating existing consumer packages in the wild — promote provides the mechanism; adoption is per-package, owner-driven.
  • Custom plugin/tool merge decisions (still routed to the plugin engineer; scaffold copies then hands off).
  • migrate/clear-cache interplay with promoted scopes (issue Placeholder: scaffold subcommand for the bunx CLI #20's stated follow-up).
  • Windows path/URI normalization for emitted file:/// snippets beyond what templates already encode.

Further Notes

  • Sources: the consolidated architecture review of Oct 9 2026 (scaffold, sync table, core carve-out) plus the Oct 8 review candidates it carries (entry predicate, config reader, editor split, environment injection, opportunistic wrapper sweep). Implements Placeholder: scaffold subcommand for the bunx CLI #20 as batch 1.
  • Architecture vocabulary throughout: module, interface, implementation, depth, seam, adapter, leverage, locality. Domain vocabulary per the project glossary (generated package, plugin registration key, scope base, manifest, deployment plan, content declaration, load-time installation).
  • The generator of this program is the repository under change; the suite's own tests are the harness for almost every item, which is why batch 1 lands first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions