The image-count pricing regression was passing an override into a helper that ignored it, so the test exercised the one-image default while expecting the four-image total. This makes the fixture accept the same node data overrides as the lower-level image node factory without touching production pricing logic.
Constraint: Remote master already contains the UI change that hides unimplemented image toolbar actions.
Rejected: Change GenerationComposer point calculation | the failing evidence showed draft.imageCount was never set by the fixture.
Confidence: high
Scope-risk: narrow
Tested: npm run test:run -- src/components/__tests__/ImageInputNode.test.tsx src/app/api/generate/providers/__tests__/newapiwg.test.ts src/app/api/points/estimate/__tests__/route.test.ts src/components/__tests__/GenerationComposer.test.tsx src/store/execution/__tests__/generateVideoExecutor.test.ts
Tested: npm run build
Tested: git diff --check
Popi/NewAPIWG is now the primary gateway, so the UI and request paths need to avoid legacy provider assumptions while keeping local reference media usable for upstream calls. This change aligns model defaults, point display, inline node controls, and temporary reference image handling around that gateway boundary.
Constraint: Upstream providers cannot fetch localhost reference URLs, but large base64 payloads previously caused request/body reliability issues.
Constraint: Seedance point estimates depend on authenticated NewAPIWG /api/points/estimate responses rather than static catalog prices.
Rejected: Send all reference images directly as large data URLs | would reintroduce oversized JSON/request failures.
Rejected: Restore legacy per-provider key controls by default | current product direction is a single Popi/NewAPI gateway.
Confidence: high
Scope-risk: moderate
Directive: Do not pass /api/images localhost URLs to upstream generation APIs; resolve them server-side or fail before the upstream call.
Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts src/app/api/points/estimate/__tests__/route.test.ts src/lib/images/store.test.ts src/components/__tests__/GenerationComposer.test.tsx src/components/__tests__/ImageInputNode.test.tsx src/components/__tests__/PromptNode.test.tsx src/components/__tests__/ProjectSetupModal.test.tsx src/store/execution/__tests__/generateVideoExecutor.test.ts src/store/execution/__tests__/nanoBananaExecutor.test.ts src/store/utils/__tests__/nodeDefaults.test.ts
Tested: npm run build
Tested: git diff --check
Not-tested: npm run lint fails because next lint is parsed as a missing /lint project directory under Next 16.
Multi-image NewAPIWG generations were sending base64 reference images
inside the /api/generate JSON body. Two ordinary canvas references can
push that payload near the dev/server body boundary, leaving the route
to parse an incomplete JSON string and surface a raw syntax error.
Move large NewAPIWG references through the existing in-memory image
serving path: the browser uploads large data URLs as multipart image
files, sends short /api/images/{id} URLs in the generation request, and
the route resolves those temporary URLs back to server-local image data
before calling providers. The route also turns malformed/truncated JSON
into a readable validation error.
Constraint: Keep provider payload semantics unchanged after /api/generate receives the request
Constraint: Do not rely on request origin matching because Next dev can report 0.0.0.0 while the browser uses localhost
Rejected: Increase body size limits only | still sends multi-MB JSON and does not protect hosted/proxied environments
Rejected: Compress reference images client-side | changes model input fidelity and still leaves large JSON possible
Confidence: high
Scope-risk: moderate
Directive: Do not forward /api/images/{id} temporary URLs to external providers; resolve them server-side first
Tested: npm run test:run -- src/store/execution/__tests__/nanoBananaExecutor.test.ts src/app/api/generate/__tests__/route.test.ts
Tested: npm run build
Not-tested: Live NewAPIWG generation with the user's exact two images
The welcome screen now matches the Popi.TV brand and gives unauthenticated users two explicit paths: jump to the main-site login page or provide a Popi/NewAPI gateway key directly. The manually provided key is persisted in the local user store and forwarded through the NewAPI-backed generation, model, LLM, and chat routes without requiring a login token.
Constraint: Users need a usable local path when they are not logged into the main site.\nRejected: Keep the startup login modal for missing tokens | it blocked the in-welcome login/key choices.\nRejected: Rename internal workflow prompts and storage keys | those are compatibility/internal identifiers, not the visible brand surface.\nConfidence: high\nScope-risk: moderate\nDirective: Keep manual gateway-key access scoped to NewAPI-backed paths; non-NewAPI providers should continue requiring a login token.\nTested: npm run test:run -- src/app/api/models/__tests__/route.test.ts src/app/api/models/[modelId]/__tests__/route.test.ts src/app/api/llm/__tests__/route.test.ts src/app/api/chat/__tests__/route.test.ts src/app/api/generate/__tests__/route.test.ts\nTested: npm run test:run -- src/components/__tests__/QuickstartInitialView.test.tsx src/components/__tests__/WelcomeModal.test.tsx\nTested: npm run build\nTested: Browser verification for Popi.TV welcome title and two unauthenticated entry paths\nTested: git diff --cached --check\nNot-tested: Full test suite\nNot-tested: npm run lint still fails because the existing Next 16 next lint script resolves /lint as a project directory
The welcome screen now matches the Popi.TV brand and gives unauthenticated users two explicit paths: jump to the main-site login page or provide a Popi/NewAPI gateway key directly. The manually provided key is persisted in the local user store and forwarded through the NewAPI-backed generation, model, LLM, and chat routes without requiring a login token.
Constraint: Users need a usable local path when they are not logged into the main site.\nRejected: Keep the startup login modal for missing tokens | it blocked the in-welcome login/key choices.\nRejected: Rename internal workflow prompts and storage keys | those are compatibility/internal identifiers, not the visible brand surface.\nConfidence: high\nScope-risk: moderate\nDirective: Keep manual gateway-key access scoped to NewAPI-backed paths; non-NewAPI providers should continue requiring a login token.\nTested: npm run test:run -- src/app/api/models/__tests__/route.test.ts src/app/api/models/[modelId]/__tests__/route.test.ts src/app/api/llm/__tests__/route.test.ts src/app/api/chat/__tests__/route.test.ts src/app/api/generate/__tests__/route.test.ts\nTested: npm run test:run -- src/components/__tests__/QuickstartInitialView.test.tsx src/components/__tests__/WelcomeModal.test.tsx\nTested: npm run build\nTested: Browser verification for Popi.TV welcome title and two unauthenticated entry paths\nTested: git diff --cached --check\nNot-tested: Full test suite\nNot-tested: npm run lint still fails because the existing Next 16 next lint script resolves /lint as a project directory
Browser directory picking can be intercepted by embedded automation environments, which raises before the user can choose a folder. The project setup browse flow now treats that specific interception as a signal to use the existing server browse-directory fallback, while preserving normal browser picker behavior and cancellation semantics.
Constraint: In-app browser automation may install Page.setInterceptFileChooserDialog and break showDirectoryPicker
Constraint: Browser File System Access selection should still be preferred when it works
Rejected: Always fall back after any browser picker error | permission and unsupported errors should remain visible instead of silently choosing a server path
Confidence: high
Scope-risk: narrow
Directive: Do not broaden fallback errors without checking browser local-project semantics
Tested: npm run test:run -- src/components/__tests__/ProjectSetupModal.test.tsx --testNamePattern "fall back to browse-directory API"
Tested: npm run test:run -- src/components/__tests__/ProjectSetupModal.test.tsx --testNamePattern "Browse Button"
Tested: npm run test:run -- src/components/__tests__/ProjectSetupModal.test.tsx --testNamePattern "fall back to browse-directory API|should call browse-directory API|should keep selected parent path|should handle cancelled browse dialog"
Tested: npm run build
Not-tested: npm run lint | existing Next 16 next lint script resolves to nonexistent /lint directory
Not-tested: Full ProjectSetupModal test file | existing Provider tab assertions expect API key inputs hidden by current Popi mode
The app now starts I18nProvider in zh-CN when no saved language exists, while preserving explicit saved choices. The login-required Ant Design modal also uses the v6 mask.closable shape so the console no longer warns about maskClosable.
Constraint: First-run locale should not override an existing saved language preference
Constraint: Ant Design 6 deprecates maskClosable on Modal.confirm
Rejected: Keep browser-language detection | first entry needs deterministic Simplified Chinese
Confidence: high
Scope-risk: narrow
Directive: Keep provider-less useI18n fallback stable for isolated tests unless all callers are wrapped in I18nProvider
Tested: npm run test:run -- src/i18n/__tests__/index.test.tsx src/components/__tests__/TemplateExplorerView.test.tsx src/components/__tests__/Header.test.tsx src/utils/__tests__/loginRequiredModal.test.ts src/utils/__tests__/authFetch.test.ts src/store/__tests__/userStore.test.ts
Tested: npm run build
Not-tested: npm run lint | existing Next 16 next lint script resolves to nonexistent /lint directory
The canvas now treats the visible plus affordance as part of the output handle hit zone, so users can click or drag from the same right-side target. Empty image input nodes also handle double-click locally before the canvas node double-click centering logic sees the event.
Constraint: The plus affordance must not block React Flow handle drag events
Rejected: Keep the plus as the primary pointer target | it prevented drag-to-connect from the same visual area
Confidence: high
Scope-risk: moderate
Directive: Keep quick-add visuals non-interactive unless handle dragging is reworked at the React Flow layer
Tested: npm run test:run -- src/components/__tests__/ImageInputNode.test.tsx src/components/__tests__/WorkflowCanvas.test.tsx
Tested: npm run build
Not-tested: npm run lint | existing Next 16 next lint script resolves to nonexistent /lint directory
This keeps the testing branch aligned with the current Popi.TV interaction direction: generation is controlled from the selected node context, model/provider selection is simplified around the NewAPI gateway path, Popiai embedding can hydrate workflow state, and media nodes expose names plus dimensions in the canvas header.
Constraint: Testing branch needs the full current source state, not runtime logs or local browser artifacts
Rejected: Commit logs, browserfs state, and handoff bundles | they are local runtime artifacts rather than application source
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep model selection consolidated around the NewAPI gateway unless supplier-specific behavior is intentionally reintroduced
Tested: npm run test:run -- src/components/__tests__/FloatingNodeHeader.test.tsx src/components/__tests__/nodeHeaderMedia.test.ts src/components/__tests__/WorkflowCanvas.test.tsx
Tested: npm run test:run -- src/components/__tests__/ImageInputNode.test.tsx src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/GenerateImageNode.test.tsx
Tested: npm run build
Tested: git diff --check -- src
Not-tested: npm run lint | existing script uses next lint and resolves to nonexistent ./lint directory
The current fixed bottom composer separates node inspection from node editing and competes with graph space. This design records the approved direction to make selected nodes the center of editing, with a contextual toolbar and inline editor that reuse composer behavior instead of duplicating it.
Constraint: Must follow the Minimax Hub display principle requested for node interaction
Rejected: Keep fixed bottom composer as the primary editor | it preserves a global surface that hides graph content and separates action from object
Confidence: high
Scope-risk: narrow
Directive: Extract composer behavior before replacing the UI surface; do not duplicate prompt/model generation logic
Tested: Spec self-review for placeholders and git diff whitespace check
Not-tested: Runtime implementation because this commit is design-only
TEST-s needs the same default image generation recovery path as master so the testing deployment does not fail when apiyi_nano_banana_2 returns transient overloads. This merge preserves the testing branch history and carries the verified master fix forward.
Constraint: Testing deployments track TEST-s
Rejected: Force-update TEST-s to master | would discard branch history used by the deployment lane
Confidence: high
Scope-risk: moderate
Directive: Keep TEST-s merged from master after verified fixes
Tested: npm run test:run
Tested: npm run build
The default Popi Models image route can return transient upstream overloads for apiyi_nano_banana_2 even when the gateway configuration is valid. The executor now uses gpt-image-2-all as an implicit same-gateway fallback only for overload-shaped errors, while preserving explicit fallback behavior and surfacing non-overload errors unchanged.
Constraint: The gateway can intermittently return system_disk_overloaded for the default image model
Rejected: Change the default model globally | would alter every new image node instead of only recovering transient failures
Rejected: Fallback on every error | would hide prompt, safety, or request-shape problems
Confidence: high
Scope-risk: moderate
Directive: Keep implicit fallback predicates narrow so user-actionable errors remain visible
Tested: npm run test:run
Tested: npm run build
TEST-s had the Popi.TV and browserfs fixes as cherry-picked commits, while master also carried the connected-output media release commit. Merging master preserves TEST-s history and brings the testing deployment branch onto the same verified release baseline without rewriting the branch.
Constraint: Testing deployment appears to track TEST-s while master is the canonical release branch
Rejected: Force-update TEST-s to master | would discard the existing TEST-s branch history
Confidence: high
Scope-risk: moderate
Directive: Keep TEST-s merged from master for deploy parity unless intentionally testing divergent changes
Tested: npm run test:run
Tested: npm run build
Not-tested: Direct server restart blocked by missing SSH credentials
Browser-file-system workflow saves were still bypassing external media storage, so local projects could keep large image, video, and audio data embedded instead of writing refs into the selected browser-backed workspace. The storage path now uses the browser file helpers for image, video, and audio refs and hydrates them back without API calls.
Constraint: Keep local browserfs storage on the existing external media storage path instead of adding a new persistence mode.
Rejected: Keep browserfs excluded from externalization | it preserves the old large-payload behavior for local projects.
Confidence: high
Scope-risk: moderate
Directive: Do not route browserfs media through server API endpoints; browserfs paths must stay client-local.
Tested: npm run test:run
Tested: npm run build
Tested: git diff --check -- src/store/workflowStore.ts src/utils/browserFileSystem.ts src/utils/mediaStorage.ts src/utils/__tests__/mediaStorage.browserfs.test.ts
Not-tested: npm run lint is blocked because next lint is no longer a valid Next 16 command in this repo
Not-tested: npx tsc --noEmit reports existing test-file type debt outside this change while next build TypeScript passes
Browser-file-system workflow saves were still bypassing external media storage, so local projects could keep large image, video, and audio data embedded instead of writing refs into the selected browser-backed workspace. The storage path now uses the browser file helpers for image, video, and audio refs and hydrates them back without API calls.
Constraint: Keep local browserfs storage on the existing external media storage path instead of adding a new persistence mode.
Rejected: Keep browserfs excluded from externalization | it preserves the old large-payload behavior for local projects.
Confidence: high
Scope-risk: moderate
Directive: Do not route browserfs media through server API endpoints; browserfs paths must stay client-local.
Tested: npm run test:run
Tested: npm run build
Tested: git diff --check -- src/store/workflowStore.ts src/utils/browserFileSystem.ts src/utils/mediaStorage.ts src/utils/__tests__/mediaStorage.browserfs.test.ts
Not-tested: npm run lint is blocked because next lint is no longer a valid Next 16 command in this repo
Not-tested: npx tsc --noEmit reports existing test-file type debt outside this change while next build TypeScript passes
The app header still exposed the internal popiart-node label while the requested product brand is Popi.TV. This keeps the existing icon asset but makes it decorative so the visible header label is the accessible brand signal.
Constraint: Keep the change scoped to the top-left header logo area.
Rejected: Rename all popiart-node strings globally | the request only targeted the top-left Logo.
Confidence: high
Scope-risk: narrow
Directive: Do not broaden this into a product-wide rename without checking welcome and onboarding copy.
Tested: npm run test:run -- src/components/__tests__/Header.test.tsx
The app header still exposed the internal popiart-node label while the requested product brand is Popi.TV. This keeps the existing icon asset but makes it decorative so the visible header label is the accessible brand signal.
Constraint: Keep the change scoped to the top-left header logo area.
Rejected: Rename all popiart-node strings globally | the request only targeted the top-left Logo.
Confidence: high
Scope-risk: narrow
Directive: Do not broaden this into a product-wide rename without checking welcome and onboarding copy.
Tested: npm run test:run -- src/components/__tests__/Header.test.tsx
Output nodes were collapsing multiple connected media inputs into one visible result, which made image/video/audio/3D connections look ignored. The node now preserves each connected media kind and presents explicit result switching with current/all download actions.
Constraint: Output node remains a result endpoint and does not transform media or create hidden downstream behavior
Rejected: Auto-merge media into one combined preview | would hide workflow structure and violate the endpoint-only product model
Confidence: high
Scope-risk: moderate
Directive: Keep output node behavior as explicit result display; route further processing through visible downstream nodes
Tested: npm test -- --run src/components/__tests__/OutputNode.test.tsx src/store/execution/__tests__/simpleNodeExecutors.test.ts
Tested: npm run build
Tested: npm run test:run
Tested: Playwright localhost smoke with no page or console errors
The bottom composer and right-side node settings both expose video duration, resolution, and sound. Selected video nodes now receive quick-control changes immediately, so both surfaces read the same node data instead of diverging until blur or generate.
Constraint: Bottom composer remains the primary quick control while existing node settings continue to read workflow store data
Rejected: Remove the node settings panel | broader product change outside this fix
Rejected: Convert all composer draft fields to immediate store writes | higher risk and unnecessary for the reported video parameter mismatch
Confidence: high
Scope-risk: narrow
Directive: Keep shared video parameters on GenerateVideoNodeData; do not introduce a second parameter source for quick controls
Tested: npm run test:run -- src/components/__tests__/GenerationComposer.test.tsx
Tested: npm run test:run -- src/components/__tests__/GenerateVideoNode.test.tsx
Tested: git diff --check
Not-tested: full lint/typecheck still blocked by existing repo script/type issues from prior verification
Seedream rejects image generation requests whose size resolves below its minimum pixel floor. The NewApiWG adapter now clamps Seedream pixel dimensions to the existing 2K area floor while preserving the requested aspect ratio and leaving non-Seedream image models unchanged.
Constraint: Provider error requires image size to be at least 3686400 pixels
Rejected: Change the composer default resolution | would affect unrelated image models and UI behavior
Confidence: high
Scope-risk: narrow
Directive: Keep Seedream size normalization in the provider adapter so UI controls remain provider-agnostic
Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts
Tested: npm run test:run -- src/app/api/generate/__tests__/route.test.ts -t NewApiWG provider
Not-tested: npm run lint currently fails because next lint is parsed as a missing lint directory
Not-tested: npx tsc --noEmit currently fails on pre-existing test type errors
Vidu-style video models can load parameter schemas that change the Generate Video node height and trigger React Flow dimension updates. The fix keeps schema-driven resize notifications stable, skips no-op node height writes, and ignores duplicate React Flow node changes before they enter the workflow store.
It also preserves completed media outputs when a stopped or reloaded workflow still has transient loading/skipped state, so existing video results do not appear stuck after recovery.
Constraint: The crash was reported from a browser state where choosing Vidu hit React's maximum update depth in onNodesChange.
Rejected: Clear browser storage as the fix | it would hide persisted workflow loops without fixing the update path.
Rejected: Remove dynamic video handles | visible media/text inputs are product behavior and required for model-specific workflows.
Confidence: high
Scope-risk: moderate
Directive: Keep React Flow dimension and schema resize writes idempotent; new node resize paths should no-op when dimensions are already applied.
Tested: npm test -- --run src/components/__tests__/GenerateVideoNode.test.tsx -t 'should not resize repeatedly'
Tested: npm test -- --run src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/ModelParameters.test.tsx src/store/__tests__/workflowStore.integration.test.ts src/store/__tests__/skipPropagation.test.ts src/store/execution/__tests__/generateVideoExecutor.test.ts
Tested: npm run build
Tested: npm run test:run
The accumulated fixes move the test build toward the current product shape: Popi models are the default provider surface, remote community workflow APIs are removed from the local template flow, media nodes expose accurate preview/preview-modal behavior, and Generate Video owns first-class duration, resolution, and sound defaults instead of burying those values in provider schemas.
The test suite was brought back in line with the product mode split: Popi provider mode is the default runtime, while multi-provider model search remains covered explicitly under multi mode.
Constraint: Product direction is canvas-owned workflow structure with Popi Models as the tested default provider path.
Rejected: Keep legacy community workflow API surface | user requested removing remote data paths and retaining local templates.
Rejected: Treat video duration/resolution/sound as hidden provider params | these are user-facing Generate Video controls.
Confidence: high
Scope-risk: broad
Directive: Do not reintroduce remote community template loading without a product decision on hosted templates and i18n ownership.
Tested: npm test -- --run src/store/utils/__tests__/nodeDefaults.test.ts src/store/execution/__tests__/generateVideoExecutor.test.ts src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/GenerationComposer.test.tsx
Tested: npm run build
Tested: npm test -- --run src/components/__tests__/ModelSearchDialog.test.tsx src/store/utils/__tests__/localStorage.test.ts
Tested: npm run test:run
Not-tested: Live provider generation calls against paid external APIs.
The bottom composer is the canonical image-generation control surface now. The legacy right-side control panel was still rendering schema defaults for selected image nodes, which made aspect and size controls disagree on the same operation.
Constraint: Image generation controls must stay consistent with the Popi composer UX
Rejected: Synchronize both panels | duplicating controls preserves the confusing two-source UI
Confidence: high
Scope-risk: narrow
Directive: Do not re-enable the right-side image panel unless it reads from the same composer-owned state
Tested: npm run test:run -- src/components/__tests__/ControlPanel.test.tsx
Tested: npm run test:run -- src/components/__tests__/GenerationComposer.test.tsx src/components/__tests__/ControlPanel.test.tsx
Tested: PROVIDER_MODE=popi NEXT_PUBLIC_PROVIDER_MODE=popi npm run build
Tested: Playwright localhost check for composer visible and legacy image settings absent
The composer top row no longer shows the placeholder Style, Mark, and Focus chips. Reference image upload remains available while those future controls are deferred.
Constraint: User asked to hide the highlighted area temporarily, not remove reference upload
Rejected: Disabling the buttons visually | hidden controls better avoid confusing unfinished affordances
Confidence: high
Scope-risk: narrow
Directive: Reintroduce these chips only with implemented behavior behind them
Tested: npm run test:run -- src/components/__tests__/GenerationComposer.test.tsx
Tested: PROVIDER_MODE=popi NEXT_PUBLIC_PROVIDER_MODE=popi npm run build
The bottom generation composer now treats reference thumbnails as previewable assets: clicking a thumbnail opens a portal lightbox with the full image, backdrop/click-close behavior, and Escape close support. The remove button stops propagation so deletion remains distinct from preview opening.
Constraint: Keep the interaction inside the existing composer surface without introducing a shared modal dependency
Rejected: Reusing output-node lightbox directly | it is coupled to node media state and download/video behavior
Confidence: high
Scope-risk: narrow
Directive: Keep thumbnail click and remove click separated; do not nest buttons
Tested: npm run test:run -- src/components/__tests__/GenerationComposer.test.tsx
Tested: PROVIDER_MODE=popi NEXT_PUBLIC_PROVIDER_MODE=popi npm run build
Tested: Playwright uploaded a reference image, opened the preview dialog, and closed it with Escape
Gemini native image edits can default to the input image dimensions unless the response format also carries the requested image shape. The NewApiWG Gemini payload now sends the existing imageConfig plus responseFormat.image so Popi 16:9 selections survive image-to-image generation while preserving the prior native config path.
Constraint: Gemini image edit defaults can keep the input image dimensions when output format is not explicit
Rejected: Changing composer or node sizing first | the selected aspect ratio already reached the API payload in tests
Confidence: medium
Scope-risk: narrow
Directive: Keep imageConfig and responseFormat in sync for Gemini-native NewApiWG image models
Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts src/app/api/generate/__tests__/route.test.ts
Tested: PROVIDER_MODE=popi NEXT_PUBLIC_PROVIDER_MODE=popi npm run build