Skip to content

Release: v0.41.0-alpha.3#110

Merged
SisyphusZheng merged 49 commits into
mainfrom
dev
Jun 25, 2026
Merged

Release: v0.41.0-alpha.3#110
SisyphusZheng merged 49 commits into
mainfrom
dev

Conversation

@SisyphusZheng

Copy link
Copy Markdown
Member

v0.41.0-alpha.3 — Cross-Framework Web Components Integration

Mature WC Library Direct Use

  • Lit 3.3.3, Shoelace 2.20.1, Material Web 2.4.1 verified end-to-end
  • Bidirectional nesting: openElement in Lit, Lit in openElement
  • SSR preserves all third-party tags, attributes, children, slots
  • Dashed event handler support (on-sl-change, on-md-input)

Pure ESM / ECMAScript Quality Gates

  • publint + ATTW on all 11 npm tarballs
  • Custom ESM scans: no require(), CJS globals, or host APIs in browser-facing packages

Architecture Deepening

Fixes & Refactoring

  • Alpha.2 bot review findings resolved
  • Release tooling fixes (semver pre-release, --allow-dirty, E403 skip)
  • Interop contract documentation

Closes #107, #108, #109.

DevBot and others added 30 commits June 23, 2026 11:05
- tools/autoflow/release.ts: skip release gates and deploy steps when CI=true

- .github/workflows/autoflow-release.yml: drop gates and deploy jobs; rely on branch protection

- tools/consumer-smoke.ts: include @openelement/protocol in local workspace smoke

- docs/release/v0.41.0-alpha.2-plan.md: plan pure-ESM npm quality gates for alpha.3
…red surfaces (#62)

Protocol surface and over-engineering cleanup for v0.41.0-alpha.1-cleanup patch.

- Removes speculative protocol types while keeping protected surface intact.
- Preserves SignalEngine abstraction per ADR-0109 P2.
- Flattens OpenElementBuildContext phase tokens.
- Removes dead DSD report pipeline, merges ssg-i18n into ssg-dynamic.
- Cleans content nav writer exports and architecture contract less* guard.
- Replaces tools/lib/walk.ts with @std/fs/walk.

All local release gates pass (autoflow:ci 21/21).
- Generate ui manifest at build/test/pack time; remove runtime Deno.* usage.
- Flatten lifecycle helpers (start/stop/transition) into app authoring.
- Break core intra-package cycle via BindingRenderer interface.
- Router data-context uses render-scoped stack.
- Move app Vite plugin to @openelement/adapter-vite; make Preact adapter dynamic/peer.

Co-authored-by: DevBot <devbot@openelement.dev>
)

cleanup(verifiability): release hygiene and type verification (#61)
fix: post-audit fixes for alpha.1-cleanup review findings (#65-#74) plus CI typecheck fix
* fix(core/content/ssg): address deferred alpha.1 review findings (#81)

- Standardize import extensions to .js across packages/core/src

- Remove nodeType === 0 production coupling; set fake fragment nodeType to 11 in tests

- Restore globalThis.document after binding-activation and jsx-render-dom tests

- Add content-helpers.test.ts for write-json, sitemap/plugin, fs-adapter

- Import only cwd from node:process in ssg-render and build-postprocess

* fix(content,core): address PR #84 robot nits

- Import BufferEncoding via NodeJS namespace in fs-adapter.ts so the cast
  is type-safe without relying on a non-existent 'node:buffer' export.
- Add comments to the restore-global-document tests documenting that they
  must remain the last test in each file.

---------

Co-authored-by: DevBot <devbot@openelement.dev>
…heet (#86)

Safari/WebKit does not support :host-context, so the dark-mode token
rule was dropped entirely in shadow roots that adopted
openPropsTokenSheet. Remove :host-context from the host sheet and add
openPropsRootSheet, a :root variant for document-level adoption that
cascades into every shadow DOM via custom-property inheritance.

Closes #80

Co-authored-by: DevBot <devbot@openelement.dev>
* feat(ui): validate static subpath with open-button (#79)

- Migrate open-button escapeAttr import from @openelement/core to @openelement/core/static

- Add packages/ui/__tests__/static-subpath.test.ts

* test(create): add @openelement/core/static alias in generated project test

open-button now imports from @openelement/core/static; the create CLI
workspace-test harness must alias that subpath or the generated project
build fails during SSR bundle resolution.

* fix(create): add @openelement/core/static alias to consumer-local.ts

---------

Co-authored-by: DevBot <devbot@openelement.dev>
…/core/hydrate (#83)

* feat(core/element): extract HydrationScope to @openelement/core/hydrate (#76)

- Create HydrationScope value object in packages/core/src/hydration-scope.ts

- Export HydrationScope from @openelement/core/hydrate

- Refactor OpenElement to own a single #hydrationScope

- Refactor open-element-hydration.ts and open-element-render.ts to operate on scope

- Add hydration-scope.test.ts with fake DOM and isolation tests

- Update subpath-exports.test.ts for HydrationScope export

- Update STATUS.md and VERSION_PLAN.md for v0.41.0-alpha.2

* fix(core,element): address HydrationScope robot review findings

- Add HydrationScope.reset() to clear effects/events without deactivating
  the scope; use it before re-render/re-hydrate.
- Fix critical bug in hydrateExistingDom: scope.dispose() was called before
  scope.hydrate(), which made hydrate() a no-op. Replace with scope.reset().
- Use scope.dispose() in OpenElement.disconnectedCallback for true lifecycle
  cleanup instead of disposeRenderBindings().
- Remove duplicate disposeRenderBindings() helper; disposal now lives in the
  scope.
- Remove unused disposeScope() helper and VNodeCacheAccess type export.
- Drop unused render callback from OpenElement's HydrationScope.
- Normalize import extension in hydrate.ts (.ts -> .js).

* fix(hydration): fmt/lint/arch and consumer-local alias for HydrationScope

* style: format hydration-scope.test and open-element-render for fmt:check

* ci: trigger rebuild after format fixes

---------

Co-authored-by: DevBot <devbot@openelement.dev>
)

- jsx-render-dom.ts now emits BindingDescriptors and commits them via commitBindings()
- Move Show/For effect lifecycle into binding-activation.ts as conditional/list bindings
- Add commitBindings export to core/csr/hydrate entry points

Co-authored-by: DevBot <devbot@openelement.dev>
- Add factory constructors for all descriptor kinds in binding-descriptor.ts
- Replace central switch in applyBindingDescriptor with internal Map registry
- Add registerBindingKind for custom binding kinds
- Update jsx-render-dom.ts, hydration-scope.ts, event-hydration.ts to use factories
- Re-export factories and registerBindingKind from core/csr/hydrate entry points
- Add custom binding kind test

Co-authored-by: DevBot <devbot@openelement.dev>
- Cache cacheAccess getter in private field to fix object identity
- Remove dead VNodeCacheAccess.get() method
- Mark VNodeCacheAccess and cacheAccess as @internal
- Normalize import extensions to .js in hydration-scope.ts
- Fix --blue-10 -> --teal-10 typo in dark-mode token block

Co-authored-by: DevBot <devbot@openelement.dev>
…90)

* fix(core): binding layer cleanup - factories, registry, types (#90)

- Remove _root dead param from commitBindings
- Add bindHtml factory for signal-html descriptors
- Add overwrite warning to registerBindingKind
- Remove dead keyFn field from ListBindingDescriptor
- Narrow condition type to Signal<boolean> | boolean
- Narrow items type to Signal<unknown[]> | unknown[]
- Use bindHtml factory in collectPropBindings
- Re-export bindHtml from core/csr/hydrate entry points

* docs: add comments documenting narrowed type casts

* fix(core): revert narrowed factory types per bot review

Widen bindConditional/bindList factory signatures back to
Signal<unknown> | unknown to match runtime coercion in applyConditional/applyList

---------

Co-authored-by: DevBot <devbot@openelement.dev>
* chore: bump versions to 0.41.0-alpha.2 and update roadmap (#82)

- Bump all 11 packages from 0.41.0-alpha.1 to 0.41.0-alpha.2
- Mark v0.41.0-alpha.2 as Shipped in ROADMAP.md
- Add completion note

* fix: update PACKAGE_VERSION and docs for alpha.2 (#82)

- Bump PACKAGE_VERSION in tools/project-constants.ts to 0.41.0-alpha.2
- Update README.md, README.zh.md, PROJECT_WORKFLOW.md with alpha.2

* fix: update ACTIVE_VERSION and www route version refs for alpha.2

* fix: update getting-started.tsx with alpha.2 version ref

---------

Co-authored-by: DevBot <devbot@openelement.dev>
* docs: fix stale version refs in ROADMAP header, README Current Line, PACKAGE_SURFACE, design README

* docs: fix remaining stale version refs and PACKAGE_SURFACE protocol row

- Update OPENELEMENT_VERSION constant to v0.41.0-alpha.2
- Fix hardcoded alpha.1 strings in architecture, apilist, examples routes
- Add @openelement/protocol to PACKAGE_SURFACE table (11-package)

* docs: fix two more stale v0.41.0-alpha.1 refs

* docs: fix 14-to-10 -> 14-to-11 in PACKAGE_SURFACE.md

---------

Co-authored-by: DevBot <devbot@openelement.dev>
- Remove dead _hydrateSignals() method and unused hydrateSignals import
- Fix inconsistent DOM clearing: shadow root now clears before null check
- Import eventTypeFromProp from event-marker.js instead of event-hydration.js
- Remove dead 'key' field from StaticAttrBindingDescriptor interface and factory
- Remove dangling comment in protocol/manifest.ts
- Update stale comments referencing _hydrateSignals

Co-authored-by: DevBot <devbot@openelement.dev>
1. CSR className/class props use bindAttr (signal-attr) instead of bindClass
2. collectHydrationBindings accepts lifecycle param directly
3. Make VNodeCacheAccess non-exported (file-private)
4. Add HydrationScope.createLifecycle() method
5. Remove duplicate bindHydrateEvents re-export from hydrate.ts
6. Restore framework.js barrel in protocol/src/index.ts
7. Document AbortSignal absence in open-element-render.ts
8. Make SignalRenderBindingDescriptor.lifecycle optional
9. Reject null bytes in resolveDynamicRoutePath
10. Replace ponytail comment with normal comment
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>
- Collapse identical if/else branches in collectPropBindings
- Remove unused _effectDisposers / _eventCleanups getters
- Document SignalClassBindingDescriptor as DSD-only
…t, constructor init

- reset() no longer gates on #active so reconnection can re-hydrate
- registerDispose uses AbortSignal OR disposers Set, not both (avoid double-dispose)
- Drop @internal from DANGEROUS_KEYS barrel export
- Move HydrationScope construction into OpenElement constructor with super() call
…vate, hoist inline import

- Replace cacheAccess getter + VNodeCacheAccess interface with setCachedVNode method
- Remove export from hydrateSignals (only used in same module)
- Hoist Signal import type in open-element-render.ts to top level
- Make bindRender lifecycle parameter required (no default {})
- reset() now sets #active = true so disconnect/reconnect can re-hydrate
- Add comment documenting test mock ordering requirement
DevBot added 5 commits June 25, 2026 11:09
- Move inline fixture strings to standalone files under tools/third-party-wc-smoke/app/
- Add allPackageAliases() to tools/lib/package-graph.ts
- Use allPackageAliases() in both third-party-wc-smoke.ts and consumer-local.ts
- Reduces smoke test from 551 → 253 lines (54% reduction)
- #107: Create @openelement/app/vite as canonical Vite entry
- #108: Deepen BuildContext with recordPhase* and registerPlugin methods
- #109: Consolidate test DOM harness into test-utils.ts (-619 lines)
- Update alpha.3 plan checkboxes to mark all workstreams complete
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 25, 2026

Copy link
Copy Markdown

Deploying openelement with  Cloudflare Pages  Cloudflare Pages

Latest commit: fb8265d
Status: ✅  Deploy successful!
Preview URL: https://b1b4fa8c.lessjs.pages.dev
Branch Preview URL: https://dev.lessjs.pages.dev

View logs

@github-actions

Copy link
Copy Markdown
Contributor

Now I have a thorough understanding of all 42 changed files. Here's my review.


PR Review: v0.41.0-alpha.3 — Cross-Framework Web Components Integration

Bugs

1. markComplete has inverted phase ordering (build-context.ts:204-205)

if (phase === 2 && !this.completed.has(3)) {
  throw new Error('Phase 2 requires Phase 3 to be completed first');
}

Phase 2 is the client island build, Phase 3 is SSG rendering. Phase 2 cannot require Phase 3 — that's backwards. The intent reads like Phase 3 requires Phase 2 (SSG rendering depends on the client bundle being built). This condition should either be removed or corrected to !this.completed.has(2) inside the phase-3 check.

2. stripComments can mangle code containing // after block comments (check-package-artifacts.ts:75-84)

After stripping /* ... */, the concatenated result may produce spurious // sequences (e.g. http://example.com becomes http: + //example.com). The line.replace(/\/\/.*/) then swallows legitimate code. This can cause both false positives and false negatives in the CJS/host-API scans.

3. Module-level global mutation leaks across test files (jsx-render-dom.test.ts:11-12)

const _savedDocument = (globalThis as ...).document;
(globalThis as ...).document = new TestDocument();

The mock document is set at module scope and only restored on line 235 ("this test must remain the last one"). If any earlier test in this file throws, or if test ordering changes, the global is permanently corrupted for all subsequent test files in the Deno runner. Use withMockDocument() from test-utils.ts instead, or at minimum wrap in a try/finally.

Over-engineering / Unnecessary Abstraction

4. recordPhase*Fact methods are unused thin wrappers (build-context.ts:182-195)

These three one-liner methods do nothing beyond direct assignment — yet plugin.ts and build-ssg.ts still mix direct field mutation (ctx.phase1.islandTagNames = ...) with the new methods (ctx.recordPhase1Fact('honoEntryCode', ...)). Either commit to them fully across all call sites or remove them. As is, they add API surface without any consistency benefit.

5. registerPlugin bypasses type safety (build-context.ts:197-199)

registerPlugin(name: string, instance: unknown): void {
  this.plugins[name] = instance;
}

The plugins record has a strongly-typed shape (blogOptions, navSections, i18nOptions, etc.). Using unknown discards that contract for anything registered via this method. A generic constrained to keyof the plugin record would retain safety.

6. Fragile path slicing in cleanup (check-package-artifacts.ts:234)

await Deno.remove(packageRoot.slice(0, packageRoot.length - '/package'.length), { recursive: true });

String-length arithmetic for path manipulation is brittle. Use import { dirname } from 'node:path' or Deno.dirname-equivalent instead.

Readability / Maintenance

7. Hardcoded version anchors make release brittle (autoflow/release.ts:364-409)

updateCurrentVersionAnchors contains exact version strings like v0.40.8. When these drift in source files between releases, the function will throw does not contain expected version anchor. These should be parameterized with the currentVersion argument that's already available.

8. HOST_PATTERNS regex for process is too broad (check-package-artifacts.ts:35)

/\bprocess\b/ matches any occurrence of the word "process" (variable names, string literals, JSDoc). This will produce false positives on any code that has comments or variables containing "process". A more targeted check (e.g., typeof process !== 'undefined' or import.*process) would reduce noise.

9. consumer-local.ts helper placement (consumer-local.ts:463-509)

findMissingGeneratedImports, extractBareImportSpecifiers, isBareSpecifier, isMappedSpecifier are declared at the file bottom after Deno.exit() calls. While JS hoisting makes this valid, it's confusing to read. Move to the top of the file, or at minimum before the main control flow.

Style / Nits

10. DASHED_EVENT_PROP_RE is case-sensitive (event-marker.ts:16)

/^on-[a-z].../ won't match on-Sl-Change. This is likely intentional (HTML attributes are lowercase), but worth confirming the contract is documented somewhere, since the HTML spec says attribute names are case-insensitive by default.

11. Unnecessary empty onListen callback (third-party-wc-smoke.ts:96)

return Deno.serve({ port: 0, onListen: () => {} }, async (request) => {

The onListen callback does nothing. The empty function can be omitted entirely — Deno.serve({ port: 0 }, handler) works without it.

12. #__type: 'RegExp' serialization in Phase3Meta.ssrNoExternal (build-context.ts:120)

The { __type: 'RegExp'; source: string; flags: string } custom serialization for noExternal patterns is clever but fragile. If someone passes a RegExp to populatePhase3, it won't be serialized — it'll just be the RegExp itself. Consider a normalize step or using Vite's built-in RegExp serialization if available.

Summary

Priority Count Key Items
Bugs 3 Inverted phase check in markComplete, stripComments URL mangling, global state leak in tests
Over-engineering 3 Unused recordPhase*Fact wrappers, unknown type in registerPlugin, fragile path slicing
Readability 3 Hardcoded version anchors, broad regex, helper function placement
Nits 3 Case-sensitive event regex, empty callback, RegExp serialization

The substantive architecture work (WC interop, ESM gates, test consolidation) looks solid. The main risk items are the inverted phase ordering check (will cause runtime failures if markComplete is ever called in normal builds) and the stripComments edge case that could hide real artifacts during scanning.

New%20session%20-%202026-06-25T04%3A09%3A15.322Z
opencode session  |  github run

- Remove inverted phase ordering check in markComplete
- Fix stripComments URL mangling in check-package-artifacts
- Replace module-level document mutation with setupMockDocument()
- Remove unused recordPhase*Fact wrappers
- Tighten registerPlugin generic type
- Replace fragile path slicing with dirname()
@github-actions

Copy link
Copy Markdown
Contributor

Here's my review, verified against the actual source files:


PR Review: v0.41.0-alpha.3

Disposition: Mostly clean. One real bug, several readability issues worth fixing. Several bot review findings were already addressed in the actual code (markComplete ordering, stripComments URL mangling, registerPlugin typing, fragile path slicing are all correct).

Bugs

1. Module-level setupMockDocument() leaks global statepackages/core/__tests__/jsx-render-dom.test.ts:11

setupMockDocument() is called at module scope and never restored. The file includes withMockDocument in the imports from test-utils.ts but doesn't use it. Other test files (binding-activation.test.ts, hydration-scope.test.ts) use withMockDocument correctly. Under --shuffle, tests from other files running between this file's tests will restore globalThis.document to the real value, breaking this file's tests. Wrap all tests in withMockDocument or refactor to Deno.test({ fn: () => withMockDocument(...) }).

Readability / Maintenance

2. Hardcoded version anchors will silently miss targetstools/autoflow/release.ts:366-409

updateCurrentVersionAnchors hardcodes 0.40.8/v0.40.8 in every from string. These must be manually bumped on every release cycle before the function takes effect. If a source file still has v0.40.8 but the function is never updated to match the next release's "previous" version, it silently skips that file. Extract the previous version as a parameter.

3. Helper functions declared after control flowtools/consumer-local.ts:463-509

findMissingGeneratedImports, extractBareImportSpecifiers, isBareSpecifier, isMappedSpecifier are defined on lines 463–509, after Deno.exit(exitCode) on line 461. Hoisting makes this valid, but reading top-to-bottom it appears unreachable. Move before the main control flow.

Nits

4. Empty onListen callbacktools/third-party-wc-smoke.ts:96

Deno.serve({ port: 0, onListen: () => {} }, async (request) => {

The no-op callback is unnecessary. Deno.serve({ port: 0 }, handler) works identically.

5. ssrNoExternal RegExp serializationpackages/adapter-vite/src/build-context.ts:120

The { __type: 'RegExp'; source: string; flags: string } type is only enforced at the type level. Passing an actual RegExp instance to populatePhase3 compiles fine but won't serialize correctly downstream. A runtime normalization step or Zod-like validation would catch misuse.

Summary

Severity Count Key Items
Bug 1 Global mock document leak in jsx-render-dom.test.ts
Maint. 2 Hardcoded version anchors, helper placement
Nit 2 Empty callback, RegExp serialization

The architecture work (BuildContext deepening, WC interop smoke tests, ESM gates, test consolidation into test-utils.ts) is solid. The check-package-artifacts.ts tool with its test coverage is well done. The only actionable bug is the test isolation issue.

New%20session%20-%202026-06-25T04%3A17%3A54.396Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown
Contributor

All files reviewed. Here's my independent assessment. Earlier bot reviews had several false positives; the actual code is cleaner than reported.


PR Review: v0.41.0-alpha.3

Disposition: The architecture is solid. One real edge-case bug in the comment scanner, two maintainability issues, and a few nits.

Bugs

1. stripComments can mangle code when // follows a stripped block commenttools/check-package-artifacts.ts:82-84

return source
  .replace(/\/\*[\s\S]*?\*\//g, (match) => ' '.repeat(match.length))
  .replace(/\/\/.*/g, '');

After block-comment replacement with spaces, a line like const x = 'http:' /*cmt*/ + '//path' becomes const x = 'http:' + '//path', and the //.* regex then strips '//path', corrupting the line for subsequent pattern matching. Very low likelihood in practice, but it can both hide real issues and create false negatives in the CJS/host-API scan. A more robust approach would track string literals or skip // that appear inside quotes.

Readability / Maintenance

2. Hardcoded version anchors will silently miss targetstools/autoflow/release.ts:366-409

Every from string in updateCurrentVersionAnchors hardcodes 0.40.8/v0.40.8. The function receives version as a parameter but uses it only in to strings. On each release the from values must be manually bumped. If they drift, the function silently skips replacements rather than failing. Extract a previousVersion parameter or compute from strings from the input.

3. Helper functions declared after Deno.exit()tools/consumer-local.ts:461-509

findMissingGeneratedImports, extractBareImportSpecifiers, isBareSpecifier, isMappedSpecifier are defined on lines 463-509, after cleanup() and if (exitCode !== 0) Deno.exit(exitCode) on lines 458-461. While hoisting makes this valid JS, reading top-to-bottom suggests the helpers are unreachable. Move them before the main control flow.

Nits

4. Unnecessary empty onListen callbacktools/third-party-wc-smoke.ts:96

Deno.serve({ port: 0, onListen: () => {} }, async (request) => {

Deno.serve({ port: 0 }, handler) works without the no-op callback. Remove it.

5. ssrNoExternal RegExp serialization is type-onlypackages/adapter-vite/src/build-context.ts:120

The { __type: 'RegExp'; source: string; flags: string } type in Phase3Meta.ssrNoExternal enforces the shape at the type level only. Passing an actual RegExp instance compiles fine but wouldn't serialize correctly downstream. Consider a runtime normalization in populatePhase3 that converts RegExp instances to the plain-object form.

Previously-reported issues that are already correct

  • markComplete phase ordering: Code correctly requires Phase 1 before Phase 2 or Phase 3. The test at build-context.test.ts:117-131 validates this. No inversion bug.
  • registerPlugin typing: Uses registerPlugin<K extends keyof...> with proper generics (build-context.ts:198). Not unknown.
  • Module-level mock document leak: jsx-render-dom.test.ts wraps all tests in withMockDocument() via the it() helper. No global leak.
  • Path slicing: Uses dirname() from node:path, not string arithmetic (check-package-artifacts.ts).

Summary

Severity Count Key Items
Bug 1 stripComments edge case (very low risk)
Maint. 2 Hardcoded version anchors, helper placement after exit
Nit 2 Empty onListen, RegExp serialization

The core work — BuildContext deepening, WC interop smoke tests, ESM artifact gates, and test consolidation into test-utils.ts — is well-executed.

New%20session%20-%202026-06-25T04%3A22%3A35.802Z
opencode session  |  github run

app→adapter-vite→app creates a cycle that breaks graph:check.
Keep adapter-vite as the canonical Vite entry; update docs accordingly.
@github-actions

Copy link
Copy Markdown
Contributor

Here's my independent review of the current code (post-fix commits 05aa46a and 1059f64):


PR Review: v0.41.0-alpha.3

Disposition: Clean. The 05aa46a fix commit already addressed the substantive bot findings (inverted phase check, stripComments URL mangling, module-level mock leak, recordPhase*Fact wrappers, registerPlugin typing, path slicing). What remains are two maintainability nits and one readability issue.

Bugs

None. markComplete ordering (build-context.ts:186-196) is correct — Phase 2 and Phase 3 both require only Phase 1, per the ADR 0023 comment. jsx-render-dom.test.ts properly wraps every test in withMockDocument() via the it() helper (line 11-13). stripComments uses space-padding to avoid URL mangling (check-package-artifacts.ts:76).

Readability / Maintenance

1. Helper functions declared after Deno.exit()tools/consumer-local.ts:463-509

findMissingGeneratedImports, extractBareImportSpecifiers, isBareSpecifier, isMappedSpecifier are defined on lines 463–509, after cleanup() and Deno.exit(exitCode) on lines 460-461. Hoisting makes this valid, but reading top-to-bottom it reads as unreachable dead code. Move these four functions before the main control flow (before line 32).

2. Hardcoded version anchors require manual bump per releasetools/autoflow/release.ts:366-409

Every from string hardcodes 0.40.8/v0.40.8. After the first run, the source files contain the new version, and subsequent runs will throw because the hardcoded anchor no longer matches. This self-documents the need to bump (it won't silently skip), but extracting a previousVersion parameter would remove a manual step from the release checklist.

Nits

3. Empty onListen callbacktools/third-party-wc-smoke.ts:96

Deno.serve({ port: 0, onListen: () => {} }, async (request) => {

Deno.serve({ port: 0 }, handler) works identically without the no-op.

4. ssrNoExternal RegExp shape is type-onlypackages/adapter-vite/src/build-context.ts:120

The { __type: 'RegExp'; source: string; flags: string } type is enforced at compile time but not runtime. Passing an actual RegExp instance to populatePhase3 compiles fine but won't serialize correctly downstream. A runtime normalizer in populatePhase3 (converting instanceof RegExp to the plain-object form) would make the API more robust.

Summary

Severity Count Key Items
Bug 0
Maintenance 2 Helper placement after exit, hardcoded version anchors
Nit 2 Empty onListen, RegExp serialization is type-only

The cross-framework WC integration, check-package-artifacts.ts ESM gate, BuildContext deepening, and test consolidation into test-utils.ts (-619 lines) are well-executed.

New%20session%20-%202026-06-25T04%3A30%3A02.011Z
opencode session  |  github run

DevBot added 3 commits June 25, 2026 12:32
- Document stripComments edge case as extremely unlikely
- Remove empty onListen callback in smoke test
- Move helper functions before control flow in consumer-local.ts
@github-actions

Copy link
Copy Markdown
Contributor

PR Review: v0.41.0-alpha.3

Disposition: Clean. The architecture is solid, and all substantive issues from the earlier bot reviews have been addressed in the actual code. Two maintainability nits remain.


Bugs

None. All previously-flagged issues are already correct in the code:

  • markComplete (build-context.ts:186-196): Phase 2 and 3 both correctly require only Phase 1 — the phase order is documented per ADR 0023.
  • stripComments (check-package-artifacts.ts:74-82): Space-padding prevents // concatenation across comment boundaries.
  • jsx-render-dom.test.ts: Every test is wrapped in withMockDocument() via the it() helper (line 11-13).
  • registerPlugin (build-context.ts:178): Uses proper generic K extends keyof....
  • Path slicing (check-package-artifacts.ts:227): Uses dirname() from node:path.

Readability / Maintenance

1. Helper functions declared after their first call sitetools/consumer-local.ts:456-502

findMissingGeneratedImports, extractBareImportSpecifiers, isBareSpecifier, isMappedSpecifier are function declarations defined on lines 456–502, but first called on line 244. Hoisting makes this valid, but reading top-to-bottom the code appears to call undefined functions. Move them above the main control flow (before line 59).

2. Hardcoded version anchors require bump per releasetools/autoflow/release.ts:366-409

Every from string in updateCurrentVersionAnchors hardcodes 0.40.8/v0.40.8. The function does have a guard (throws if the anchor isn't found AND the target isn't already present), so it won't silently skip — but extracting a previousVersion parameter would eliminate a manual step from the release checklist.

Nits

3. ssrNoExternal RegExp shape is type-onlypackages/adapter-vite/src/build-context.ts:120

ssrNoExternal: (string | { __type: 'RegExp'; source: string; flags: string })[] = [];

Passing an actual RegExp instance to populatePhase3 compiles fine but won't serialize correctly downstream. Consider a runtime normalization (instanceof RegExp → plain object) in populatePhase3.

Summary

Severity Count Key Items
Bug 0
Maint. 2 Helper placement after first call, hardcoded version anchors
Nit 1 RegExp serialization is type-only

The core work — WC interop smoke tests, ESM artifact gates (check-package-artifacts.ts + tests), BuildContext deepening, test harness consolidation into test-utils.ts (-619 lines) — is well-executed and well-tested.

New%20session%20-%202026-06-25T04%3A33%3A15.620Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown
Contributor

Here's my review after reading the actual source files (focusing on the diff):

PR Review: v0.41.0-alpha.3

Disposition: Clean. All substantive bugs flagged in earlier bot reviews are already correct in the code. Two maintainability issues and one nit remain.

Bugs

None. Verified against source:

  • markComplete (build-context.ts:191-195): Phase 2/3 correctly require only Phase 1. Test at build-context.test.ts:100-118 validates this.
  • stripComments (check-package-artifacts.ts:79-81): Space-padding prevents // concatenation across comment boundaries.
  • jsx-render-dom.test.ts:11-13: Every test wraps via withMockDocument() through the it() helper.
  • registerPlugin (build-context.ts:180): Uses proper generic K extends keyof....
  • Path slicing (check-package-artifacts.ts:227): Uses dirname() from node:path.
  • third-party-wc-smoke.ts:96: No empty onListen callback present — uses { port: 0 } cleanly.

Readability / Maintenance

1. Hardcoded version anchors require manual bump per releasetools/autoflow/release.ts:366-409

Every from string hardcodes 0.40.8/v0.40.8. The function has a guard (throws at line 422-424 if the anchor isn't found AND the target isn't already present), so it won't silently skip — but extracting a previousVersion parameter would eliminate a manual step from the release checklist. The self-documenting comment on lines 366-369 acknowledges this.

2. Helper functions declared after exit logictools/consumer-local.ts:456-502

findMissingGeneratedImports, extractBareImportSpecifiers, isBareSpecifier, isMappedSpecifier are defined after cleanup() (line 508) and Deno.exit(exitCode) (line 509). Hoisting makes this valid, but reading top-to-bottom it appears unreachable. Move them before the main control flow (before line 59).

Nit

3. ssrNoExternal RegExp shape is type-onlypackages/adapter-vite/src/build-context.ts:120-122

ssrNoExternal: (string | { __type: 'RegExp'; source: string; flags: string })[] = [];

Passing a real RegExp instance to populatePhase3 compiles fine but won't serialize correctly downstream. Consider a runtime normalization in populatePhase3.

New%20session%20-%202026-06-25T04%3A34%3A06.367Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown
Contributor

Here's my review against the actual source files (previous bot reviews had several false positives I've verified):


PR Review: v0.41.0-alpha.3

Disposition: Clean. No bugs found. The cross-framework WC integration, ESM artifact gate, BuildContext deepening, and test consolidation (-619 lines into test-utils.ts) are well-executed. Three maintainability nits and two code organization issues.

Previously-Reported Issues That Are Already Correct

All three prior reviews flagged items that don't exist in the source:

  • markComplete phase ordering (build-context.ts:188-198): Phase 2 and Phase 3 both require only Phase 1. ADR-0023 comment on line 190 explains Phase 2 runs after Phase 3. Test validates this at build-context.test.ts:100-118.
  • stripComments URL mangling (check-package-artifacts.ts:74-82): Block comments are replaced with same-length spaces before line-comment stripping, explicitly preventing false // sequences across comment boundaries (documented in comment lines 68-72).
  • registerPlugin typing (build-context.ts:180-185): Uses K extends keyof OpenElementBuildContext['plugins'], not unknown.
  • Module-level mock leak (jsx-render-dom.test.ts:11-13): Every test is wrapped in withMockDocument() via the it() helper.
  • Path slicing (check-package-artifacts.ts:227): Uses dirname() from node:path, not string arithmetic.
  • Empty onListen callback: Already absent from the code (third-party-wc-smoke.ts:96).

Maintainability

1. Helper functions declared after Deno.exit() callstools/consumer-local.ts:456-501

findMissingGeneratedImports, extractBareImportSpecifiers, isBareSpecifier, isMappedSpecifier are declared at lines 456-501, interleaved with control flow code and after calls like Deno.exit(exitCode) on line 509. All four are called before their declarations (e.g. findMissingGeneratedImports at line 244). Hoisting makes this valid but reading top-to-bottom suggests dead code. Move these four functions to line 12, before const repoRoot.

2. Hardcoded version anchors require manual bump per releasetools/autoflow/release.ts:366-409

Every from string hardcodes 0.40.8/v0.40.8. The function receives version as a parameter but uses it only for to strings. The guard at line 415-420 throws when the anchor isn't found (won't silently skip), but releasing v0.42.0 would require editing this function first. Extract previousVersion as a parameter or compute it from version (previous minor/patch).

Type-Safety Gap

3. OpenElementBuildContextLike.registerPlugin uses unknownpackages/protocol/src/framework.ts:70

registerPlugin(name: string, instance: unknown): void;

The concrete implementation (build-context.ts:180) uses K extends keyof... for type-safe registration. The protocol interface discards this safety. While callers always use the concrete type at runtime, tightening this to at least Record<string, unknown> would reduce the mismatch:

registerPlugin<K extends keyof OpenElementBuildContextLike['plugins']>(
  name: K, instance: OpenElementBuildContextLike['plugins'][K]
): void;

Nits

4. TestShadowRoot.querySelectorAll only matches [attrName] selectorspackages/core/__tests__/test-utils.ts:435-447

The implementation strips brackets and matches only attribute presence, unlike real DOM querySelectorAll. Adequate for current tests but will silently return wrong results for tag selectors or [attr=value]. Add a comment documenting the limitation.

5. ssrNoExternal RegExp shape is type-onlypackages/adapter-vite/src/build-context.ts:120-122

Passing an actual RegExp to populatePhase3 compiles fine but won't serialize correctly downstream. A runtime normalizer (e.g., item instanceof RegExp ? { __type: 'RegExp', source: item.source, flags: item.flags } : item) at populatePhase3:209 would catch misuse.

Summary

Severity Count Key Items
Bug 0
Maintenance 2 Helper placement after exit in consumer-local.ts, hardcoded version anchors in release.ts
Type Safety 1 OpenElementBuildContextLike.registerPlugin uses unknown
Nit 2 Limited TestShadowRoot.querySelectorAll, type-only ssrNoExternal RegExp

The architecture is solid. Approve as-is; the maintainability items are low-priority improvements for the next cycle.

New%20session%20-%202026-06-25T04%3A33%3A40.991Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown
Contributor

PR Review: v0.41.0-alpha.3

Disposition: Clean. All substantive bugs flagged in earlier bot reviews are already correct in the source. Two maintainability items and two nits remain.

Previously-Reported Issues — Verified Correct

  • markComplete phase ordering (build-context.ts:191-195): Phase 2 and Phase 3 both require only Phase 1, per the ADR-0023 comment. No inversion bug.
  • stripComments URL mangling (check-package-artifacts.ts:74-81): Space-padding prevents // concatenation across comment boundaries; documented inline.
  • Module-level global state leak (jsx-render-dom.test.ts:11-13): Every test is wrapped via the it() helper calling withMockDocument().
  • registerPlugin typing (build-context.ts:180-185): Uses proper generic K extends keyof....
  • Path slicing (check-package-artifacts.ts:227): Uses dirname() from node:path.
  • Empty onListen callback: Already absent (third-party-wc-smoke.ts:96 uses { port: 0 } cleanly).

Maintenance

1. Hardcoded version anchors require manual bump per releasetools/autoflow/release.ts:366-409

Every from string hardcodes 0.40.8/v0.40.8. The self-documenting comment on lines 366-369 acknowledges this and the guard throws when anchors aren't found, so it won't silently skip — but extracting a previousVersion parameter would eliminate a manual step from the release checklist.

2. Helper functions declared after first call sitetools/consumer-local.ts:456-502

findMissingGeneratedImports, extractBareImportSpecifiers, isBareSpecifier, isMappedSpecifier are declared at lines 456-502 but first called at line 244. Hoisting makes this valid, but reading top-to-bottom the code appears to call undefined functions. They are, however, before the cleanup()/Deno.exit() block at lines 504-509 (earlier bot reviews were incorrect about this). Move them above the main control flow (before line 59).

Type Safety

3. Protocol registerPlugin signature is weaker than implementationpackages/protocol/src/framework.ts:70

registerPlugin(name: string, instance: Record<string, unknown>): void;

The concrete class uses K extends keyof OpenElementBuildContext['plugins'] for full type safety. The protocol interface discards this, meaning consumers relying on the interface type won't get key-checked plugin registration.

Nits

4. ssrNoExternal RegExp shape is type-onlypackages/adapter-vite/src/build-context.ts:119-122

The JSDoc warns callers to use { __type: 'RegExp', source, flags } rather than RegExp instances, but passing a real RegExp compiles fine and will fail silently at runtime. Consider adding a normalization step in populatePhase3 (line 217) to convert instanceof RegExp to the plain-object form.

Summary

Severity Count Items
Bug 0
Maintenance 2 Hardcoded version anchors (release.ts), helper placement after first call (consumer-local.ts)
Type Safety 1 registerPlugin protocol/impl mismatch
Nit 1 ssrNoExternal RegExp is type-only

The core work — WC interop smoke tests, ESM artifact gates, BuildContext deepening, test harness consolidation (-619 lines into test-utils.ts) — is well-executed and well-tested. Approve as-is; the maintainability items are low-priority for the next cycle.

New%20session%20-%202026-06-25T04%3A37%3A12.479Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown
Contributor

Now I have a thorough understanding of the code. Here's my independent review:


PR Review: v0.41.0-alpha.3

Disposition: Clean. The architecture work is solid — WC interop, ESM artifact gates, BuildContext, and test consolidation (-619 lines into test-utils.ts) are all well-executed. Several earlier bot reviews flagged false positives that are already correct in the source.

Verified False Positives from Prior Bot Reviews

  • markComplete phase orderingbuild-context.ts:188-196 correctly enforces Phase 1 before Phase 2/3. Test validates it.
  • stripComments URL manglingcheck-package-artifacts.ts:74-81 uses same-length space replacement to prevent // concatenation across comment boundaries.
  • Module-level state leakjsx-render-dom.test.ts:11-13 wraps every test via withMockDocument() through the it() helper.
  • registerPlugin typingbuild-context.ts:180-185 uses proper K extends keyof... generic.
  • Fragile path slicingcheck-package-artifacts.ts:227 uses dirname() from node:path.
  • Empty onListenthird-party-wc-smoke.ts:96 uses { port: 0 } cleanly, no bootstrap.
  • Helper placement after exitconsumer-local.ts:19-65 defines helpers at the top, before main control flow.

Maintainability

1. Hardcoded version anchors require manual bump per releasetools/autoflow/release.ts:366-409

Every from string hardcodes 0.40.8/v0.40.8. The self-documenting comment on lines 366-369 acknowledges this, and the guard at line 419 throws if anchors are missing and targets aren't already present (no silent skip). Still, extracting a previousVersion parameter would remove a manual step from the release checklist.

2. ssrNoExternal RegExp shape is type-onlypackages/adapter-vite/src/build-context.ts:119-122

ssrNoExternal: (string | { __type: 'RegExp'; source: string; flags: string })[] = [];

The JSDoc warns callers to use the plain-object form, but passing an actual RegExp compiles fine and would fail silently at runtime. Adding a normalization step in populatePhase3 (item instanceof RegExp ? { __type: 'RegExp', source: item.source, flags: item.flags } : item) would make the API robust.

Nits

3. Protocol registerPlugin signature is weaker than implementationpackages/protocol/src/framework.ts:71

registerPlugin(name: string, instance: unknown): void;

The concrete class at build-context.ts:180 uses K extends keyof... for full type safety. The protocol interface discards this, meaning consumers coding against the protocol type get no key-checked plugin registration. Consider tightening to match the implementation contract.

4. TestShadowRoot.querySelectorAll only supports [attrName] selectorspackages/core/__tests__/test-utils.ts:435-447

Documented in the ponytail comment on lines 435-436. Adequate for current tests but will silently return wrong results for tag selectors or [attr=value] if those patterns are ever used. Keep the comment; no action needed now.

Summary

Severity Count Items
Bug 0
Maintenance 2 Hardcoded version anchors, ssrNoExternal type-only enforcement
Nit 2 Protocol registerPlugin loose type, limited querySelectorAll

Approve as-is. The maintenance items can be addressed in a follow-up cycle.

New%20session%20-%202026-06-25T04%3A41%3A55.145Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown
Contributor

Review complete. No bugs found; the three documented items (hardcoded release anchors, RegExp serialization, limited querySelectorAll) are acknowledged in-code with ponytail/JSDoc comments and can be addressed in a follow-up cycle.

New%20session%20-%202026-06-25T04%3A55%3A45.407Z
opencode session  |  github run

@SisyphusZheng SisyphusZheng merged commit 592dc4a into main Jun 25, 2026
8 of 9 checks passed
SisyphusZheng added a commit that referenced this pull request Jun 25, 2026
* ci(release): remove duplicate gates and deploy job from release workflow

- tools/autoflow/release.ts: skip release gates and deploy steps when CI=true

- .github/workflows/autoflow-release.yml: drop gates and deploy jobs; rely on branch protection

- tools/consumer-smoke.ts: include @openelement/protocol in local workspace smoke

- docs/release/v0.41.0-alpha.2-plan.md: plan pure-ESM npm quality gates for alpha.3

* cleanup(protocol): remove speculative protocol types and over-engineered surfaces (#62)

Protocol surface and over-engineering cleanup for v0.41.0-alpha.1-cleanup patch.

- Removes speculative protocol types while keeping protected surface intact.
- Preserves SignalEngine abstraction per ADR-0109 P2.
- Flattens OpenElementBuildContext phase tokens.
- Removes dead DSD report pipeline, merges ssg-i18n into ssg-dynamic.
- Cleans content nav writer exports and architecture contract less* guard.
- Replaces tools/lib/walk.ts with @std/fs/walk.

All local release gates pass (autoflow:ci 21/21).

* cleanup(runtime): boundary and core architecture cleanup (#59)

- Generate ui manifest at build/test/pack time; remove runtime Deno.* usage.
- Flatten lifecycle helpers (start/stop/transition) into app authoring.
- Break core intra-package cycle via BindingRenderer interface.
- Router data-context uses render-scoped stack.
- Move app Vite plugin to @openelement/adapter-vite; make Preact adapter dynamic/peer.

Co-authored-by: DevBot <devbot@openelement.dev>

* cleanup(verifiability): release hygiene and type verification (#61) (#64)

cleanup(verifiability): release hygiene and type verification (#61)

* fix: post-audit fixes for alpha.1-cleanup review findings (#75)

fix: post-audit fixes for alpha.1-cleanup review findings (#65-#74) plus CI typecheck fix

* [v0.41.0-alpha.2] Deferred alpha.1 review cleanup (#84)

* fix(core/content/ssg): address deferred alpha.1 review findings (#81)

- Standardize import extensions to .js across packages/core/src

- Remove nodeType === 0 production coupling; set fake fragment nodeType to 11 in tests

- Restore globalThis.document after binding-activation and jsx-render-dom tests

- Add content-helpers.test.ts for write-json, sitemap/plugin, fs-adapter

- Import only cwd from node:process in ssg-render and build-postprocess

* fix(content,core): address PR #84 robot nits

- Import BufferEncoding via NodeJS namespace in fs-adapter.ts so the cast
  is type-safe without relying on a non-existent 'node:buffer' export.
- Add comments to the restore-global-document tests documenting that they
  must remain the last test in each file.

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* fix(ui): remove :host-context from token sheet and add openPropsRootSheet (#86)

Safari/WebKit does not support :host-context, so the dark-mode token
rule was dropped entirely in shadow roots that adopted
openPropsTokenSheet. Remove :host-context from the host sheet and add
openPropsRootSheet, a :root variant for document-level adoption that
cascades into every shadow DOM via custom-property inheritance.

Closes #80

Co-authored-by: DevBot <devbot@openelement.dev>

* [v0.41.0-alpha.2] Validate static subpath with open-button (#85)

* feat(ui): validate static subpath with open-button (#79)

- Migrate open-button escapeAttr import from @openelement/core to @openelement/core/static

- Add packages/ui/__tests__/static-subpath.test.ts

* test(create): add @openelement/core/static alias in generated project test

open-button now imports from @openelement/core/static; the create CLI
workspace-test harness must alias that subpath or the generated project
build fails during SSR bundle resolution.

* fix(create): add @openelement/core/static alias to consumer-local.ts

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* [v0.41.0-alpha.2] Candidate 3: Extract HydrationScope to @openelement/core/hydrate (#83)

* feat(core/element): extract HydrationScope to @openelement/core/hydrate (#76)

- Create HydrationScope value object in packages/core/src/hydration-scope.ts

- Export HydrationScope from @openelement/core/hydrate

- Refactor OpenElement to own a single #hydrationScope

- Refactor open-element-hydration.ts and open-element-render.ts to operate on scope

- Add hydration-scope.test.ts with fake DOM and isolation tests

- Update subpath-exports.test.ts for HydrationScope export

- Update STATUS.md and VERSION_PLAN.md for v0.41.0-alpha.2

* fix(core,element): address HydrationScope robot review findings

- Add HydrationScope.reset() to clear effects/events without deactivating
  the scope; use it before re-render/re-hydrate.
- Fix critical bug in hydrateExistingDom: scope.dispose() was called before
  scope.hydrate(), which made hydrate() a no-op. Replace with scope.reset().
- Use scope.dispose() in OpenElement.disconnectedCallback for true lifecycle
  cleanup instead of disposeRenderBindings().
- Remove duplicate disposeRenderBindings() helper; disposal now lives in the
  scope.
- Remove unused disposeScope() helper and VNodeCacheAccess type export.
- Drop unused render callback from OpenElement's HydrationScope.
- Normalize import extension in hydrate.ts (.ts -> .js).

* fix(hydration): fmt/lint/arch and consumer-local alias for HydrationScope

* style: format hydration-scope.test and open-element-render for fmt:check

* ci: trigger rebuild after format fixes

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* [v0.41.0-alpha.2] Candidate 1: Split renderer from activation layer (#77)

- jsx-render-dom.ts now emits BindingDescriptors and commits them via commitBindings()
- Move Show/For effect lifecycle into binding-activation.ts as conditional/list bindings
- Add commitBindings export to core/csr/hydrate entry points

Co-authored-by: DevBot <devbot@openelement.dev>

* [v0.41.0-alpha.2] Candidate 2: BindingDescriptor registry (#78)

- Add factory constructors for all descriptor kinds in binding-descriptor.ts
- Replace central switch in applyBindingDescriptor with internal Map registry
- Add registerBindingKind for custom binding kinds
- Update jsx-render-dom.ts, hydration-scope.ts, event-hydration.ts to use factories
- Re-export factories and registerBindingKind from core/csr/hydrate entry points
- Add custom binding kind test

Co-authored-by: DevBot <devbot@openelement.dev>

* docs: mark v0.41.0-alpha.2 workstreams complete

* fix(core,ui): polish HydrationScope and fix token typo (closes #89)

- Cache cacheAccess getter in private field to fix object identity
- Remove dead VNodeCacheAccess.get() method
- Mark VNodeCacheAccess and cacheAccess as @internal
- Normalize import extensions to .js in hydration-scope.ts
- Fix --blue-10 -> --teal-10 typo in dark-mode token block

Co-authored-by: DevBot <devbot@openelement.dev>

* fix(core): binding layer cleanup - factories, registry, types (closes #90)

* fix(core): binding layer cleanup - factories, registry, types (#90)

- Remove _root dead param from commitBindings
- Add bindHtml factory for signal-html descriptors
- Add overwrite warning to registerBindingKind
- Remove dead keyFn field from ListBindingDescriptor
- Narrow condition type to Signal<boolean> | boolean
- Narrow items type to Signal<unknown[]> | unknown[]
- Use bindHtml factory in collectPropBindings
- Re-export bindHtml from core/csr/hydrate entry points

* docs: add comments documenting narrowed type casts

* fix(core): revert narrowed factory types per bot review

Widen bindConditional/bindList factory signatures back to
Signal<unknown> | unknown to match runtime coercion in applyConditional/applyList

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* chore: bump versions to 0.41.0-alpha.2 and update docs (#82)

* chore: bump versions to 0.41.0-alpha.2 and update roadmap (#82)

- Bump all 11 packages from 0.41.0-alpha.1 to 0.41.0-alpha.2
- Mark v0.41.0-alpha.2 as Shipped in ROADMAP.md
- Add completion note

* fix: update PACKAGE_VERSION and docs for alpha.2 (#82)

- Bump PACKAGE_VERSION in tools/project-constants.ts to 0.41.0-alpha.2
- Update README.md, README.zh.md, PROJECT_WORKFLOW.md with alpha.2

* fix: update ACTIVE_VERSION and www route version refs for alpha.2

* fix: update getting-started.tsx with alpha.2 version ref

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* docs: fix stale version refs found by bot review of #94

* docs: fix stale version refs in ROADMAP header, README Current Line, PACKAGE_SURFACE, design README

* docs: fix remaining stale version refs and PACKAGE_SURFACE protocol row

- Update OPENELEMENT_VERSION constant to v0.41.0-alpha.2
- Fix hardcoded alpha.1 strings in architecture, apilist, examples routes
- Add @openelement/protocol to PACKAGE_SURFACE table (11-package)

* docs: fix two more stale v0.41.0-alpha.1 refs

* docs: fix 14-to-10 -> 14-to-11 in PACKAGE_SURFACE.md

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* fix: resolve bot review findings from PR #96 (dev→main)

- Remove dead _hydrateSignals() method and unused hydrateSignals import
- Fix inconsistent DOM clearing: shadow root now clears before null check
- Import eventTypeFromProp from event-marker.js instead of event-hydration.js
- Remove dead 'key' field from StaticAttrBindingDescriptor interface and factory
- Remove dangling comment in protocol/manifest.ts
- Update stale comments referencing _hydrateSignals

Co-authored-by: DevBot <devbot@openelement.dev>

* fix: address all 10 PR #99 bot review findings

1. CSR className/class props use bindAttr (signal-attr) instead of bindClass
2. collectHydrationBindings accepts lifecycle param directly
3. Make VNodeCacheAccess non-exported (file-private)
4. Add HydrationScope.createLifecycle() method
5. Remove duplicate bindHydrateEvents re-export from hydrate.ts
6. Restore framework.js barrel in protocol/src/index.ts
7. Document AbortSignal absence in open-element-render.ts
8. Make SignalRenderBindingDescriptor.lifecycle optional
9. Reject null bytes in resolveDynamicRoutePath
10. Replace ponytail comment with normal comment

* Potential fix for pull request finding 'Use of returnless function'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>

* Potential fix for pull request finding 'Use of returnless function'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>

* Potential fix for pull request finding 'Use of returnless function'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>

* fix: collapse dead if/else, remove unused getters, add signal-class doc

- Collapse identical if/else branches in collectPropBindings
- Remove unused _effectDisposers / _eventCleanups getters
- Document SignalClassBindingDescriptor as DSD-only

* fix: reset() ungated, registerDispose exclusive, DANGEROUS_KEYS export, constructor init

- reset() no longer gates on #active so reconnection can re-hydrate
- registerDispose uses AbortSignal OR disposers Set, not both (avoid double-dispose)
- Drop @internal from DANGEROUS_KEYS barrel export
- Move HydrationScope construction into OpenElement constructor with super() call

* fix: replace cacheAccess with setCachedVNode, make hydrateSignals private, hoist inline import

- Replace cacheAccess getter + VNodeCacheAccess interface with setCachedVNode method
- Remove export from hydrateSignals (only used in same module)
- Hoist Signal import type in open-element-render.ts to top level
- Make bindRender lifecycle parameter required (no default {})

* fix: reset() reactivates scope after dispose, doc test mock leak

- reset() now sets #active = true so disconnect/reconnect can re-hydrate
- Add comment documenting test mock ordering requirement

* fix(test): wrap mock document in per-test setup/teardown to prevent cross-file leaks

* fix: add @internal annotation to DANGEROUS_KEYS barrel export

* fix: remove DANGEROUS_KEYS from barrel, add ShadowRoot stub restore

* fix(release): accept semver pre-release suffix in parseSemver

* fix: add @internal to DANGEROUS_KEYS in static.ts, fix greedy regex

* fix(release): use --allow-dirty for deno pack in release mode

deno fmt may touch files outside the staged bump list, making the
worktree dirty. This is safe in CI where changes are expected.

* fix(release): skip already-published packages on E403

* docs: add architecture review candidates to alpha.3 plan (#107-#109)

* docs: reorganize alpha.4/5 — Preact island proof to alpha.4, Deno Desktop only in alpha.5

* feat(alpha3): add web components artifact gates

* refactor(alpha.3): extract smoke fixtures to files, share alias logic

- Move inline fixture strings to standalone files under tools/third-party-wc-smoke/app/
- Add allPackageAliases() to tools/lib/package-graph.ts
- Use allPackageAliases() in both third-party-wc-smoke.ts and consumer-local.ts
- Reduces smoke test from 551 → 253 lines (54% reduction)

* feat(alpha.3): complete architecture candidates #107-#109

- #107: Create @openelement/app/vite as canonical Vite entry
- #108: Deepen BuildContext with recordPhase* and registerPlugin methods
- #109: Consolidate test DOM harness into test-utils.ts (-619 lines)
- Update alpha.3 plan checkboxes to mark all workstreams complete

* fix(alpha.3): address PR #110 bot review findings

- Remove inverted phase ordering check in markComplete
- Fix stripComments URL mangling in check-package-artifacts
- Replace module-level document mutation with setupMockDocument()
- Remove unused recordPhase*Fact wrappers
- Tighten registerPlugin generic type
- Replace fragile path slicing with dirname()

* fix(test): wrap jsx-render-dom tests in withMockDocument for test isolation

* fix(alpha.3): remove app/vite re-export to break dependency cycle

app→adapter-vite→app creates a cycle that breaks graph:check.
Keep adapter-vite as the canonical Vite entry; update docs accordingly.

* fix(alpha.3): clear remaining bot review issues

- Document stripComments edge case as extremely unlikely
- Remove empty onListen callback in smoke test
- Move helper functions before control flow in consumer-local.ts

* docs: clarify ssrNoExternal RegExp serialization contract

* docs: note hardcoded version anchors in release.ts as ponytail debt

* fix(alpha.3): tighten registerPlugin protocol type, doc querySelectorAll limitation

* fix(alpha.3): move helpers to top of consumer-local, revert protocol registerPlugin to unknown

* fix: update arch:check allowlist for sitemap registerPlugin

* fix(release): update version anchors from 0.40.8 to 0.41.0-alpha.2

* docs: mark alpha.3 as Shipped, update alpha.5 to Deno Desktop

* feat(alpha.4): add hydrateOpenElement client runtime to core/hydrate

* feat(alpha.4): add Fresh example with openElement + Preact island

* feat(alpha.4): complete remaining tasks - smoke test, docs, plan update

- Preact island smoke test (18 tests covering SSR, props, signals, lifecycle)
- Fresh integration docs (docs/integrations/open-element-in-fresh.md)
- Alpha.4 plan checkboxes updated (15 items marked complete)
- Fresh example now uses hydrateOpenElement from core/hydrate

* fix(alpha.4): use real hydrateOpenElement, format Fresh files

* fix alpha4 ci review issues

* fix alpha4 review followups

* fix alpha4 final review gaps

* fix alpha4 review docs and preact proof

* fix alpha4 test stub cleanup

* docs: mark alpha.4 release workflow items as done

---------

Signed-off-by: Zhi <freemanzheng@hotmail.com>
Co-authored-by: DevBot <devbot@openelement.dev>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
SisyphusZheng added a commit that referenced this pull request Jun 25, 2026
* ci(release): remove duplicate gates and deploy job from release workflow

- tools/autoflow/release.ts: skip release gates and deploy steps when CI=true

- .github/workflows/autoflow-release.yml: drop gates and deploy jobs; rely on branch protection

- tools/consumer-smoke.ts: include @openelement/protocol in local workspace smoke

- docs/release/v0.41.0-alpha.2-plan.md: plan pure-ESM npm quality gates for alpha.3

* cleanup(protocol): remove speculative protocol types and over-engineered surfaces (#62)

Protocol surface and over-engineering cleanup for v0.41.0-alpha.1-cleanup patch.

- Removes speculative protocol types while keeping protected surface intact.
- Preserves SignalEngine abstraction per ADR-0109 P2.
- Flattens OpenElementBuildContext phase tokens.
- Removes dead DSD report pipeline, merges ssg-i18n into ssg-dynamic.
- Cleans content nav writer exports and architecture contract less* guard.
- Replaces tools/lib/walk.ts with @std/fs/walk.

All local release gates pass (autoflow:ci 21/21).

* cleanup(runtime): boundary and core architecture cleanup (#59)

- Generate ui manifest at build/test/pack time; remove runtime Deno.* usage.
- Flatten lifecycle helpers (start/stop/transition) into app authoring.
- Break core intra-package cycle via BindingRenderer interface.
- Router data-context uses render-scoped stack.
- Move app Vite plugin to @openelement/adapter-vite; make Preact adapter dynamic/peer.

Co-authored-by: DevBot <devbot@openelement.dev>

* cleanup(verifiability): release hygiene and type verification (#61) (#64)

cleanup(verifiability): release hygiene and type verification (#61)

* fix: post-audit fixes for alpha.1-cleanup review findings (#75)

fix: post-audit fixes for alpha.1-cleanup review findings (#65-#74) plus CI typecheck fix

* [v0.41.0-alpha.2] Deferred alpha.1 review cleanup (#84)

* fix(core/content/ssg): address deferred alpha.1 review findings (#81)

- Standardize import extensions to .js across packages/core/src

- Remove nodeType === 0 production coupling; set fake fragment nodeType to 11 in tests

- Restore globalThis.document after binding-activation and jsx-render-dom tests

- Add content-helpers.test.ts for write-json, sitemap/plugin, fs-adapter

- Import only cwd from node:process in ssg-render and build-postprocess

* fix(content,core): address PR #84 robot nits

- Import BufferEncoding via NodeJS namespace in fs-adapter.ts so the cast
  is type-safe without relying on a non-existent 'node:buffer' export.
- Add comments to the restore-global-document tests documenting that they
  must remain the last test in each file.

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* fix(ui): remove :host-context from token sheet and add openPropsRootSheet (#86)

Safari/WebKit does not support :host-context, so the dark-mode token
rule was dropped entirely in shadow roots that adopted
openPropsTokenSheet. Remove :host-context from the host sheet and add
openPropsRootSheet, a :root variant for document-level adoption that
cascades into every shadow DOM via custom-property inheritance.

Closes #80

Co-authored-by: DevBot <devbot@openelement.dev>

* [v0.41.0-alpha.2] Validate static subpath with open-button (#85)

* feat(ui): validate static subpath with open-button (#79)

- Migrate open-button escapeAttr import from @openelement/core to @openelement/core/static

- Add packages/ui/__tests__/static-subpath.test.ts

* test(create): add @openelement/core/static alias in generated project test

open-button now imports from @openelement/core/static; the create CLI
workspace-test harness must alias that subpath or the generated project
build fails during SSR bundle resolution.

* fix(create): add @openelement/core/static alias to consumer-local.ts

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* [v0.41.0-alpha.2] Candidate 3: Extract HydrationScope to @openelement/core/hydrate (#83)

* feat(core/element): extract HydrationScope to @openelement/core/hydrate (#76)

- Create HydrationScope value object in packages/core/src/hydration-scope.ts

- Export HydrationScope from @openelement/core/hydrate

- Refactor OpenElement to own a single #hydrationScope

- Refactor open-element-hydration.ts and open-element-render.ts to operate on scope

- Add hydration-scope.test.ts with fake DOM and isolation tests

- Update subpath-exports.test.ts for HydrationScope export

- Update STATUS.md and VERSION_PLAN.md for v0.41.0-alpha.2

* fix(core,element): address HydrationScope robot review findings

- Add HydrationScope.reset() to clear effects/events without deactivating
  the scope; use it before re-render/re-hydrate.
- Fix critical bug in hydrateExistingDom: scope.dispose() was called before
  scope.hydrate(), which made hydrate() a no-op. Replace with scope.reset().
- Use scope.dispose() in OpenElement.disconnectedCallback for true lifecycle
  cleanup instead of disposeRenderBindings().
- Remove duplicate disposeRenderBindings() helper; disposal now lives in the
  scope.
- Remove unused disposeScope() helper and VNodeCacheAccess type export.
- Drop unused render callback from OpenElement's HydrationScope.
- Normalize import extension in hydrate.ts (.ts -> .js).

* fix(hydration): fmt/lint/arch and consumer-local alias for HydrationScope

* style: format hydration-scope.test and open-element-render for fmt:check

* ci: trigger rebuild after format fixes

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* [v0.41.0-alpha.2] Candidate 1: Split renderer from activation layer (#77)

- jsx-render-dom.ts now emits BindingDescriptors and commits them via commitBindings()
- Move Show/For effect lifecycle into binding-activation.ts as conditional/list bindings
- Add commitBindings export to core/csr/hydrate entry points

Co-authored-by: DevBot <devbot@openelement.dev>

* [v0.41.0-alpha.2] Candidate 2: BindingDescriptor registry (#78)

- Add factory constructors for all descriptor kinds in binding-descriptor.ts
- Replace central switch in applyBindingDescriptor with internal Map registry
- Add registerBindingKind for custom binding kinds
- Update jsx-render-dom.ts, hydration-scope.ts, event-hydration.ts to use factories
- Re-export factories and registerBindingKind from core/csr/hydrate entry points
- Add custom binding kind test

Co-authored-by: DevBot <devbot@openelement.dev>

* docs: mark v0.41.0-alpha.2 workstreams complete

* fix(core,ui): polish HydrationScope and fix token typo (closes #89)

- Cache cacheAccess getter in private field to fix object identity
- Remove dead VNodeCacheAccess.get() method
- Mark VNodeCacheAccess and cacheAccess as @internal
- Normalize import extensions to .js in hydration-scope.ts
- Fix --blue-10 -> --teal-10 typo in dark-mode token block

Co-authored-by: DevBot <devbot@openelement.dev>

* fix(core): binding layer cleanup - factories, registry, types (closes #90)

* fix(core): binding layer cleanup - factories, registry, types (#90)

- Remove _root dead param from commitBindings
- Add bindHtml factory for signal-html descriptors
- Add overwrite warning to registerBindingKind
- Remove dead keyFn field from ListBindingDescriptor
- Narrow condition type to Signal<boolean> | boolean
- Narrow items type to Signal<unknown[]> | unknown[]
- Use bindHtml factory in collectPropBindings
- Re-export bindHtml from core/csr/hydrate entry points

* docs: add comments documenting narrowed type casts

* fix(core): revert narrowed factory types per bot review

Widen bindConditional/bindList factory signatures back to
Signal<unknown> | unknown to match runtime coercion in applyConditional/applyList

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* chore: bump versions to 0.41.0-alpha.2 and update docs (#82)

* chore: bump versions to 0.41.0-alpha.2 and update roadmap (#82)

- Bump all 11 packages from 0.41.0-alpha.1 to 0.41.0-alpha.2
- Mark v0.41.0-alpha.2 as Shipped in ROADMAP.md
- Add completion note

* fix: update PACKAGE_VERSION and docs for alpha.2 (#82)

- Bump PACKAGE_VERSION in tools/project-constants.ts to 0.41.0-alpha.2
- Update README.md, README.zh.md, PROJECT_WORKFLOW.md with alpha.2

* fix: update ACTIVE_VERSION and www route version refs for alpha.2

* fix: update getting-started.tsx with alpha.2 version ref

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* docs: fix stale version refs found by bot review of #94

* docs: fix stale version refs in ROADMAP header, README Current Line, PACKAGE_SURFACE, design README

* docs: fix remaining stale version refs and PACKAGE_SURFACE protocol row

- Update OPENELEMENT_VERSION constant to v0.41.0-alpha.2
- Fix hardcoded alpha.1 strings in architecture, apilist, examples routes
- Add @openelement/protocol to PACKAGE_SURFACE table (11-package)

* docs: fix two more stale v0.41.0-alpha.1 refs

* docs: fix 14-to-10 -> 14-to-11 in PACKAGE_SURFACE.md

---------

Co-authored-by: DevBot <devbot@openelement.dev>

* fix: resolve bot review findings from PR #96 (dev→main)

- Remove dead _hydrateSignals() method and unused hydrateSignals import
- Fix inconsistent DOM clearing: shadow root now clears before null check
- Import eventTypeFromProp from event-marker.js instead of event-hydration.js
- Remove dead 'key' field from StaticAttrBindingDescriptor interface and factory
- Remove dangling comment in protocol/manifest.ts
- Update stale comments referencing _hydrateSignals

Co-authored-by: DevBot <devbot@openelement.dev>

* fix: address all 10 PR #99 bot review findings

1. CSR className/class props use bindAttr (signal-attr) instead of bindClass
2. collectHydrationBindings accepts lifecycle param directly
3. Make VNodeCacheAccess non-exported (file-private)
4. Add HydrationScope.createLifecycle() method
5. Remove duplicate bindHydrateEvents re-export from hydrate.ts
6. Restore framework.js barrel in protocol/src/index.ts
7. Document AbortSignal absence in open-element-render.ts
8. Make SignalRenderBindingDescriptor.lifecycle optional
9. Reject null bytes in resolveDynamicRoutePath
10. Replace ponytail comment with normal comment

* Potential fix for pull request finding 'Use of returnless function'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>

* Potential fix for pull request finding 'Use of returnless function'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>

* Potential fix for pull request finding 'Use of returnless function'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: Zhi <freemanzheng@hotmail.com>

* fix: collapse dead if/else, remove unused getters, add signal-class doc

- Collapse identical if/else branches in collectPropBindings
- Remove unused _effectDisposers / _eventCleanups getters
- Document SignalClassBindingDescriptor as DSD-only

* fix: reset() ungated, registerDispose exclusive, DANGEROUS_KEYS export, constructor init

- reset() no longer gates on #active so reconnection can re-hydrate
- registerDispose uses AbortSignal OR disposers Set, not both (avoid double-dispose)
- Drop @internal from DANGEROUS_KEYS barrel export
- Move HydrationScope construction into OpenElement constructor with super() call

* fix: replace cacheAccess with setCachedVNode, make hydrateSignals private, hoist inline import

- Replace cacheAccess getter + VNodeCacheAccess interface with setCachedVNode method
- Remove export from hydrateSignals (only used in same module)
- Hoist Signal import type in open-element-render.ts to top level
- Make bindRender lifecycle parameter required (no default {})

* fix: reset() reactivates scope after dispose, doc test mock leak

- reset() now sets #active = true so disconnect/reconnect can re-hydrate
- Add comment documenting test mock ordering requirement

* fix(test): wrap mock document in per-test setup/teardown to prevent cross-file leaks

* fix: add @internal annotation to DANGEROUS_KEYS barrel export

* fix: remove DANGEROUS_KEYS from barrel, add ShadowRoot stub restore

* fix(release): accept semver pre-release suffix in parseSemver

* fix: add @internal to DANGEROUS_KEYS in static.ts, fix greedy regex

* fix(release): use --allow-dirty for deno pack in release mode

deno fmt may touch files outside the staged bump list, making the
worktree dirty. This is safe in CI where changes are expected.

* fix(release): skip already-published packages on E403

* docs: add architecture review candidates to alpha.3 plan (#107-#109)

* docs: reorganize alpha.4/5 — Preact island proof to alpha.4, Deno Desktop only in alpha.5

* feat(alpha3): add web components artifact gates

* refactor(alpha.3): extract smoke fixtures to files, share alias logic

- Move inline fixture strings to standalone files under tools/third-party-wc-smoke/app/
- Add allPackageAliases() to tools/lib/package-graph.ts
- Use allPackageAliases() in both third-party-wc-smoke.ts and consumer-local.ts
- Reduces smoke test from 551 → 253 lines (54% reduction)

* feat(alpha.3): complete architecture candidates #107-#109

- #107: Create @openelement/app/vite as canonical Vite entry
- #108: Deepen BuildContext with recordPhase* and registerPlugin methods
- #109: Consolidate test DOM harness into test-utils.ts (-619 lines)
- Update alpha.3 plan checkboxes to mark all workstreams complete

* fix(alpha.3): address PR #110 bot review findings

- Remove inverted phase ordering check in markComplete
- Fix stripComments URL mangling in check-package-artifacts
- Replace module-level document mutation with setupMockDocument()
- Remove unused recordPhase*Fact wrappers
- Tighten registerPlugin generic type
- Replace fragile path slicing with dirname()

* fix(test): wrap jsx-render-dom tests in withMockDocument for test isolation

* fix(alpha.3): remove app/vite re-export to break dependency cycle

app→adapter-vite→app creates a cycle that breaks graph:check.
Keep adapter-vite as the canonical Vite entry; update docs accordingly.

* fix(alpha.3): clear remaining bot review issues

- Document stripComments edge case as extremely unlikely
- Remove empty onListen callback in smoke test
- Move helper functions before control flow in consumer-local.ts

* docs: clarify ssrNoExternal RegExp serialization contract

* docs: note hardcoded version anchors in release.ts as ponytail debt

* fix(alpha.3): tighten registerPlugin protocol type, doc querySelectorAll limitation

* fix(alpha.3): move helpers to top of consumer-local, revert protocol registerPlugin to unknown

* fix: update arch:check allowlist for sitemap registerPlugin

* fix(release): update version anchors from 0.40.8 to 0.41.0-alpha.2

* docs: mark alpha.3 as Shipped, update alpha.5 to Deno Desktop

* feat(alpha.4): add hydrateOpenElement client runtime to core/hydrate

* feat(alpha.4): add Fresh example with openElement + Preact island

* feat(alpha.4): complete remaining tasks - smoke test, docs, plan update

- Preact island smoke test (18 tests covering SSR, props, signals, lifecycle)
- Fresh integration docs (docs/integrations/open-element-in-fresh.md)
- Alpha.4 plan checkboxes updated (15 items marked complete)
- Fresh example now uses hydrateOpenElement from core/hydrate

* fix(alpha.4): use real hydrateOpenElement, format Fresh files

* fix alpha4 ci review issues

* fix alpha4 review followups

* fix alpha4 final review gaps

* fix alpha4 review docs and preact proof

* fix alpha4 test stub cleanup

* docs: mark alpha.4 release workflow items as done

---------

Signed-off-by: Zhi <freemanzheng@hotmail.com>
Co-authored-by: DevBot <devbot@openelement.dev>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
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.

[v0.41.0-alpha.3] Candidate 1: Deepen the Framework Vite Entry

1 participant