Skip to content

Spec #44: architecture program — integration branch - #51

Merged
diegohb merged 29 commits into
mainfrom
issue-44-rearch
Oct 10, 2026
Merged

diegohb merged 29 commits into
mainfrom
issue-44-rearch

Conversation

@diegohb

@diegohb diegohb commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Closes #44, #20, #45, #46, #47, #48, #49, #50.

Summary

The suite's install machinery existed twice — once in src/, once vendored into every generated package via templates — with nothing asserting the two agreed. This program carves the shared machinery into one deep core module, makes the template↔suite relationship a classified, tested table, and replaces the ~20-minute mechanical scaffolding agent pass with a CLI.

src/
├── core/                    # the deep module — exported at "./core" (ADR-0013)
│   ├── entry-predicate.ts   # one classifier: does this entry name our package
│   ├── config-reader.ts     # entries(scope, projectDir) — both jsonc strategies
│   ├── config-splicer.ts    # surgical splice (offset-preserving)
│   ├── registration-detector.ts
│   ├── manifest.ts          # io + sha256 hashes (generated format, frozen)
│   ├── environment.ts       # the only place process.env/console/fetch are read
│   ├── status-reporter.ts   # effective-version pipeline
│   └── …                    # plugin-entry, plugin-name, npm-cache, scope-base
├── scaffold/
│   ├── renderer.ts          # templates → package tree (ex-test-harness renderer)
│   ├── scaffolder.ts        # fresh mode: flags/questions → conformant tree
│   └── promoter.ts          # promote mode: copy → ensure reference → verify → consent-gated retire
└── …                        # suite adapter — consumes core at the seam
templates/                    # adapter renderings over opencode-architect/core, no longer vendored machinery
 generated package install
-  vendored src/installer.ts, config editor, name matcher, manifest io
+  import { … } from "opencode-architect/core"
+
 sync question "are templates and suite in sync?"
-  read files two at a time, hope
+  references/template-sync-table.md — byte-synced | declared-divergent | fixture-covered, all test-enforced
+
 package creation
-  ~20 min of agent file generation (source of every field deviation)
+  bunx opencode-architect scaffold --name … / --promote <path>

Policy is declared at the seam, not parameterized: each adapter (suite CLI, generated CLI) carries named constants for its scope coverage, cache modes, and status flags; the divergence manifest is the executable record. ADR-0013 records the design-twice comparison and the frozen manifest-format compatibility rule. ADR-0006 invariants and ADR-0004 mode exclusivity are preserved; hand-edited installed files keep consumer-modification protection.

Evidence

  • Before: main — 276 tests; template sync asserted for 2 pairs; no scaffold; config-entries.ts + monolithic editor; env mutated/console.warn intercepted in 4 test suites.
  • After: 313 pass / 0 fail (1250 assertions, 23 files, ~12s), tsc --noEmit clean:
Ran 313 tests across 23 files. 313 pass, 0 fail.

Rendered-fixture proof (end-to-end, generated package depends on opencode-architect/core via a node_modules junction — no registry access): typecheck through the dependency, its own test gate, v2 module contract, install --mode plugin (config + manifest asserted), status (Local: mode=plugin, should-load verdict), load-hook activation with payload hashes, uninstall — all green (tests/templates.test.ts, tests/scaffold.test.ts, tests/core.test.ts).

Merge Danger

Door: two-way

Rollback is a branch revert; no data migration — the manifest compatibility rule makes adopting (or un-adopting) the core dependency a non-event for existing manifests, and no real consumer package has been migrated (promote is the per-package, owner-driven mechanism).

Blast Radius: suite-wide

Every install/status/uninstall path on both adapters now routes through src/core; a core defect reaches all generated packages at once (that is the point — fix once, reach everywhere). Generated packages gain a file:/registry dependency on this package; consumer-modification semantics, skills-market discoverability/install parity, and copy/plugin mode exclusivity are unchanged and test-pinned.

diegohb added 29 commits October 9, 2026 08:15
src/entry-spec.ts dissolves into src/entry-predicate.ts, which also
exposes the scoped-name policy (matchesName) and path-form/file:///
resolution-backed matching (matches/matched). No behavior change yet;
consumers still hold their own matching logic.
…dicate

plugin-config's scattered entryMatches/matchesEntry/normalizeEntry and
status-reporter's inline classify-and-match fold into EntryPredicate.
Matching now applies the scoped-name policy (case folding on Windows,
canonical spec equivalence) uniformly at both seams; external behavior
is unchanged for every spec shape the suites exercise.
…icate

New entry-predicate.template.txt is byte-identical to src/entry-predicate.ts
(asserted in the sync table). The generated registration detector, the
generated installer status path, and the generated surgical editor drop
their scattered spec/path/npm parsing and match through EntryPredicate —
the same module the suite consumes — so drift between suite and generated
matching is impossible on one side only.
Fold EntryPredicate into the split config reader / registration detector
/ surgical splicer from the integration branch.

Note: this commit completes the interrupted merge of issue-44-rearch (editor split) into the predicate work.
…bpath export

Moves environment, scope bases, cache paths + cache-target removal, name
matching, entry predicate/resolver, config reader/detector/splicer/editor,
manifest io + per-file hashes (frozen generated-package format), the
loaded-version resolver, and the status/effective-version pipeline into
src/core; suite adapters import them from there. StatusReporter drops its
suite-side PACKAGE_NAME default (callers pass the package name) and its
dependency on the suite installer's manifest types.
…ncode-architect/core dependency

Dissolves the machinery templates (plugin-name, plugin-entry,
entry-predicate, manifest, registration, plugin-config); the rendered
package.json gains a local file: dependency on opencode-architect and the
remaining installer/cli/plugin-local templates declare their policy
constants and import machinery from the ./core subpath. The rendered
fixture links the dependency the way a package manager links file: specs
(no registry access) and still passes typecheck plus its own test gate and
install/status/uninstall. promote links the dependency before running the
generated install. Sync table: machinery rows dissolved, adapter rows
re-policied; packager/publisher agent docs updated.
- consent-gated retirement now also deletes source package.json,
  lockfiles, and node_modules under .opencode (only what exists,
  reported in removed/would-remove)
- payload verification compares per-file sha256 hashes from the
  manifest against on-disk content; mismatch aborts retirement
- --target sibling with an existing package (package.json/plugin.ts)
  is refused with routing to the packager/plugin-engineer merge flow
- git rev-parse failure is reported as an explicit assumed-root note
- tests: artifact retirement, consent gate for artifacts, sibling
  refusal, verifyPayload hash paths, copy-mode non-identical removal
  plan classification
- drop section-marker comments from src/core/index.ts
- renderer: substituted arrow -> function declaration; deduplicate the
  code-kind loop into CODE_KINDS
- entry-predicate: matched() consults the name policy directly instead
  of double-delegating through matchesName
@diegohb
diegohb marked this pull request as ready for review October 10, 2026 07:03
@diegohb
diegohb merged commit 54ce9eb into main Oct 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant