diff --git a/harness/DECISIONS.md b/harness/DECISIONS.md index 2f153155..f4af1415 100644 --- a/harness/DECISIONS.md +++ b/harness/DECISIONS.md @@ -380,3 +380,8 @@ integration. It requires verified applied-source identity and never substitutes model selection for the caret. Dirty-source semantic lookup remains a disclosed limitation. Implementation is branch-specific at this decision; Windows build, combined tests and native acceptance are not established by this record. + + +## 2026-09-12 — Cross-machine coordination evidence + +User clarification: independent Gitea peer sessions run on different machines and cannot access this session’s `.local` directory. In-thread delegated agents share this workspace. Publish required source, reviews and runtime evidence through versioned Gitea commits or attachments before handing them to peers; verify access and link the exact version. Local paths are provenance only for remote peers. Local compiler, process and native-window observations do not establish another machine’s state or a shared hardware lease. diff --git a/harness/experiments/behavior-layout-policy-20260912/README.md b/harness/experiments/behavior-layout-policy-20260912/README.md new file mode 100644 index 00000000..5335b58d --- /dev/null +++ b/harness/experiments/behavior-layout-policy-20260912/README.md @@ -0,0 +1,11 @@ +# Scenario layout policy experiment — bounded evidence + +Question: can absolute desired coordinates and gap-before records express the same small feasible layouts, and what semantics must a movement planner preserve? This is an isolated JavaScript experiment, not production Rust or a substitute for actual Arcadia Scene, parser, REST, history, or native acceptance. The ten-unit separation and rectangle metrics are illustrative. + +[Source](experiment.mjs) executed through Node REPL and wrote [results](results.json): 3,555 feasible three-row layouts and 53,325 single-move cases passed. Handwritten numeric cases expose different naive reorder/delete/resize behavior. Both encodings can express the enumerated feasible outputs. Changed-coordinate counts compare only the specific single-edit forward policy, not equally expressive retained intent across histories. Elapsed time includes assertions and this machine’s load; it is not a renderer performance result. + +[Independent review](independent-review.md) found an omitted conflict-then-zero-motion history. [The negative probe](negative-zero-motion.mjs) reuses the same experiment with that additional assertion; root executed it and [reproduced the expected assertion failure](negative-zero-motion-result.json): a zero-screen-motion operation changes B.desired from200 to280. The original source/results remain preserved. No production failure is implied. + +The next experiment must distinguish a pointer gesture (original frame and actual delta; zero motion preserves authored input) from an explicit SetDesiredY/Normalize operation, and test move-away/back and resize histories. Equal visible geometry does not imply equal latent layout intent. Before schema adoption, exercise one parsed real scenario through the actual Scene/attachment adapter, including self-message and nested/lifetime cases. The [design proposal and critique](../behavior-navigation-review-20260912/README.md) remain conditional. + +To reproduce, copy experiment.mjs and negative-zero-motion.mjs into an empty directory and run `node experiment.mjs`, then `node negative-zero-motion.mjs`. The first writes results.json; the second must fail its zero-distance assertion. Existing output is never overwritten. Node built-ins only; no package installation or network is required. [Hashes](hashes.json) bind the exact executed source/result artifacts. diff --git a/harness/experiments/behavior-layout-policy-20260912/experiment.mjs b/harness/experiments/behavior-layout-policy-20260912/experiment.mjs new file mode 100644 index 00000000..6513fb12 --- /dev/null +++ b/harness/experiments/behavior-layout-policy-20260912/experiment.mjs @@ -0,0 +1,87 @@ +import assert from 'node:assert/strict'; +import { writeFile } from 'node:fs/promises'; +import { performance } from 'node:perf_hooks'; +// Isolated policy comparison; these illustrative metrics are not Studio rules. +const separation = 10; +function absolute(rows) { + let end = -separation; + return rows.map(r => { const y = Math.max(r.y, end + separation); end=y+r.height; return {...r,y}; }); +} +function gaps(rows) { + let end=0; + return rows.map((r,i) => { assert(r.gap >= (i?separation:0)); const y=end+r.gap; end=y+r.height; return {...r,y}; }); +} +function encodeGaps(rows) { + return rows.map((r,i)=>({...r,gap:r.y-(i?rows[i-1].y+rows[i-1].height:0)})); +} +const positions = rows=>rows.map(r=>r.y); +function moveAbsolute(rows,index,requested) { + const current=absolute(rows); + const floor=index?current[index-1].y+current[index-1].height+separation:0; + const candidate=rows.map((r,i)=>({...r,y:i===index?Math.max(requested,floor):r.y})); + return {candidate,preview:absolute(candidate)}; +} +function changeCount(before,after,key) {return before.reduce((n,r,i)=>n+(r[key]!==after[i][key]),0);} +const initial=[{id:'A',y:100,height:20},{id:'B',y:200,height:20}]; +const initialG=encodeGaps(initial); +const cases=[]; +function named(name,a,g,expectedA,expectedG) { + assert.deepEqual(positions(a),expectedA,name+' absolute'); + assert.deepEqual(positions(g),expectedG,name+' gaps'); + cases.push({name,absolute:positions(a),gapsWithoutInverse:positions(g)}); +} +named('move A to50',absolute([{...initial[0],y:50},initial[1]]),gaps([{...initialG[0],gap:50},initialG[1]]),[50,200],[50,150]); +named('move A to250',absolute([{...initial[0],y:250},initial[1]]),gaps([{...initialG[0],gap:250},initialG[1]]),[250,280],[250,350]); +named('resize A to80',absolute([{...initial[0],height:80},initial[1]]),gaps([{...initialG[0],height:80},initialG[1]]),[100,200],[100,260]); +named('resize A to120',absolute([{...initial[0],height:120},initial[1]]),gaps([{...initialG[0],height:120},initialG[1]]),[100,230],[100,300]); +named('source reorder B A',absolute([initial[1],initial[0]]),gaps([initialG[1],initialG[0]]),[200,230],[80,200]); +named('delete A',absolute([initial[1]]),gaps([initialG[1]]),[200],[80]); +assert.deepEqual(positions(moveAbsolute(initial,1,110).preview),[100,130]); +let feasible=0,moves=0,absoluteChanges=0,gapChanges=0; +const started=performance.now(); +for(const ha of [10,20,30])for(const hb of [10,20,30])for(const hc of [10,20,30]) +for(let a=0;a<=40;a+=10)for(let b=a+ha+10;b<=90;b+=10)for(let c=b+hb+10;c<=140;c+=10) { + const rows=[{id:'A',height:ha,y:a},{id:'B',height:hb,y:b},{id:'C',height:hc,y:c}]; + assert.deepEqual(positions(absolute(rows)),[a,b,c]); + assert.deepEqual(positions(gaps(encodeGaps(rows))),[a,b,c]); + feasible++; + for(let i=0;i<3;i++)for(const delta of [-50,-20,0,10,40]) { + const requested=rows[i].y+delta, {candidate,preview}=moveAbsolute(rows,i,requested); + const independent=[]; + for(let j=0;j<3;j++) { + const floor=j?independent[j-1]+rows[j-1].height+10:0; + independent.push(Math.max(j===i?requested:rows[j].y,floor)); + } + assert.deepEqual(positions(preview),independent); + assert.deepEqual(positions(absolute(preview)),independent); + assert.deepEqual(positions(absolute(JSON.parse(JSON.stringify(candidate)))),independent); + const encoded=encodeGaps(preview); + assert.deepEqual(positions(gaps(encoded)),independent); + assert.deepEqual(candidate.map(r=>r.id),['A','B','C']); + for(let j=0;jr.y+r.height))+20; +assert.equal(requiredFrameBottom,300); +assert.equal(Math.max(300,requiredFrameBottom),300); +assert.equal(Math.max(220,requiredFrameBottom),300); +assert.deepEqual(children,[{y:160,height:20},{y:260,height:20}]); +assert.equal(220-90,130); // delayed header top from moved create receive and header height +const result={ + scope:'Illustrative linear policy and anchor experiment; no production parser, REST, Scene, GUI or full fragment/lifecycle acceptance.', + separation, cases, feasibleLayouts:feasible, moveCases:moves, + changedAuthoredCoordinateRecords:{absolute:absoluteChanges,gapsWithInverse:gapChanges}, + elapsedMs:performance.now()-started, + assertions:'passed', + interpretation:'Both policies express all enumerated feasible layouts. Absolute coordinates preserve unaffected positions with fewer authored coordinate changes under these fixtures. Choose no production schema from this alone; full scope/anchor/inverse and source lifecycle validation remain required.' +}; +await writeFile(new URL('./results.json',import.meta.url),JSON.stringify(result,null,2)+'\n',{flag:'wx'}); +export default result; diff --git a/harness/experiments/behavior-layout-policy-20260912/hashes.json b/harness/experiments/behavior-layout-policy-20260912/hashes.json new file mode 100644 index 00000000..2fa1ef54 --- /dev/null +++ b/harness/experiments/behavior-layout-policy-20260912/hashes.json @@ -0,0 +1,22 @@ +[ + { + "file": "experiment.mjs", + "bytes": 5123, + "sha256": "8a2b5adc35a25173ec2d37a46572cebac12d73bc8c9440f3cfdff784858c52c1" + }, + { + "file": "results.json", + "bytes": 1633, + "sha256": "866e4c81c42a922033669aa6c591857029303627db28c5307b0126503a6791b8" + }, + { + "file": "negative-zero-motion.mjs", + "bytes": 5383, + "sha256": "b76b1aa1c5d19043540381fc31224ecb86298796fb3adf2cdc8847c412b034ce" + }, + { + "file": "negative-zero-motion-result.json", + "bytes": 707, + "sha256": "1dab9972fdef1586e48c89f5afe8dd6a3cfa5d58adf68088e74440dbf4eb70a9" + } +] diff --git a/harness/experiments/behavior-layout-policy-20260912/independent-review.md b/harness/experiments/behavior-layout-policy-20260912/independent-review.md new file mode 100644 index 00000000..baa8b588 --- /dev/null +++ b/harness/experiments/behavior-layout-policy-20260912/independent-review.md @@ -0,0 +1,55 @@ +# Review of the isolated layout-policy experiment + +2026-09-12. Independent reviewer `/root/diagram_usage_workflows`; read [experiment.mjs](experiment.mjs) and [results.json](results.json). Root reports executing the experiment. This review inspected code/results without rerunning them; no production edits, builds, UI, or Gitea operations. Local links are for this shared agent workspace; root will publish portable artifacts before any external peer handoff. + +## Findings and justified conclusion + +**Useful result:** both encodings reproduce all 3,555 enumerated feasible three-row layouts. Under the particular forward-only, minimum-gap movement policy, a gap inverse can reproduce the absolute planner's 53,325 single-move previews. This rebuts the idea that gap storage inherently prevents precise placement. Absolute coordinates are a reasonable candidate for the next experiment. + +**Not established:** the best production policy, equivalent edit-history behavior, source/API parity, real scenario anchors, fragments/lifetimes, minimal record changes in general, responsiveness, or schema adoption. The result file correctly disclaims most of these. Its “fewer authored coordinate changes” claim should be scoped to single edits from initially feasible layouts, using this forward resolver and this gap encoding. It is not yet a fair comparison of equally expressive persistent intent across histories. + +### 1. A concrete omitted history violates the proposed zero-movement contract + +Starting authored A100/B200, height20 each: + +1. `moveAbsolute(rows, A, 250)` stores A250/B200 and displays A250/B280. +2. Select B and release at unchanged displayed y280. +3. `moveAbsolute(candidate, B, 280)` stores B280, changing its previous desired200 despite no screen movement. + +The experiment starts each move from a feasible layout, so its delta=0 cases cannot find this. A pointer gesture should carry its original frame, original authored candidate and actual pointer delta, returning that original candidate for zero delta before inverse mapping. That is an additional required contract, not something tested here. An explicit text/API `SetDesiredY` may legitimately change latent authored intent even when the current projected coordinate is equal; explicitly applying the displayed coordinate through Properties could likewise be a meaningful normalization operation. The toy function has no operation-intent input and is not a complete gesture planner. An untouched pointer gesture must remain a no-op. + +Another important history is moving A back to100. Retaining absolute B.desired=200 restores B200, whereas the first gap inverse encoded the displaced output B280 and did not preserve B's original desired position. A gap planner can preserve the current visible B280 or use extra latent intent to restore200; choose the intended behavior explicitly. Neither result is intrinsically correct, but the current counters compare representations containing different information after the first conflict. + +### 2. Oracle independence is partial + +- The six named example outputs are hand-authored numerical expectations. They are useful independent checks of the proposed policy examples, though they do not exercise Studio. +- The loop called `independent` repeats the same left-to-right `max(requested-or-old, predecessor-bottom+10)` recurrence as `moveAbsolute` plus `absolute`. It can catch implementation inconsistencies but does not independently establish that the chosen movement policy matches user intent or is the least disruptive feasible solution. +- `absolute(preview)` checks idempotence on already resolved geometry. It does not prove that authored unresolved intent survives a sequence of commands or that the inverse preserves inactive fields. +- JSON stringify/parse of plain toy records demonstrates only that these small numeric values survive JavaScript JSON. There is no Arcadia grammar, source comment ownership, schema version, f32 precision, REST conditional transaction, or Document history exercised. +- Checking the three IDs remain A/B/C and earlier rows are unchanged is useful but does not exercise steps, sender/receiver, reply_to, branch membership, or lifecycle semantics. +- “Independent event/frame attachment probes” is too strong a label. `250+24=274`, `max(child bottoms)+20=300`, and `220-90=130` are arithmetic expectations. They do not invoke a self-message, frame resize or lifetime implementation. The unchanged-children assertion observes an array no code attempted to change. Treat these as fixture seeds for future tests. + +### 3. Fairness and coverage of the comparison + +Encoding the same final preview with `encodeGaps` is a fair test of gap expressiveness and a narrowly useful coordinate-update count. It is not an independent gap-policy optimizer: the desired final layout comes from the absolute policy. The named gap cases intentionally omit an inverse and should illustrate naive behavior only, not count as evidence against gap-based direct manipulation. + +The recorded totals, 37,134 absolute-coordinate changes versus 61,371 gap changes, exclude latent-intent equivalence, bytes, comments, identities, transactions, secondary geometry and histories. An absolute edit leaves displaced followers' authored y unchanged; the gap inverse rewrites them to encode resolved positions. Count logical preserved intent, visible displacement and serialized changes separately. Also remove unused y fields from the conceptual gap representation when estimating storage; `encodeGaps` currently retains them through object spread even though `gaps` ignores/overwrites them. + +The enumerator covers nonnegative integer tops on a ten-unit grid, three rows, fixed minimum separation, and five deltas. The third height varies but constrains no successor, so the count contains geometrically equivalent positioning cases. This does not invalidate the result; it limits the diversity implied by the headline number. Negative/fractional/extreme coordinates, variable per-kind clearances, mixed resize/move sequences, preexisting conflicts, simultaneous branches, nested frames and route geometry are absent. The elapsed time includes assertions and allocations; it is not a renderer performance measurement. + +### 4. Important absent lifecycle/source cases + +The isolated source-reorder and delete examples change array order/membership but test no cleanup or diagnostics. Missing are: repeated source reorder; deleting a displaced predecessor; insert between authored neighbors; rename/content growth; reparent between operands; reorder across call/reply or creation/deletion constraints; hidden records; reset one override; two saved views; first-edit identity capture; undo/redo; and comments keyed by typed addresses. Frame anchors need header/operand containment, while self calls need both send and receive geometry consumed by activation logic. These are specification gates, not reasons to expand the toy to thousands more similar rows. + +## Bounded next experiment + +First add a small history table to the isolated experiment: conflict then zero-distance drag, move away/back, resize away/back, delete predecessor, and source reorder/reverse. Assert authored records and displayed positions separately. Use a tiny exhaustive feasible-layout oracle with a declared lexicographic objective (selected target error, predecessor changes, other displacement, then record changes) to check a few cases without duplicating the forward recurrence. The objective itself remains a policy proposal to review. Run both candidate representations through their own repeated histories rather than deriving every gap state from an absolute preview. + +Then stop scaling the toy and make **one real-model renderer experiment**, isolated from the production schema: + +1. Parse a real minimal Arcadia scenario through the existing parser: two lifelines, separate call/reply occurrences using one exchange, and an action/state box between them. Use an experimental presentation adapter in a test boundary consumed by the actual Scene geometry; do not relocate the entire renderer or invent production syntax yet. +2. Exercise call movement that displaces the box/reply, zero-motion on the displaced box, movement back, and box-height changes. Record independent numeric anchor expectations and compare actual rendered bounds/line endpoints. Execute a valid source/SetSteps reorder of the independent box and confirm the semantic order, model validation and presentation diagnostic. The semantic Project must remain unchanged during cosmetic previews. +3. Extend that same bounded fixture with one nested operand and a self-message, then a delayed-created lifeline case. Assert send/receive anchors, header attachment, frame containment and child authored-coordinate preservation against the actual Scene/lifecycle outputs. Coordinate with #16 and #21 owners; do not duplicate or replace their implementation. +4. Compare absolute and gap adapters on those identical requested layouts and histories. Capture unmet constraints explicitly; do not auto-normalize source or silently reorder behavior. Select the candidate only after defining its conflict and inverse behavior. + +This next experiment can validate real semantic/geometry integration without pretending to prove persistence. Once it passes, the next separate slice is the smallest versioned typed-layout record through parser/formatter, Document transaction, JSON/REST and reopen. Native editing and usability remain subsequent required evidence. Publish the exact experiment source, inputs and results together with this review so remote peers can reproduce the bounded claims. diff --git a/harness/experiments/behavior-layout-policy-20260912/negative-zero-motion-result.json b/harness/experiments/behavior-layout-policy-20260912/negative-zero-motion-result.json new file mode 100644 index 00000000..67d5628d --- /dev/null +++ b/harness/experiments/behavior-layout-policy-20260912/negative-zero-motion-result.json @@ -0,0 +1,29 @@ +{ + "status": "expected assertion failure reproduced", + "assertion": "zero-distance pointer operation must preserve authored intent\n+ actual - expected\n... Skipped lines\n\n [\n {\n height: 20,\n id: 'A',\n y: 250\n...\n id: 'B',\n+ y: 280\n- y: 200\n }\n ]\n", + "actual": [ + { + "id": "A", + "y": 250, + "height": 20 + }, + { + "id": "B", + "y": 280, + "height": 20 + } + ], + "expected": [ + { + "id": "A", + "y": 250, + "height": 20 + }, + { + "id": "B", + "y": 200, + "height": 20 + } + ], + "scope": "Same toy planner, additional conflict-then-zero-motion history; no production failure claim." +} diff --git a/harness/experiments/behavior-layout-policy-20260912/negative-zero-motion.mjs b/harness/experiments/behavior-layout-policy-20260912/negative-zero-motion.mjs new file mode 100644 index 00000000..7fc0e37d --- /dev/null +++ b/harness/experiments/behavior-layout-policy-20260912/negative-zero-motion.mjs @@ -0,0 +1,90 @@ +import assert from 'node:assert/strict'; +import { writeFile } from 'node:fs/promises'; +import { performance } from 'node:perf_hooks'; +// Isolated policy comparison; these illustrative metrics are not Studio rules. +const separation = 10; +function absolute(rows) { + let end = -separation; + return rows.map(r => { const y = Math.max(r.y, end + separation); end=y+r.height; return {...r,y}; }); +} +function gaps(rows) { + let end=0; + return rows.map((r,i) => { assert(r.gap >= (i?separation:0)); const y=end+r.gap; end=y+r.height; return {...r,y}; }); +} +function encodeGaps(rows) { + return rows.map((r,i)=>({...r,gap:r.y-(i?rows[i-1].y+rows[i-1].height:0)})); +} +const positions = rows=>rows.map(r=>r.y); +function moveAbsolute(rows,index,requested) { + const current=absolute(rows); + const floor=index?current[index-1].y+current[index-1].height+separation:0; + const candidate=rows.map((r,i)=>({...r,y:i===index?Math.max(requested,floor):r.y})); + return {candidate,preview:absolute(candidate)}; +} +function changeCount(before,after,key) {return before.reduce((n,r,i)=>n+(r[key]!==after[i][key]),0);} +const initial=[{id:'A',y:100,height:20},{id:'B',y:200,height:20}]; +const initialG=encodeGaps(initial); +const cases=[]; +function named(name,a,g,expectedA,expectedG) { + assert.deepEqual(positions(a),expectedA,name+' absolute'); + assert.deepEqual(positions(g),expectedG,name+' gaps'); + cases.push({name,absolute:positions(a),gapsWithoutInverse:positions(g)}); +} +named('move A to50',absolute([{...initial[0],y:50},initial[1]]),gaps([{...initialG[0],gap:50},initialG[1]]),[50,200],[50,150]); +named('move A to250',absolute([{...initial[0],y:250},initial[1]]),gaps([{...initialG[0],gap:250},initialG[1]]),[250,280],[250,350]); +named('resize A to80',absolute([{...initial[0],height:80},initial[1]]),gaps([{...initialG[0],height:80},initialG[1]]),[100,200],[100,260]); +named('resize A to120',absolute([{...initial[0],height:120},initial[1]]),gaps([{...initialG[0],height:120},initialG[1]]),[100,230],[100,300]); +named('source reorder B A',absolute([initial[1],initial[0]]),gaps([initialG[1],initialG[0]]),[200,230],[80,200]); +named('delete A',absolute([initial[1]]),gaps([initialG[1]]),[200],[80]); +assert.deepEqual(positions(moveAbsolute(initial,1,110).preview),[100,130]); +let feasible=0,moves=0,absoluteChanges=0,gapChanges=0; +const started=performance.now(); +for(const ha of [10,20,30])for(const hb of [10,20,30])for(const hc of [10,20,30]) +for(let a=0;a<=40;a+=10)for(let b=a+ha+10;b<=90;b+=10)for(let c=b+hb+10;c<=140;c+=10) { + const rows=[{id:'A',height:ha,y:a},{id:'B',height:hb,y:b},{id:'C',height:hc,y:c}]; + assert.deepEqual(positions(absolute(rows)),[a,b,c]); + assert.deepEqual(positions(gaps(encodeGaps(rows))),[a,b,c]); + feasible++; + for(let i=0;i<3;i++)for(const delta of [-50,-20,0,10,40]) { + const requested=rows[i].y+delta, {candidate,preview}=moveAbsolute(rows,i,requested); + const independent=[]; + for(let j=0;j<3;j++) { + const floor=j?independent[j-1]+rows[j-1].height+10:0; + independent.push(Math.max(j===i?requested:rows[j].y,floor)); + } + assert.deepEqual(positions(preview),independent); + assert.deepEqual(positions(absolute(preview)),independent); + assert.deepEqual(positions(absolute(JSON.parse(JSON.stringify(candidate)))),independent); + const encoded=encodeGaps(preview); + assert.deepEqual(positions(gaps(encoded)),independent); + assert.deepEqual(candidate.map(r=>r.id),['A','B','C']); + for(let j=0;jr.y+r.height))+20; +assert.equal(requiredFrameBottom,300); +assert.equal(Math.max(300,requiredFrameBottom),300); +assert.equal(Math.max(220,requiredFrameBottom),300); +assert.deepEqual(children,[{y:160,height:20},{y:260,height:20}]); +assert.equal(220-90,130); // delayed header top from moved create receive and header height +const conflicting = moveAbsolute(initial,0,250); +const zeroMotion = moveAbsolute(conflicting.candidate,1,conflicting.preview[1].y); +assert.deepEqual(zeroMotion.candidate, conflicting.candidate, 'zero-distance pointer operation must preserve authored intent'); +const result={ + scope:'Illustrative linear policy and anchor experiment; no production parser, REST, Scene, GUI or full fragment/lifecycle acceptance.', + separation, cases, feasibleLayouts:feasible, moveCases:moves, + changedAuthoredCoordinateRecords:{absolute:absoluteChanges,gapsWithInverse:gapChanges}, + elapsedMs:performance.now()-started, + assertions:'passed', + interpretation:'Both policies express all enumerated feasible layouts. Absolute coordinates preserve unaffected positions with fewer authored coordinate changes under these fixtures. Choose no production schema from this alone; full scope/anchor/inverse and source lifecycle validation remain required.' +}; +await writeFile(new URL('./results.json',import.meta.url),JSON.stringify(result,null,2)+'\n',{flag:'wx'}); +export default result; diff --git a/harness/experiments/behavior-layout-policy-20260912/results.json b/harness/experiments/behavior-layout-policy-20260912/results.json new file mode 100644 index 00000000..993e70f3 --- /dev/null +++ b/harness/experiments/behavior-layout-policy-20260912/results.json @@ -0,0 +1,79 @@ +{ + "scope": "Illustrative linear policy and anchor experiment; no production parser, REST, Scene, GUI or full fragment/lifecycle acceptance.", + "separation": 10, + "cases": [ + { + "name": "move A to50", + "absolute": [ + 50, + 200 + ], + "gapsWithoutInverse": [ + 50, + 150 + ] + }, + { + "name": "move A to250", + "absolute": [ + 250, + 280 + ], + "gapsWithoutInverse": [ + 250, + 350 + ] + }, + { + "name": "resize A to80", + "absolute": [ + 100, + 200 + ], + "gapsWithoutInverse": [ + 100, + 260 + ] + }, + { + "name": "resize A to120", + "absolute": [ + 100, + 230 + ], + "gapsWithoutInverse": [ + 100, + 300 + ] + }, + { + "name": "source reorder B A", + "absolute": [ + 200, + 230 + ], + "gapsWithoutInverse": [ + 80, + 200 + ] + }, + { + "name": "delete A", + "absolute": [ + 200 + ], + "gapsWithoutInverse": [ + 80 + ] + } + ], + "feasibleLayouts": 3555, + "moveCases": 53325, + "changedAuthoredCoordinateRecords": { + "absolute": 37134, + "gapsWithInverse": 61371 + }, + "elapsedMs": 24777.856600000057, + "assertions": "passed", + "interpretation": "Both policies express all enumerated feasible layouts. Absolute coordinates preserve unaffected positions with fewer authored coordinate changes under these fixtures. Choose no production schema from this alone; full scope/anchor/inverse and source lifecycle validation remain required." +}