Skip to content

feat(v3): D4 Milestone 1 — cross-doc primitives + node-based helpers (v0.14)#83

Open
mbreiser wants to merge 2 commits into
mainfrom
feat/v3-d4-milestone1
Open

feat(v3): D4 Milestone 1 — cross-doc primitives + node-based helpers (v0.14)#83
mbreiser wants to merge 2 commits into
mainfrom
feat/v3-d4-milestone1

Conversation

@mbreiser
Copy link
Copy Markdown
Contributor

What this is

Milestone 1 of D4 (cross-library import) for the v3 Experiment Designer — the
substrate for copying conditions (plus the anchors and plugin declarations they
depend on) from a second parsed YAML.Document into the one you're editing.
M1 is pure logic + Node tests; no UI (UI is Milestone 3).

Per docs/development/v3-d4-implementation-handoff.md §5. Stops at M1 — does not
start M2.

Two commits

  1. docs(v3): D4 design rev 3 — applies the rev-2 → rev-3 fix list, then folds
    in the findings from a fresh codex-plan-review (gpt-5.5) pass on rev 3.
  2. feat(v3): D4 Milestone 1 — the code + tests + footer bump.

Design: rev 2 → rev 3

Applied the 11-item fix list (v3-d4-design-reviews.md): plugin merge-by-default
when same runtime resource (anchors still namespace); one shared aliasRewriteMap;
visited-set closure; topological anchor insert; commit rewrites the cloned name:;
alias-bound plugin_name block; cross-buffer collision check; fixed sortedJson;
broken-alias hard-block; lock via UI handlers.

Then ran codex-plan-review on rev 3 and addressed the findings (reconciliation +
raw outputs under .codex-review/, gitignored). M1-actionable ones are in this
PR's code
; M2/M3 ones are recorded in the design:

  • In code now: cloneNodeAcrossDocs rewrites inline anchor definitions (not
    just alias refs); docInsert*Node derive the JS mirror internally; and (my
    finding Codex missed) ensureTopLevelSection splices a created section before
    conditions: — appending after it would put an imported *alias before its
    &anchor and, with verifyAliasOrder: true (the stringify default), toString()
    would throw.
  • Recorded for M2/M3: broaden plugin merge identity + rig guard; built-in
    plugin_name: log special-case; walk imported plugin config aliases;
    planned-name collision blocking (incl. empty prefix); commit atomicity
    (snapshot/restore); expanded import-mode locking; no setDirty on enter;
    self-referential-anchor handling in topo sort.

Open questions surfaced (not blocking M1): keep plugin merge-by-default with a
broadened identity vs explicit per-plugin mapping; confirm the "snippet importer"
product contract; import provenance. Proceeding on the handoff's pre-answered
defaults.

Code

New js/v3-import.js (dual-export: ES module + CommonJS + window global):
collectAliasReferences, resolveAlias (via alias.resolve(doc) — last-wins, not
name lookup), cloneNodeAcrossDocs (one shared rewrite map; rewrites alias refs +
inline anchor defs; preserves comments), yamlNodeStructuralEquals, sortedJson
(recursive key sort).

New helpers in js/protocol-yaml-v3.js (exported from ProtocolV3 object +
named export): ensureTopLevelSection, docInsertConditionNode,
docInsertVariableNode, docInsertPluginNode — node-based (preserve comments/
anchors, unlike the JS-object-building docInsertCondition), mirror derived
internally via extractCondition/extractPlugin.

Tests — Milestone 1 gate

Suites N1–N6 (+43 checks) in tests/test-protocol-roundtrip-v3.js + a reusable
tests/fixtures/v3_sibling_source.yaml (scalar + transitive anchors, plugins, an
aliasing condition). Cover: cross-doc clone+rewrite, inline-anchor rewrite, last-wins
resolution, transitive walks, insert into existing and absent variables: (with
a re-parse assertion proving the placement fix), internal mirror derivation, plugin
insert, round-trip stable.

npm test → arena 10/10 · v2 137/137 · v3 510/510  (was 467)

Footer bumped v0.13 → v0.14.

Not in scope (later milestones)

M2 staging buffer + commit pipeline; M3 three-pane UI + locking; M4 polish/docs.

🤖 Generated with Claude Code

mbreiser and others added 2 commits May 30, 2026 00:29
…findings

Bring the D4 cross-library import design from rev 2 to rev 3 ahead of
Milestone 1 implementation.

Rev-2 -> rev-3 fix list (from v3-d4-design-reviews.md):
- plugin merge-by-default when same runtime resource; anchors still namespace
- one shared aliasRewriteMap (hasOwnProperty guard); fixed sortedJson recursive sort
- per-batch dependency registry; visited-set closure (not depth cap)
- topological anchor insert; commit rewrites the cloned name: field
- block alias-bound plugin_name; cross-buffer name collision check
- broken-alias hard-block at preflight; lock via UI handlers not global pushUndo

codex-plan-review (gpt-5.5, rev 3) findings folded in:
- cloneNodeAcrossDocs rewrites anchor DEFINITIONS too (inline-anchor hole)
- ensureTopLevelSection splices the new section before conditions: (verifyAliasOrder
  is the stringify default — appending after conditions would throw on commit)
- docInsert*Node derive the JS mirror internally via extractCondition/extractPlugin
- broaden plugin merge identity (all fields but name, + rig when config-less)
- built-in plugin_name: log left unchanged; walk imported plugin config aliases
- self-referential anchors handled in topo sort; pre-commit validation + atomic
  commit (snapshot/restore); expanded import-mode locking; no setDirty on enter

Reconciliation report + raw Codex outputs preserved under .codex-review/
(gitignored). Product-shape (snippet vs behavior import) and provenance surfaced
as open questions; proceeding on the implementation-handoff defaults.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(v0.14)

First implementation milestone of D4 cross-library import. Pure logic +
Node tests; no UI yet (that's Milestone 3).

New module js/v3-import.js (dual-export: ES module + CJS + window global):
- collectAliasReferences(rootNode) — walk a node subtree for Alias refs
- resolveAlias(aliasNode, sourceDoc) — via alias.resolve(doc) (last-wins
  semantics), NOT name-based lookup; null on broken alias
- cloneNodeAcrossDocs(srcNode, targetDoc, aliasRewriteMap) — deep clone +
  rewrite alias REFERENCES and inline anchor DEFINITIONS via one shared map
  (hasOwnProperty guard); preserves comments
- yamlNodeStructuralEquals + sortedJson (recursive key sort) — plugin merge path

New helpers in js/protocol-yaml-v3.js (exported from all surfaces):
- ensureTopLevelSection — creates a missing section SPLICED before
  experiment:/conditions: (appending after them would put an imported *alias
  before its &anchor; verifyAliasOrder:true would throw on toString)
- docInsertConditionNode / docInsertVariableNode / docInsertPluginNode —
  splice already-cloned nodes and derive the JS mirror INTERNALLY via
  extractCondition/extractPlugin (no caller-built object that can drift)

Tests: suites N1–N6 in tests/test-protocol-roundtrip-v3.js (+43 checks) and a
reusable tests/fixtures/v3_sibling_source.yaml (scalar + transitive anchors,
plugins, aliasing condition). Covers cross-doc clone+rewrite, inline-anchor
rewrite, last-wins resolution, transitive walks, insert into existing/absent
variables: with re-parse, internal mirror derivation, round-trip stable.

npm test: arena 10/10, v2 137/137, v3 510/510 (was 467). Footer v0.13 -> v0.14.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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.

1 participant