fix(ai): JSONL fallback null-parent index reads active page, not legacy field

`store.addNode(null, …)` routes the insert through `_children()` →
`getActivePageChildren(doc, activePageId)` — meaning the parent list
is the ACTIVE PAGE's children, not `doc.children`. The previous
append-index calc read `doc.children?.length` directly, which only
holds the legacy single-page fallback array. On a multi-page doc the
two diverge: `doc.children` may be empty or stale while the active
page already has N siblings, so the computed append index doesn't
correspond to the actual insertion target — landing either before
existing siblings (off-by-N) or out of bounds.

Use `getActivePageChildren(document, activePageId)` to read the same
list `addNode` writes into. Sub-agent generation runs on whichever
page the user has active, so this matches dispatch behavior exactly.

The non-null parent path (`getNodeById(parentId)` then read its
children length) was already correct — only the null-parent branch
needed fixing.
This commit is contained in:
Fini 2026-05-05 01:57:50 +08:00
parent f7412cb26c
commit a0e84763d2

View file

@ -41,6 +41,7 @@ import { insertStreamingNode } from './design-canvas-ops';
// this dispatcher actually needs and is kept rigorously browser-safe
// (enforced by `packages/pen-mcp/src/__tests__/batch-design-dsl-browser-safe.test.ts`).
import { runBatchDesignDsl } from '@zseven-w/pen-mcp/dsl';
import { getActivePageChildren } from '@zseven-w/pen-core';
import type { PenNode } from '@/types/pen';
/**
@ -570,7 +571,17 @@ function applyBatchDesignAsJsonl(jsonl: string, ctx: DispatchContext): DispatchR
// Compute the parent's current child count for each insert and pass
// it as the explicit index so roots land in their generation order.
const computeAppendIndex = (): number => {
if (parentId === null) return useDocumentStore.getState().document.children?.length ?? 0;
if (parentId === null) {
// `addNode` writes into the ACTIVE PAGE's children (see
// document-store-node-actions.ts::_children → getActivePageChildren).
// Reading `document.children` directly would only return the legacy
// single-page fallback array — on a multi-page doc that's NOT
// where the inserts land, so the resulting append index would
// disagree with the live child list and either skip past existing
// siblings or land out of bounds.
const activePageId = useCanvasStore.getState().activePageId;
return getActivePageChildren(useDocumentStore.getState().document, activePageId).length;
}
const parentNode = useDocumentStore.getState().getNodeById(parentId);
if (
parentNode &&