Skip to content

feat: library entry + section-scoped regenerateSections - #12

Merged
ryandmonk merged 2 commits into
mainfrom
feat/regenerate-sections
Aug 3, 2026
Merged

feat: library entry + section-scoped regenerateSections#12
ryandmonk merged 2 commits into
mainfrom
feat/regenerate-sections

Conversation

@ryandmonk

Copy link
Copy Markdown
Contributor

What

The iterative rediscovery path the dspack-studio composer needs (Phase 2), at exactly the ledger's granularity. decideRegeneration's whole-file refusal table stays the answer for generate over an existing path; this adds the merge path for documents a human has enriched:

  • regenerateSections(existing, fresh) — tool-owned sections (recorded hash still matches content) refresh from the fresh document; human-owned sections (hash mismatch) are preserved verbatim with their recorded hash, so the ownership signal survives regeneration; governance and all non-generated keys carry over by construction; a ledger-less document is refused ("human-owned in full; there is no force override"). One safe additive refinement: inside a human-owned components section, fresh components with new ids are added — pure addition destroys nothing, and it covers the common rediscovery case (new component in source). Existing entries are never modified.
  • exportProject(configPath) — the programmatic generate (load → generate → schema-validate, zero writes) so the studio agent stops shelling the CLI.
  • Library entry (src/index.ts, main/exportsdist/index.js): also exports the ownership primitives (GENERATED_SECTIONS, AWAITING_AUTHORSHIP, buildLedger, sectionHash, decideRegeneration) so consumers reason with the same vocabulary. Nothing imported the package root before (bin-only usage); the bin is unchanged.

Deliberate scope boundary

Prop-level refresh of an enriched component (e.g. a new cva variant on an already-human-edited entry) is out: it would need a three-way merge base the section-level ledger does not record. The honest v1 reports and preserves; a per-component ledger is the future work if drift pressure demands it.

Verification

6 new tests pinned against real generator output over the shadcn-demo fixture: refusal without ledger, preserve+refresh split with hash-signal survival (sectionHash(components) != recorded after merge; tokens matches), pure addition, the never-modify guarantee (sparkly variant in fresh does not reach the enriched entry), missing-section flagging, clean round-trip. 111/112 total (1 pre-existing skip); build green; library entry smoke-imported.

Suggested version: 0.4.0 (new public API surface). Publishes via the existing tag-triggered OIDC workflow after merge.

🤖 Generated with Claude Code

ryandmonk and others added 2 commits August 3, 2026 17:33
The composer's iterative rediscovery path, at exactly the ledger's
granularity — decideRegeneration's invariant restated per section:
regeneration never destroys human-authored content, no force override.

- src/regenerate.ts: regenerateSections(existing, fresh) — tool-owned
  sections (recorded hash matches content) refresh from the fresh document;
  human-owned sections (hash mismatch) are preserved verbatim WITH their
  recorded hash, so the ownership signal survives; governance and every
  non-generated key carry over by construction; a document without a ledger
  is refused. One safe additive refinement: inside a human-owned components
  section, fresh components with NEW ids are added (pure addition destroys
  nothing — the common new-component-in-source case). Existing entries are
  never modified; prop-level refresh of enriched components is explicitly
  out of scope until a per-component ledger exists.
- src/index.ts library entry: exportProject(configPath) (load -> generate ->
  validate, no writes) + regenerateSections + the ownership primitives
  (GENERATED_SECTIONS, AWAITING_AUTHORSHIP, buildLedger, sectionHash,
  decideRegeneration). main -> dist/index.js, exports map added (nothing
  imported the package root before; the bin is unchanged).
- 6 new tests pinned against REAL generator output over the shadcn-demo
  fixture (refusal, preserve+refresh split, hash-signal survival, pure
  addition, no-modify guarantee, missing-section flagging, clean round
  trip). 111/112 total pass (1 pre-existing skip).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d-install boundary

- CI hardware timed out the per-test generator invocation (Babel +
  react-docgen per call); the suite now extracts ONCE and hands out clones.
- Version 0.4.0 (new public API surface: library entry + regenerateSections).
- scripts/pack-test.sh (CI-wired): from the installed tarball, exportProject
  keeps the ledger, regenerateSections preserves enrichment + governance
  with the human-owned hash signal intact, ledger-less documents fail closed
  with the no-force wording, and the bin + version resolve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ryandmonk

Copy link
Copy Markdown
Contributor Author

Final release verification (train pass)

  • 111/112 tests (CI timeout fixed: the extractor runs once per suite, not per test).
  • Packed-tarball: exportProject keeps the ledger; regenerateSections preserves enrichment + governance verbatim with the human-owned hash signal intact; ledger-less documents fail closed with the no-force wording; bin + version resolve. CI-wired as test:pack.
  • Deliberate limitation, restated: prop-level refresh of an enriched component is out of scope — it needs a three-way merge base the section-level ledger does not record. That per-component ledger question is a separate architectural decision, deliberately NOT smuggled into this release.

0.4.0 release notes:

0.4.0 — library entry + section-scoped regeneration. exportProject(configPath) (generate + validate, zero writes) and regenerateSections(existing, fresh): tool-owned sections refresh, human-owned sections and their recorded hashes are preserved (the ownership signal survives), governance carries verbatim, newly discovered components are added without touching authored entries, and ledger-less documents are refused — no force override. Ownership primitives (GENERATED_SECTIONS, AWAITING_AUTHORSHIP, buildLedger, sectionHash, decideRegeneration) are exported. The bin is unchanged.

Publish after merge: git tag v0.4.0 && git push origin v0.4.0 (OIDC).

@ryandmonk
ryandmonk marked this pull request as ready for review August 3, 2026 22:47
Copilot AI review requested due to automatic review settings August 3, 2026 22:47
@ryandmonk
ryandmonk merged commit 42bc828 into main Aug 3, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a programmatic library entrypoint and section-scoped regeneration/merge behavior to support iterative “rediscovery” workflows (e.g., dspack-studio composer) while preserving human-authored edits based on the x-bootstrap ledger.

Changes:

  • Introduces regenerateSections(existing, fresh) to refresh tool-owned sections and preserve human-owned sections (with pure-addition behavior for new component IDs).
  • Adds exportProject(configPath) as an in-process generate → validate pipeline (no filesystem writes).
  • Updates packaging/CI to publish/consume a library entry (main/exports) and adds a pack-and-install boundary test.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/tests/regenerate.test.ts Adds test coverage for section-scoped regeneration semantics against real generator output.
src/regenerate.ts Implements ledger-driven section merge logic and regeneration reporting.
src/index.ts Adds library surface exports and exportProject API.
scripts/pack-test.sh Adds a tarball install smoke test to verify published-package behavior.
package.json Bumps version to 0.4.0 and adds main/exports for the new library entry.
.github/workflows/test.yml Runs the new pack-and-install boundary test in CI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/index.ts
import type { DspackDocument } from './types.js';

export { loadConfig, type ExporterConfig, type ResolvedConfig } from './config.js';
export { generateDocument, generatedAtFromEnv, GENERATOR_VERSION, type GenerateResult } from './generate.js';
Comment thread scripts/pack-test.sh
Comment on lines +47 to +50
REPO_ROOT="$OLDPWD" node smoke.mjs

./node_modules/.bin/dspack-export --help 2>/dev/null | head -1 >/dev/null || node node_modules/.bin/dspack-export 2>/dev/null || true
node -e "const p=require('@aestheticfunction/dspack-export/package.json'); if(p.version!=='0.4.0') throw new Error('version mismatch: '+p.version); console.log('bin + version OK:', p.version)"
Comment thread src/regenerate.ts
}
const freshLedger = ledgerOf(fresh);
if (!freshLedger?.generated) {
return { ok: false, reason: 'the fresh document carries no x-bootstrap ledger; regenerate it with this tool first' };
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.

2 participants