This repository was archived by the owner on Sep 23, 2026. It is now read-only.
Repository navigation
Extract reflection engine from reflector into devonian - #23
Merged
Merged
Conversation
Per the decision recorded in ontola/atomic-plugins#6: reflector's bidirectional GitHub-issue reflection engine (origin markers, id-map, echo suppression, state reconciliation, comment reflection) was reflector-only code duplicating what devonian is meant to be the shared home for. Extracts it here, generalized so it depends on nothing but a syncables ApiClient per side (no OpenAPI document, resource model, or auth of its own) plus a namespaced marker codec (defaulting to `devonian`, so a host with markers already in production — like reflector's `reflector:origin` — can keep its own namespace). Adds ReflectionEngine, ReflectionRunner (the background-loop/reflectNow/ status wrapper), and the marker/IdMap/KvStore support types under devonian/reflect, with full test coverage against a real syncables client. Bumps to 0.6.0; reflector's own dependency on this package is a follow-up change pending publish. Claude-Session: https://claude.ai/code/session_01B2mbssSNDiu8GpKjaCFRn1 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every other export here ships raw .ts, which works for the bundler/vite- transformed consumers (this repo's own tests, atomic-plugins) this package has had so far. reflect's actual first consumer, reflector, is a plain tsc-built Node app with no TS loader at runtime — importing a raw .ts file there would fail outright. Points the "./reflect" export at build/src/reflect (types + default conditions) instead, matching how syncables — reflector's other TS dependency — already ships. Claude-Session: https://claude.ai/code/session_01B2mbssSNDiu8GpKjaCFRn1 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
michielbdejong
pushed a commit
that referenced
this pull request
Sep 21, 2026
The published devonian@0.6.0 tarball's build/ predates this repo's src/reflect/ (ontola/atomic-plugins#6, PR #23) — nothing forced a rebuild before `npm publish`, so it shipped whatever build/ happened to be on disk. That leaves build/src/reflect missing from the registry package, breaking the "./reflect" export's "types"/"default" resolution for any real consumer (confirmed via reflector's PR #58, which fails to resolve devonian/reflect against the published 0.6.0). Adds prepublishOnly: npm run clean && npm run build, so publish always ships a build/ that matches src/. Bumps to 0.6.1 since 0.6.0 is already claimed on the registry and can't be overwritten. Claude-Session: https://claude.ai/code/session_01B2mbssSNDiu8GpKjaCFRn1 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
michielbdejong
added a commit
that referenced
this pull request
Sep 21, 2026
* Guarantee a fresh build before publish; release 0.6.1 The published devonian@0.6.0 tarball's build/ predates this repo's src/reflect/ (ontola/atomic-plugins#6, PR #23) — nothing forced a rebuild before `npm publish`, so it shipped whatever build/ happened to be on disk. That leaves build/src/reflect missing from the registry package, breaking the "./reflect" export's "types"/"default" resolution for any real consumer (confirmed via reflector's PR #58, which fails to resolve devonian/reflect against the published 0.6.0). Adds prepublishOnly: npm run clean && npm run build, so publish always ships a build/ that matches src/. Bumps to 0.6.1 since 0.6.0 is already claimed on the registry and can't be overwritten. Claude-Session: https://claude.ai/code/session_01B2mbssSNDiu8GpKjaCFRn1 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Automate npm publish via GitHub Actions Per feedback on this session's manual-publish fumble (devonian@0.6.0 shipped a stale build/ — see the previous commit): publishing should not be a manual step at all. Adds .github/workflows/publish.yml, which runs on every push to main that touches package.json, and publishes only when the local version isn't already on the registry — so bumping the version in a PR and merging it is the entire release process. Needs an NPM_TOKEN repository secret (an npm automation token with publish rights on devonian) added under Settings → Secrets and variables → Actions; documented in the README's new "Publishing" section. That secret is not something this session can add. Claude-Session: https://claude.ai/code/session_01B2mbssSNDiu8GpKjaCFRn1 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extracts the bidirectional reflection engine from
localthought/reflectorintodevonianas a reusable, platform-agnostic component. This enables other hosts (reflector itself, bridges, and future integrations) to share the same battle-tested reflection logic instead of maintaining separate copies.Key Changes
src/reflect/engine.ts— CoreReflectionEngineclass that orchestrates bidirectional synchronization of records between twosyncables-backed systems:src/reflect/marker.ts— Origin marker codec for embedding/parsing hidden HTML comments that link reflected copies back to their originals:embedMarker()/parseMarker()/stripMarker()/hasMarker()/renderMarker()<!-- <namespace>:origin ... -->) to avoid collisionssrc/reflect/id-map.ts— Persisted correspondence between originals and reflected copies:InMemoryIdMapfor in-memory storageFileIdMapfor JSON-file-backed persistencesrc/reflect/kv-store.ts— Tiny persisted string→string store for reflection metadata:InMemoryKvStorefor in-memory storageFileKvStorefor JSON-file-backed persistencesrc/reflect/runner.ts— Background loop + on-demand trigger wrapper:ReflectionRunnerserializes concurrentreflectNow()calls so they never overlapstart(), then repeats on a configurable intervalsrc/reflect/index.ts— Public API barrel exportComprehensive test suite (
__tests__/unit/reflect/):engine.test.ts— 13 tests covering issue creation, state reflection, comment reflection, bidirectionality, idempotency, and error handlingmarker.test.ts— 10 tests for the origin marker codecid-map.test.ts— Tests for in-memory and file-backed id-mapkv-store.test.ts— Tests for in-memory and file-backed kv-storerunner.test.ts— Tests for background loop serialization and interval behaviorDocumentation:
docs/ai-logs/sessions/2026-09-21-...md) documenting the extraction decision and implementationdocs/ai-logs/README.md,pending-historical-sessions.md) per NLnet policyNotable Implementation Details
ReflectionEnginehas no notion of OpenAPI, overlays, or auth — a host provides twoReflectionSideobjects already bound tosyncablesclients, keeping the engine portable across any two compatible endpoints.ReflectionRunnerensures manual triggers and scheduled ticks never overlap via a promise chain.Version
Bumped to
0.6.0and added./reflectexport to `packagehttps://claude.ai/code/session_01B2mbssSNDiu8GpKjaCFRn1