Skip to content

feat: NEvo-inspired AI video lane (local ComfyUI + fal) & evolve_parameters - #140

Draft
Pantani wants to merge 12 commits into
mainfrom
claude/video-generation-integration-cbcffb
Draft

feat: NEvo-inspired AI video lane (local ComfyUI + fal) & evolve_parameters#140
Pantani wants to merge 12 commits into
mainfrom
claude/video-generation-integration-cbcffb

Conversation

@Pantani

@Pantani Pantani commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

Turns the NEvo evolutionary-video-synthesis paper (EPFL/JHU, arXiv 2607.02317, CC BY 4.0) into two shippable tdmcp capabilities. NEvo itself ships no code/weights/API and its fMRI-guided objective is out of scope, so this adopts its two reusable pieces: its off-the-shelf LTX-Video generator and its evolutionary-search method (retargeted to a TouchDesigner-measurable fitness). Study artifacts under _workspace/nevo/.

What's in it

create_ai_video + create_ai_video_backdrop (hosted or local video lane)

  • Layer 2 (create_ai_video) drops a generated clip into a Movie File In TOP; Layer 1 (create_ai_video_backdrop) wires a full moving backdrop (Movie File In → Level → Transform → Blur → Null) with live controls.
  • Provider-agnostic via TDMCP_VIDEO_GEN_PROVIDER:
    • comfyui — LTX-Video on a local GPU via ComfyUI REST: zero cost per generation, fully offline.
    • fal — hosted fal.ai (pay-per-generation), a fork of the imageGen fal provider.
  • New src/services/videoGen/ seam. Model default ltx-video, ltx-2 opt-in; schema rejects controls a model lacks. No new bridge endpoint. Ships assets/comfyui/ (API-format LTX workflow + setup README).
  • "Generate ahead, then play back live" — generation is not per-frame; only playback is real-time. Not to be confused with the StreamDiffusion path.

evolve_parameters (experimental, offline)

  • Adopts NEvo's categorical GA (μ+λ, population 20 / generations 6 / elite 0.3 / crossover 0.5 / mutation 0.2, hard cap pop×gen ≤ 300) over an operator-parameter genome, scored by a TD-measurable fitness: audio_energy (Info CHOP), top_luma, or top_motion.
  • Deterministic under seed (seeded RNG, no Math.random); losing candidates torn down each generation, only the champion rebuilt. Design-time search, not a live controller.

Supporting

  • P0: cherry-picked the hosted AI-media (imageGen) lane that these extend (it lived only on a sibling branch, not on main).
  • Fixed a real bug found while prepping local validation: ComfyUI LTXV length is frames (k·8+1), not seconds — now converted.
  • DRY: extracted the shared PNG-decode primitive (src/feedback/pngDecode.ts) used by frameStats + captionTop (behavior-preserving).
  • Verified LTX license (LTX-2 Community License, free < $10M ARR).

Testing

  • Offline gates green: biome (1427 files), typecheck, build, 5632 vitest, bridge tests.
  • UNVERIFIED — live (TD bridge + GPU offline in this env): fal img2vid contract, local ComfyUI REST round-trip on a 24 GB GPU, moviefileinTOP first-frame/codec, evolve_parameters cook-settle/delete-churn. Probe steps are in assets/comfyui/README.md and the _workspace/04_qa_*.md reports.

No release tag cut (project policy: [Unreleased] only).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added AI image and video generation tools, including backdrop creation and local or hosted provider support.
    • Added experimental parameter evolution using deterministic genetic search.
    • Added caching for generated media and delivery into TouchDesigner.
    • Added frame-statistics and motion analysis capabilities.
    • Added a structured NEvo integration study and drafting workflow.
  • Documentation
    • Added ComfyUI/LTX-Video setup, configuration, and testing guidance.
    • Updated project guidance and changelog with the new capabilities.
  • Tests
    • Added broad coverage for media generation, caching, provider flows, motion analysis, and parameter evolution.

Pantani and others added 8 commits July 10, 2026 14:46
…reate_ai_backdrop

New AI texture asset lane (hosted text-to-image), complementing the Milestone 4
realtime img2img path:

- create_ai_texture (Layer 2, CLI create-ai-texture): prompt → hosted render
  (fal.ai) → Movie File In TOP pointing at the cached asset.
- create_ai_backdrop (Layer 1, CLI create-ai-backdrop): prompt → fully wired
  backdrop (Movie File In → Level → Transform → Blur → Null) with exposed
  Brightness/Blur/Scale live controls.
- src/services/imageGen/ hosted provider seam (fal.ai; Flux-schnell default,
  WAN 2.5 selectable); local cache-file → Movie File In TOP delivery, no new
  bridge endpoint. A post-generation bridge failure still cites the on-disk
  cache path so the rendered asset is never lost.
- Config (Node-only, never sent to the TD bridge): TDMCP_IMAGE_GEN_PROVIDER
  (fal|replicate|none, default none), TDMCP_FAL_KEY, TDMCP_REPLICATE_KEY,
  TDMCP_IMAGE_GEN_MODEL, TDMCP_IMAGE_CACHE_DIR; provider keys redacted in
  doctor / doctor --json.

QA PASS (offline gates + cross-boundary coherence + offline-safety checks); live
TD cook + fal contract UNVERIFIED-pending (bridge offline + no fal key at release
time). [Unreleased] — no version bump, no tag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add create_ai_video (L2) + create_ai_video_backdrop (L1) on a new
src/services/videoGen/ provider seam with two first-class backends
selected by TDMCP_VIDEO_GEN_PROVIDER: local ComfyUI REST (zero
per-generation cost, offline, LTX-Video on a local GPU) and hosted
fal.ai. Both cache the render and hand it to a Movie File In TOP — no
new bridge endpoint. Model default ltx-video, ltx-2 opt-in; schema
rejects controls a model lacks. Reuses TDMCP_FAL_KEY (redacted, no new
secret). Generation is generate-ahead then play-back-live, not per-frame.

Adopts the generator choice (LTX-Video) from the NEvo evolutionary
video-synthesis paper (arXiv 2607.02317, CC BY 4.0); no NEvo code.

Offline gates green: typecheck, build, biome, 5608 vitest. Live
fal/comfyui contracts + TD cook UNVERIFIED-pending (bridge/GPU offline).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add the tdmcp-nevo-integration harness (nevo-integration-scout +
nevo-integration-architect agents + orchestrator skill) that produced
the NEvo->tdmcp INTEGRATION_POINTS + PROJECT_DRAFT under _workspace/nevo/,
plus the create_ai_video pipeline artifacts (design/build/integrate/qa).
Registers the harness pointer in CLAUDE.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… setup

ComfyUI's LTXV latent `length` is a FRAME count requiring length % 8 == 1;
the provider was injecting duration_seconds (e.g. 5) directly, which would
error or produce a 5-frame clip on a live run. Convert seconds to frames at
24 fps snapped to k*8+1 (5s -> 121). Found while preparing local validation.

Add assets/comfyui/: an API-format LTX-Video text-to-video workflow matching
the provider's key-based injection contract, plus a README covering the local
GPU setup, env vars, injection map, i2v adjustment, and a no-TD smoke test.

Gates green: comfyui/video slice 14 tests, tsc, biome.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add evolve_parameters (Layer 1, CLI evolve-parameters, experimental/offline):
a categorical genetic-algorithm parameter search adopting the NEvo method
(arXiv 2607.02317, CC BY 4.0) with its fMRI fitness retargeted to a
TD-measurable one — audio_energy (Info CHOP), top_luma, or top_motion.
mu+lambda elitism, seeded-deterministic RNG (no Math.random), hard cap
population*generations <= 300, per-candidate teardown with only the champion
rebuilt. Design-time search, not real-time. New pure GA module + frameStats +
topMotion helpers, all CC <= 10. No bridge endpoint, no config.

Gates green: typecheck, build, biome, 5632 vitest, bridge tests. Live cook
UNVERIFIED-pending (bridge offline).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolves the probe-first LICENSE item: LTX-Video 2B is Apache-2.0 code /
OpenRAIL-M weights; LTX-2 open weights under the LTX-2 Community License,
free commercial use under $10M ARR. Local generation is free regardless.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
npm run dev runs the server (src/index.ts), not the tools. The verbs live in
the agent CLI (src/cli/agent.ts / bin tdmcp-agent) and take one JSON object via
--params. Add a --dry-run offline sanity step, ComfyUI reachability check, and
the real generate command (needs ComfyUI + TD bridge up).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts/captionTop)

Close the QA-flagged duplication: the 8-bit PNG decode mechanics (signature,
chunk parse, zlib inflate, un-filter) lived in both frameStats.ts and
captionTop.ts. Extract them once into src/feedback/pngDecode.ts, returning a
discriminated PngDecodeResult so each caller keeps its own accumulation and
messaging — captionTop maps the failure reason to its exact existing warning
strings, frameStats falls back to a byte-histogram. Behavior-preserving.

Gates green: lint (1427 files), typecheck, build, 5632 tests (caption_top
unchanged).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 15, 2026 23:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Pantani, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7235fb56-0db1-48ea-9675-9dde89962dcc

📥 Commits

Reviewing files that changed from the base of the PR and between dc2a596 and 10f5a9f.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • src/cli/agent.ts
  • src/feedback/pngDecode.ts
  • src/tools/layer1/evolveParameters.ts
  • src/utils/config.ts
  • tests/unit/pngDecode.test.ts
  • tests/unit/videoGenFal.test.ts
📝 Walkthrough

Walkthrough

This PR adds AI image/video generation lanes with fal.ai and ComfyUI providers, cached TouchDesigner asset tools, evolutionary parameter search, shared PNG feedback scoring, CLI/config wiring, tests, and a resumable NEvo integration-study harness.

Changes

AI media and NEvo integration

Layer / File(s) Summary
NEvo study harness
.claude/agents/*, .claude/skills/tdmcp-nevo-integration/SKILL.md, CLAUDE.md
Defines scout and architect agents plus a two-phase workflow producing integration findings and optional project drafts.
Image/video provider foundation
src/services/imageGen/*, src/services/videoGen/*, src/server/context.ts, src/utils/config.ts
Adds provider contracts, fal.ai and ComfyUI implementations, filesystem caches, provider resolution, configuration, secret redaction, and tool-context wiring.
AI asset and backdrop tools
src/tools/layer1/createAi*.ts, src/tools/layer2/createAi*.ts, src/tools/*/index.ts, src/cli/agent.ts
Adds texture, video, image-backdrop, and video-backdrop tools with schema validation, caching, TouchDesigner node creation, controls, previews, and CLI registration.
Evolutionary search and feedback scoring
src/tools/layer1/evolveParameters.ts, src/tools/layer1/geneticAlgorithm.ts, src/feedback/*, src/tools/layer3/captionTop.ts
Adds deterministic genetic search, luma/motion scoring, shared PNG decoding, and caption-statistics reuse.
ComfyUI workflow assets
assets/comfyui/*
Adds an LTX-Video API workflow and setup, injection, testing, and probe documentation.
Validation suites
tests/unit/*
Tests provider flows, caching, schemas, TouchDesigner delivery, control bindings, PNG motion scoring, and genetic search cleanup/determinism.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agent as CLI/MCP tool
  participant Provider as Image/Video provider
  participant Cache as Filesystem cache
  participant TD as TouchDesigner bridge
  Agent->>Provider: Generate requested asset
  Provider-->>Agent: Return downloaded bytes and metadata
  Agent->>Cache: Write or reuse deterministic cache path
  Agent->>TD: Create Movie File In TOP from cache path
  TD-->>Agent: Return node and preview metadata
Loading

Possibly related PRs

  • Pantani/tdmcp#57 — Both changes extend the CLI agent command registry with new tool-backed commands.
  • Pantani/tdmcp#36 — Both add Layer 1 tool behavior and related TouchDesigner wiring.
  • Pantani/tdmcp#47 — Both modify agent/tool integration surfaces, though this PR focuses on AI generation and study workflows.

Poem

A rabbit found pixels, a clip, and a key,
Then hopped through ComfyUI merrily.
Genes danced softly, caches stayed bright,
TouchDesigner welcomed the assets just right.
“NEvo,” said Bun, “now map what you see!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.40% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main additions: NEvo-inspired AI video generation and evolve_parameters.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dc2a596803

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/agent.ts
Comment on lines +5009 to +5010
falKey: "TDMCP_FAL_KEY",
replicateKey: "TDMCP_REPLICATE_KEY",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Redact AI provider keys from --write-env

When TDMCP_FAL_KEY or TDMCP_REPLICATE_KEY is set, adding these keys to ENV_NAMES makes tdmcp-agent config --write-env print the raw secret because SECRET_ENV was not extended with the same entries. The command advertises that secrets are redacted and produces paste/share-ready output, so this can leak provider credentials from the new AI generation lane.

Useful? React with 👍 / 👎.

model,
prompt: req.prompt,
negativePrompt: req.negativePrompt,
initImagePath: req.initImagePath,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include init image contents in the video cache key

For image-to-video calls, the cache key only records initImagePath; if an artist overwrites the anchor image at the same absolute path and reruns the same prompt/seed, findCachedVideo will return the old clip without calling the provider. Because the image bytes are generation-defining input, the key needs to include a content hash or at least file metadata to avoid stale/corrupt results.

Useful? React with 👍 / 👎.

Comment thread assets/comfyui/README.md
frame-streaming bridge that `connect_comfyui` builds. It POSTs an API-format
workflow to `/prompt`, polls `/history`, and downloads the finished mp4 from `/view`.

This folder ships a matching workflow: [`ltx-video-t2v.api.json`](./ltx-video-t2v.api.json).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ship the ComfyUI workflow with npm packages

The new ComfyUI instructions point users at assets/comfyui/ltx-video-t2v.api.json, but the published package will not contain this folder: package.json has a files whitelist that excludes assets/, and scripts/copy-assets.mjs only copies knowledge data and recipes; I also checked npm pack --dry-run and no assets/comfyui files were present. Users installing the npm CLI therefore cannot follow this documented local-video setup unless the asset is included or copied into a packaged path.

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts
/** Absolute path to a ComfyUI API-format workflow JSON (required for the comfyui video provider). */
comfyuiVideoWorkflow: z.string().optional(),
/** Optional override for the video generation timeout (ms); provider default is 10 min. */
videoGenTimeoutMs: z.coerce.number().int().positive().optional(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wire the configured video timeout into requests

TDMCP_VIDEO_GEN_TIMEOUT_MS is parsed into config, but neither buildToolContext nor toVideoRequest carries it into VideoGenRequest.timeoutMs, so both video tools still use DEFAULT_VIDEO_GEN_TIMEOUT_MS regardless of the user's override. This matters for long local ComfyUI renders that need more than 10 minutes, or for operators trying to fail fast with a shorter timeout.

Useful? React with 👍 / 👎.

The complexity ratchet flagged two functions moved/added in this branch:
pngDecode.unfilter (13) and evolveParameters.runEvolution (10). Extract the
inner scanline loop (unfilterRow) and the per-generation scoring loop
(scoreGeneration) to bound nesting depth. Behavior-preserving.

Cognitive ratchet passes; typecheck, biome, and the affected tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Pantani
Pantani marked this pull request as draft July 15, 2026 23:33
@Pantani
Pantani marked this pull request as ready for review July 15, 2026 23:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/agents/nevo-integration-architect.md:
- Around line 1-5: Create the Codex counterpart for the
nevo-integration-architect agent at the mirrored `.codex/agents` location,
preserving equivalent metadata, model, responsibilities, workflow constraints,
and artifact-writing behavior from the
`.claude/agents/nevo-integration-architect.md` definition.

In @.claude/agents/nevo-integration-scout.md:
- Around line 1-5: Create the Codex mirror of the nevo-integration-scout agent
at .codex/agents/nevo-integration-scout.md, preserving the equivalent
frontmatter and research-only behavior defined by the existing
nevo-integration-scout agent. Ensure the mirrored agent maintains the same
scope, integration-mapping responsibilities, and prohibition on editing source.

In @.claude/skills/tdmcp-nevo-integration/SKILL.md:
- Around line 1-4: Create a Codex mirror of the tdmcp-nevo-integration skill
under .codex/skills/tdmcp-nevo-integration/, preserving the same SKILL.md
content and workflow as the existing Claude skill. Follow the established
mirrored structure used by tdmcp-quality-audit so both Claude and Codex expose
equivalent behavior.

In `@src/agentCommandCatalog.ts`:
- Around line 756-763: Update the create-ai-video-backdrop catalog entry’s
summary to include the Blur stage in the Movie → Level → Transform pipeline and
mention the exposed Blur control alongside Play, Speed, Brightness, and Scale.

In `@src/feedback/pngDecode.ts`:
- Around line 136-149: Update the PNG decoding flow around parsePngChunks and
unfilter to enforce configured caps on PNG input bytes, width and height, total
pixel count, and expected decompressed output size before calling
zlib.inflateSync or allocating buffers. Reject oversized or unsafe
dimensions/data with the existing failure-result pattern, and ensure validation
uses overflow-safe calculations for width × height × channels.
- Around line 63-79: Reduce the cognitive complexity of unfilter by extracting
per-scanline or neighbor reconstruction into a focused helper. Keep unfilter
responsible for buffer allocation and row iteration, while the helper handles
filter values, neighbor lookup, and reconstruct calls without changing output
behavior.
- Around line 47-59: Update the PNG decode flow around reconstruct, IHDR
parsing, and scanline unfiltering to reject unsupported layouts instead of
fabricating pixels: parse and reject nonzero interlace modes, validate every
scanline filter is 0–4 rather than treating unknown values as filter 0, and
verify the inflated data contains the complete expected scanline bytes before
unfiltering. Propagate these validation failures so decoding does not return ok:
true for Adam7, corrupt, or truncated payloads.

In `@src/feedback/topMotion.ts`:
- Around line 56-61: Update the motion calculation in src/feedback/topMotion.ts
lines 56-61 to return the neutral/unavailable motion score immediately when
either decodePngStats result has decoded: false, while preserving the warning.
Update tests/unit/topMotion.test.ts lines 79-91 to assert the
neutral/unavailable score and warning instead of a positive delta.

In `@src/services/videoGen/cache.ts`:
- Around line 30-43: Update videoCacheKey to derive the init image component
from the file’s content digest or immutable asset identifier rather than only
req.initImagePath, so replacing the image produces a different cache key.
Preserve the existing key fields and behavior when no init image is provided.

In `@src/services/videoGen/comfyuiProvider.ts`:
- Around line 219-229: Update downloadView and the related extractClip output
handling to explicitly support .gif with mimeType image/gif, or reject GIF
inputs as unsupported before downloading. Do not allow the current fallback to
label non-WebM GIF bytes as video/mp4; preserve the existing WebM and MP4
behavior.
- Around line 160-176: The uploadImage method currently uses basename(path) with
overwrite enabled, allowing concurrent or repeated uploads to overwrite each
other. Generate a unique filename by adding a unique suffix to the basename in
the form-data append call, and remove the overwrite=true form field so ComfyUI
does not force replacement.
- Around line 195-205: Update pollHistory to inspect the history entry’s
terminal status metadata after extractClip returns no clip. Continue sleeping
and polling only while the job is running; when status.completed indicates
completion without a clip, derive the failure from status.status_str or
status.messages and throw a descriptive ComfyUI error instead of continuing
until timeout.

In `@src/services/videoGen/resolve.ts`:
- Around line 11-17: Thread the parsed TDMCP_VIDEO_GEN_TIMEOUT_MS setting from
src/utils/config.ts lines 428-429 through src/server/context.ts into the
video-generation execution path, adding it to VideoGenConfig and consuming it
where requests are executed in src/services/videoGen/resolve.ts lines 11-17.
Preserve the configured timeout instead of falling back to per-request or
hardcoded defaults; do not remove the option.

In `@src/tools/layer1/createAiVideoBackdrop.ts`:
- Around line 141-176: Update the catch block surrounding the builder creation
and delivery calls to best-effort delete the partially built container using
builder.containerPath before returning errorResult. Ensure cleanup is attempted
only when the builder was created, and preserve the existing error response and
cache_path details even if deletion also fails.

In `@src/tools/layer1/evolveParameters.ts`:
- Around line 259-270: The evolveParameters flow must not rebuild a winner when
all fitness evaluations failed and best.fitness is the -Infinity failure
sentinel. In src/tools/layer1/evolveParameters.ts:259-270, require
Number.isFinite(best.fitness) alongside the existing best check before calling
rebuildWinner, returning the existing errorResult otherwise. In
tests/unit/evolveParameters.test.ts:178-187, update the unresolved-target case
to expect an error instead of successful winner creation.
- Around line 222-274: Reduce cognitive complexity in runEvolution by extracting
the per-generation candidate scoring and best-winner update into a dedicated
helper. Have the helper receive the required context, population, generation
index, scratch path, and mutable warning/evaluation state, then return the
scored candidates and updated winner; keep runEvolution responsible for
initialization, generation progression, no-candidate handling, rebuilding, and
cleanup.
- Around line 276-311: The evolve_parameters tool must be unavailable when raw
Python execution is disabled. Add an implementation guard in
evolveParametersImpl that rejects direct calls when ctx.allowRawPython is false,
and update registerEvolveParameters to skip registration in that case while
preserving the existing validation and execution flow when enabled.

In `@src/tools/layer2/createAiTexture.ts`:
- Around line 156-157: Update the caching flow around writeCachedImage in the
tool handler to catch file-system failures and prevent exceptions from escaping.
On failure, return the existing errorResult with the caught error details;
preserve the successful cachePath result and cacheHit: false behavior when
writing succeeds.

In `@src/tools/layer2/createAiVideo.ts`:
- Around line 190-228: Update createAiVideo to catch failures from
findCachedVideo and writeCachedVideo, returning consistently structured
errorResult values with provider and model context instead of allowing rejected
handlers. In src/services/videoGen/cache.ts lines 51-59, modify the cache lookup
handling to suppress only ENOENT and rethrow permission or other I/O failures;
apply the tool-layer handling at createAiVideo and the errno filtering at the
cache implementation.
- Around line 340-351: Update the registered create_ai_video handler in
registerCreateAiVideo to validate arguments with createAiVideoSchema.safeParse
instead of parse. When validation fails, return the resulting errorResult, and
pass the parsed data to createAiVideoImpl only on successful validation so
invalid model, duration_seconds, or resolution combinations do not throw.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8b152a09-4dba-48e4-a4e4-5b119f6c9b33

📥 Commits

Reviewing files that changed from the base of the PR and between fd7282e and dc2a596.

📒 Files selected for processing (42)
  • .claude/agents/nevo-integration-architect.md
  • .claude/agents/nevo-integration-scout.md
  • .claude/skills/tdmcp-nevo-integration/SKILL.md
  • CHANGELOG.md
  • CLAUDE.md
  • assets/comfyui/README.md
  • assets/comfyui/ltx-video-t2v.api.json
  • src/agentCommandCatalog.ts
  • src/cli/agent.ts
  • src/feedback/frameStats.ts
  • src/feedback/pngDecode.ts
  • src/feedback/topMotion.ts
  • src/server/context.ts
  • src/services/imageGen/cache.ts
  • src/services/imageGen/falProvider.ts
  • src/services/imageGen/resolve.ts
  • src/services/imageGen/types.ts
  • src/services/videoGen/cache.ts
  • src/services/videoGen/comfyuiProvider.ts
  • src/services/videoGen/falProvider.ts
  • src/services/videoGen/resolve.ts
  • src/services/videoGen/types.ts
  • src/tools/layer1/createAiBackdrop.ts
  • src/tools/layer1/createAiVideoBackdrop.ts
  • src/tools/layer1/evolveParameters.ts
  • src/tools/layer1/geneticAlgorithm.ts
  • src/tools/layer1/index.ts
  • src/tools/layer2/createAiTexture.ts
  • src/tools/layer2/createAiVideo.ts
  • src/tools/layer2/index.ts
  • src/tools/layer3/captionTop.ts
  • src/tools/types.ts
  • src/utils/config.ts
  • tests/unit/createAiBackdrop.test.ts
  • tests/unit/createAiTexture.test.ts
  • tests/unit/createAiVideo.test.ts
  • tests/unit/createAiVideoBackdrop.test.ts
  • tests/unit/evolveParameters.test.ts
  • tests/unit/geneticAlgorithm.test.ts
  • tests/unit/imageGenProvider.test.ts
  • tests/unit/topMotion.test.ts
  • tests/unit/videoGenComfyui.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Unit Tests (Node 22.x)
  • GitHub Check: Unit Tests (Node 20.x)
  • GitHub Check: Coverage Gate
⚠️ CI failures not shown inline (4)

GitHub Actions: Code Quality / 0_Code Quality Success.txt: feat: NEvo-inspired AI video lane (local ComfyUI + fal) & evolve_parameters

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1mfailed=0�[0m
 �[36;1mfor item in \�[0m
 �[36;1m  "typecheck=${TYPECHECK}" \�[0m
 �[36;1m  "lint=${LINT}" \�[0m
 �[36;1m  "recipes=${RECIPES}" \�[0m
 �[36;1m  "dependency-boundaries=${DEPENDENCY_BOUNDARIES}" \�[0m
 �[36;1m  "complexity=${COMPLEXITY}" \�[0m
 �[36;1m  "generated-artifacts=${GENERATED_ARTIFACTS}" \�[0m
 �[36;1m  "workflow-lint=${WORKFLOW_LINT}"�[0m
 �[36;1mdo�[0m
 �[36;1m  name="${item%%=*}"�[0m
 �[36;1m  result="${item#*=}"�[0m
 �[36;1m  echo "$name: $result"�[0m
 �[36;1m  if [ "$result" != "success" ]; then�[0m
 �[36;1m    failed=1�[0m
 �[36;1m  fi�[0m
 �[36;1mdone�[0m
 �[36;1mexit "$failed"�[0m
 shell: /usr/bin/bash -e {0}
 env:
   NPM_CONFIG_AUDIT: false
   NPM_CONFIG_FUND: false
   TYPECHECK: success
   LINT: success
   RECIPES: success
   DEPENDENCY_BOUNDARIES: success
   COMPLEXITY: failure
   GENERATED_ARTIFACTS: success
   WORKFLOW_LINT: success
 ##[endgroup]
 typecheck: success
 lint: success
 recipes: success
 dependency-boundaries: success
 complexity: failure
 generated-artifacts: success
 workflow-lint: success
 ##[error]Process completed with exit code 1.

GitHub Actions: Code Quality / Code Quality Success: feat: NEvo-inspired AI video lane (local ComfyUI + fal) & evolve_parameters

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1mfailed=0�[0m
 �[36;1mfor item in \�[0m
 �[36;1m  "typecheck=${TYPECHECK}" \�[0m
 �[36;1m  "lint=${LINT}" \�[0m
 �[36;1m  "recipes=${RECIPES}" \�[0m
 �[36;1m  "dependency-boundaries=${DEPENDENCY_BOUNDARIES}" \�[0m
 �[36;1m  "complexity=${COMPLEXITY}" \�[0m
 �[36;1m  "generated-artifacts=${GENERATED_ARTIFACTS}" \�[0m
 �[36;1m  "workflow-lint=${WORKFLOW_LINT}"�[0m
 �[36;1mdo�[0m
 �[36;1m  name="${item%%=*}"�[0m
 �[36;1m  result="${item#*=}"�[0m
 �[36;1m  echo "$name: $result"�[0m
 �[36;1m  if [ "$result" != "success" ]; then�[0m
 �[36;1m    failed=1�[0m
 �[36;1m  fi�[0m
 �[36;1mdone�[0m
 �[36;1mexit "$failed"�[0m
 shell: /usr/bin/bash -e {0}
 env:
   NPM_CONFIG_AUDIT: false
   NPM_CONFIG_FUND: false
   TYPECHECK: success
   LINT: success
   RECIPES: success
   DEPENDENCY_BOUNDARIES: success
   COMPLEXITY: failure
   GENERATED_ARTIFACTS: success
   WORKFLOW_LINT: success
 ##[endgroup]
 typecheck: success
 lint: success
 recipes: success
 dependency-boundaries: success
 complexity: failure
 generated-artifacts: success
 workflow-lint: success
 ##[error]Process completed with exit code 1.

GitHub Actions: Code Quality / 4_Complexity Gates.txt: feat: NEvo-inspired AI video lane (local ComfyUI + fal) & evolve_parameters

Conclusion: failure

View job details

##[group]Run make complexity
 �[36;1mmake complexity�[0m
 shell: /usr/bin/bash -e {0}
 env:
   NPM_CONFIG_AUDIT: false
   NPM_CONFIG_FUND: false
   UV_PYTHON_INSTALL_DIR: /home/runner/work/_temp/uv-python-dir
   UV_CACHE_DIR: /home/runner/work/_temp/setup-uv-cache
 ##[endgroup]
 make[1]: Entering directory '/home/runner/work/tdmcp/tdmcp'
 npm run complexity:py
 > `@dpantani/tdmcp`@0.13.1 complexity:py
 > uvx ruff check td --select C901
 Downloading ruff (11.0MiB)
  Downloaded ruff
 Installed 1 package in 2ms
 All checks passed!
 make[1]: Leaving directory '/home/runner/work/tdmcp/tdmcp'
 make[1]: Entering directory '/home/runner/work/tdmcp/tdmcp'
 npm run complexity:js
 > `@dpantani/tdmcp`@0.13.1 complexity:js
 > npm exec --offline -- eslint --no-error-on-unmatched-pattern "src/**/*.{js,mjs,cjs}" "scripts/**/*.{js,mjs,cjs}" "*.{js,mjs,cjs}"
 make[1]: Leaving directory '/home/runner/work/tdmcp/tdmcp'
 make[1]: Entering directory '/home/runner/work/tdmcp/tdmcp'
 npm run complexity:cognitive
 > `@dpantani/tdmcp`@0.13.1 complexity:cognitive
 > npm run complexity:cognitive:js && npm run complexity:cognitive:py
 > `@dpantani/tdmcp`@0.13.1 complexity:cognitive:js
 > node scripts/check-js-cognitive-complexity.mjs
 JS/TS cognitive complexity ratchet failed (2 regression(s)).
 - src/feedback/pngDecode.ts:63:17 cognitive complexity 13 is not in the baseline
 - src/tools/layer1/evolveParameters.ts:222:16 cognitive complexity 10 is not in the baseline
 make[1]: *** [Makefile:27: complexity-cognitive] Error 1
 make[1]: Leaving directory '/home/runner/work/tdmcp/tdmcp'
 make: *** [Makefile:14: complexity] Error 2
 ##[error]Process completed with exit code 2.

GitHub Actions: Code Quality / Complexity Gates: feat: NEvo-inspired AI video lane (local ComfyUI + fal) & evolve_parameters

Conclusion: failure

View job details

##[group]Run make complexity
 �[36;1mmake complexity�[0m
 shell: /usr/bin/bash -e {0}
 env:
   NPM_CONFIG_AUDIT: false
   NPM_CONFIG_FUND: false
   UV_PYTHON_INSTALL_DIR: /home/runner/work/_temp/uv-python-dir
   UV_CACHE_DIR: /home/runner/work/_temp/setup-uv-cache
 ##[endgroup]
 make[1]: Entering directory '/home/runner/work/tdmcp/tdmcp'
 npm run complexity:py
 > `@dpantani/tdmcp`@0.13.1 complexity:py
 > uvx ruff check td --select C901
 Downloading ruff (11.0MiB)
  Downloaded ruff
 Installed 1 package in 2ms
 All checks passed!
 make[1]: Leaving directory '/home/runner/work/tdmcp/tdmcp'
 make[1]: Entering directory '/home/runner/work/tdmcp/tdmcp'
 npm run complexity:js
 > `@dpantani/tdmcp`@0.13.1 complexity:js
 > npm exec --offline -- eslint --no-error-on-unmatched-pattern "src/**/*.{js,mjs,cjs}" "scripts/**/*.{js,mjs,cjs}" "*.{js,mjs,cjs}"
 make[1]: Leaving directory '/home/runner/work/tdmcp/tdmcp'
 make[1]: Entering directory '/home/runner/work/tdmcp/tdmcp'
 npm run complexity:cognitive
 > `@dpantani/tdmcp`@0.13.1 complexity:cognitive
 > npm run complexity:cognitive:js && npm run complexity:cognitive:py
 > `@dpantani/tdmcp`@0.13.1 complexity:cognitive:js
 > node scripts/check-js-cognitive-complexity.mjs
 JS/TS cognitive complexity ratchet failed (2 regression(s)).
 - src/feedback/pngDecode.ts:63:17 cognitive complexity 13 is not in the baseline
 - src/tools/layer1/evolveParameters.ts:222:16 cognitive complexity 10 is not in the baseline
 make[1]: *** [Makefile:27: complexity-cognitive] Error 1
 make[1]: Leaving directory '/home/runner/work/tdmcp/tdmcp'
 make: *** [Makefile:14: complexity] Error 2
 ##[error]Process completed with exit code 2.
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Never allow accidental overlap between UI elements, generated project components, nodes, cards, controls, text, canvases, media, or visual assets
When creating new projects, tools, docs previews, dashboards, diagrams, or frontend screens, keep the composition organized with explicit spacing, stable grids or flex layouts, responsive constraints, and predictable stacking order
Treat any element that covers, invades, clips, or visually competes with another as a defect to fix before delivery, unless the overlap is explicitly requested and intentionally designed

Follow Biome formatting and linting: 2-space indentation, double quotes, semicolons, trailing commas, 100-column width, and organized imports.

Files:

  • tests/unit/imageGenProvider.test.ts
  • src/services/imageGen/resolve.ts
  • src/feedback/topMotion.ts
  • src/tools/layer2/index.ts
  • src/tools/layer1/index.ts
  • src/services/videoGen/resolve.ts
  • src/feedback/frameStats.ts
  • src/services/videoGen/types.ts
  • src/server/context.ts
  • src/feedback/pngDecode.ts
  • src/tools/types.ts
  • tests/unit/topMotion.test.ts
  • src/services/videoGen/cache.ts
  • tests/unit/createAiVideoBackdrop.test.ts
  • src/agentCommandCatalog.ts
  • tests/unit/evolveParameters.test.ts
  • src/tools/layer1/createAiVideoBackdrop.ts
  • src/services/imageGen/cache.ts
  • src/services/imageGen/types.ts
  • tests/unit/geneticAlgorithm.test.ts
  • src/tools/layer1/geneticAlgorithm.ts
  • tests/unit/createAiTexture.test.ts
  • src/services/imageGen/falProvider.ts
  • tests/unit/videoGenComfyui.test.ts
  • src/tools/layer1/createAiBackdrop.ts
  • tests/unit/createAiBackdrop.test.ts
  • src/utils/config.ts
  • src/cli/agent.ts
  • src/tools/layer3/captionTop.ts
  • src/tools/layer2/createAiTexture.ts
  • src/services/videoGen/falProvider.ts
  • src/services/videoGen/comfyuiProvider.ts
  • tests/unit/createAiVideo.test.ts
  • src/tools/layer1/evolveParameters.ts
  • src/tools/layer2/createAiVideo.ts
**/*.{ts,tsx,js,jsx,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Verify relevant desktop, mobile, or preview states when a change affects visual layout

Files:

  • tests/unit/imageGenProvider.test.ts
  • src/services/imageGen/resolve.ts
  • src/feedback/topMotion.ts
  • src/tools/layer2/index.ts
  • src/tools/layer1/index.ts
  • src/services/videoGen/resolve.ts
  • src/feedback/frameStats.ts
  • src/services/videoGen/types.ts
  • src/server/context.ts
  • src/feedback/pngDecode.ts
  • src/tools/types.ts
  • tests/unit/topMotion.test.ts
  • src/services/videoGen/cache.ts
  • tests/unit/createAiVideoBackdrop.test.ts
  • src/agentCommandCatalog.ts
  • tests/unit/evolveParameters.test.ts
  • src/tools/layer1/createAiVideoBackdrop.ts
  • src/services/imageGen/cache.ts
  • src/services/imageGen/types.ts
  • tests/unit/geneticAlgorithm.test.ts
  • src/tools/layer1/geneticAlgorithm.ts
  • tests/unit/createAiTexture.test.ts
  • src/services/imageGen/falProvider.ts
  • tests/unit/videoGenComfyui.test.ts
  • src/tools/layer1/createAiBackdrop.ts
  • tests/unit/createAiBackdrop.test.ts
  • src/utils/config.ts
  • src/cli/agent.ts
  • src/tools/layer3/captionTop.ts
  • src/tools/layer2/createAiTexture.ts
  • src/services/videoGen/falProvider.ts
  • src/services/videoGen/comfyuiProvider.ts
  • tests/unit/createAiVideo.test.ts
  • src/tools/layer1/evolveParameters.ts
  • src/tools/layer2/createAiVideo.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESM with strict TypeScript and include .js extensions on all relative imports; respect noUncheckedIndexedAccess.

Files:

  • tests/unit/imageGenProvider.test.ts
  • src/services/imageGen/resolve.ts
  • src/feedback/topMotion.ts
  • src/tools/layer2/index.ts
  • src/tools/layer1/index.ts
  • src/services/videoGen/resolve.ts
  • src/feedback/frameStats.ts
  • src/services/videoGen/types.ts
  • src/server/context.ts
  • src/feedback/pngDecode.ts
  • src/tools/types.ts
  • tests/unit/topMotion.test.ts
  • src/services/videoGen/cache.ts
  • tests/unit/createAiVideoBackdrop.test.ts
  • src/agentCommandCatalog.ts
  • tests/unit/evolveParameters.test.ts
  • src/tools/layer1/createAiVideoBackdrop.ts
  • src/services/imageGen/cache.ts
  • src/services/imageGen/types.ts
  • tests/unit/geneticAlgorithm.test.ts
  • src/tools/layer1/geneticAlgorithm.ts
  • tests/unit/createAiTexture.test.ts
  • src/services/imageGen/falProvider.ts
  • tests/unit/videoGenComfyui.test.ts
  • src/tools/layer1/createAiBackdrop.ts
  • tests/unit/createAiBackdrop.test.ts
  • src/utils/config.ts
  • src/cli/agent.ts
  • src/tools/layer3/captionTop.ts
  • src/tools/layer2/createAiTexture.ts
  • src/services/videoGen/falProvider.ts
  • src/services/videoGen/comfyuiProvider.ts
  • tests/unit/createAiVideo.test.ts
  • src/tools/layer1/evolveParameters.ts
  • src/tools/layer2/createAiVideo.ts
src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Raw Python execution must be controlled by TDMCP_RAW_PYTHON=off and the injected allowRawPython context flag; do not expose execution tools when disabled.

Files:

  • src/services/imageGen/resolve.ts
  • src/feedback/topMotion.ts
  • src/tools/layer2/index.ts
  • src/tools/layer1/index.ts
  • src/services/videoGen/resolve.ts
  • src/feedback/frameStats.ts
  • src/services/videoGen/types.ts
  • src/server/context.ts
  • src/feedback/pngDecode.ts
  • src/tools/types.ts
  • src/services/videoGen/cache.ts
  • src/agentCommandCatalog.ts
  • src/tools/layer1/createAiVideoBackdrop.ts
  • src/services/imageGen/cache.ts
  • src/services/imageGen/types.ts
  • src/tools/layer1/geneticAlgorithm.ts
  • src/services/imageGen/falProvider.ts
  • src/tools/layer1/createAiBackdrop.ts
  • src/utils/config.ts
  • src/cli/agent.ts
  • src/tools/layer3/captionTop.ts
  • src/tools/layer2/createAiTexture.ts
  • src/services/videoGen/falProvider.ts
  • src/services/videoGen/comfyuiProvider.ts
  • src/tools/layer1/evolveParameters.ts
  • src/tools/layer2/createAiVideo.ts
src/tools/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

src/tools/**/*.ts: Each tool file must export an …Impl(ctx, args) implementation and a register…: ToolRegistrar registrar; add the registrar to the appropriate layer index.
Never throw out of a tool handler. Validate inputs with Zod and convert TouchDesigner failures into friendly isError results using errorResult, runBuild, or friendlyTdError.

Files:

  • src/tools/layer2/index.ts
  • src/tools/layer1/index.ts
  • src/tools/types.ts
  • src/tools/layer1/createAiVideoBackdrop.ts
  • src/tools/layer1/geneticAlgorithm.ts
  • src/tools/layer1/createAiBackdrop.ts
  • src/tools/layer3/captionTop.ts
  • src/tools/layer2/createAiTexture.ts
  • src/tools/layer1/evolveParameters.ts
  • src/tools/layer2/createAiVideo.ts
{.claude,codex}/{agents,skills}/**/*

📄 CodeRabbit inference engine (AGENTS.md)

The quality team is defined in .claude/agents/ and mirrored for Codex in .codex/agents/; the skill is mirrored in .claude/skills/tdmcp-quality-audit/ and .codex/skills/tdmcp-quality-audit/

Files:

  • .claude/skills/tdmcp-nevo-integration/SKILL.md
  • .claude/agents/nevo-integration-architect.md
  • .claude/agents/nevo-integration-scout.md
src/utils/config.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Define and validate environment variables through the TDMCP_* configuration system; bridge authentication requires matching TDMCP_BRIDGE_TOKEN values on both sides.

Files:

  • src/utils/config.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Pantani/tdmcp

Timestamp: 2026-07-15T23:24:45.660Z
Learning: Run the core quality gates before integration: `npm run typecheck`, `npm run build`, `npm run lint`, and `npm test`; bridge changes additionally require `python3 -m unittest discover -s td/tests`.
Learnt from: CR
Repo: Pantani/tdmcp

Timestamp: 2026-07-15T23:24:45.660Z
Learning: When implementing features, tools, bulk tool batches, coverage work, audits, documentation, campaigns, or integrations covered by the CLAUDE.md triggers, use the corresponding repository skill or workflow.
📚 Learning: 2026-06-02T06:35:41.390Z
Learnt from: Pantani
Repo: Pantani/tdmcp PR: 50
File: tests/unit/cliLogTailFilteredRun.test.ts:38-41
Timestamp: 2026-06-02T06:35:41.390Z
Learning: In Vitest unit tests (e.g., when mocking `process.stderr.write` with `vi.spyOn(...).mockImplementation(...)`), using `as never` for `process.stderr.write` overload incompatibilities is an intentional, idiomatic workaround. Do not flag `as never` casts here as fragile/undesirable or request full overload typing; avoid suggesting additional typing noise.

Applied to files:

  • tests/unit/imageGenProvider.test.ts
  • tests/unit/topMotion.test.ts
  • tests/unit/createAiVideoBackdrop.test.ts
  • tests/unit/evolveParameters.test.ts
  • tests/unit/geneticAlgorithm.test.ts
  • tests/unit/createAiTexture.test.ts
  • tests/unit/videoGenComfyui.test.ts
  • tests/unit/createAiBackdrop.test.ts
  • tests/unit/createAiVideo.test.ts
🪛 ast-grep (0.44.1)
src/cli/agent.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 GitHub Actions: Code Quality / 4_Complexity Gates.txt
src/feedback/pngDecode.ts

[error] 63-63: Cognitive complexity regression: 13 is not in the baseline (src/feedback/pngDecode.ts:63:17).

src/tools/layer1/evolveParameters.ts

[error] 222-222: Cognitive complexity regression: 10 is not in the baseline (src/tools/layer1/evolveParameters.ts:222:16).

🪛 GitHub Actions: Code Quality / Complexity Gates
src/feedback/pngDecode.ts

[error] 63-63: cognitive complexity ratchet failed. 'JS/TS cognitive complexity ratchet failed (2 regression(s))'. cognitive complexity 13 is not in the baseline.

src/tools/layer1/evolveParameters.ts

[error] 222-222: cognitive complexity ratchet failed. 'JS/TS cognitive complexity ratchet failed (2 regression(s))'. cognitive complexity 10 is not in the baseline.

🪛 LanguageTool
.claude/agents/nevo-integration-scout.md

[grammar] ~65-~65: Use a hyphen to join words.
Context: ...ar? - Realtime honesty — for any "real time" claim, state the true latency path...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.23.0)
CLAUDE.md

[warning] 448-448: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🪛 OpenGrep (1.25.0)
tests/unit/createAiVideoBackdrop.test.ts

[ERROR] 121-121: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 160-160: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

tests/unit/evolveParameters.test.ts

[ERROR] 115-115: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

tests/unit/createAiTexture.test.ts

[ERROR] 101-101: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

tests/unit/createAiBackdrop.test.ts

[ERROR] 116-116: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 161-161: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

tests/unit/createAiVideo.test.ts

[ERROR] 114-114: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🔇 Additional comments (37)
tests/unit/imageGenProvider.test.ts (1)

1-65: LGTM!

src/feedback/frameStats.ts (1)

1-63: LGTM!

tests/unit/topMotion.test.ts (1)

55-77: LGTM!

src/tools/layer3/captionTop.ts (1)

3-3: LGTM!

Also applies to: 94-109, 150-200

src/feedback/topMotion.ts (1)

32-35: 🔒 Security & Privacy

Drop the extra raw-Python gate here
execute_python_script is already disabled when ctx.allowRawPython === false, so this helper doesn’t need to thread its own flag.

			> Likely an incorrect or invalid review comment.
src/services/imageGen/resolve.ts (1)

1-36: LGTM!

src/services/imageGen/types.ts (1)

1-54: LGTM!

tests/unit/geneticAlgorithm.test.ts (1)

1-124: LGTM!

assets/comfyui/ltx-video-t2v.api.json (1)

1-63: LGTM!

assets/comfyui/README.md (1)

1-106: LGTM!

src/tools/layer1/geneticAlgorithm.ts (1)

1-125: 📐 Maintainability & Code Quality | 💤 Low value

Move helper file outside of the tools directory.

As per coding guidelines, each tool file in src/tools/**/*.ts must export an ...Impl(ctx, args) implementation and a register...: ToolRegistrar registrar.

Since this file is a collection of utility functions for the genetic algorithm (and not an active tool), consider moving it to a utils directory (e.g., src/utils/geneticAlgorithm.ts) or a dedicated non-tool subfolder to maintain structural hygiene and prevent tool-registrar pattern mismatches.

Source: Coding guidelines

src/services/imageGen/falProvider.ts (1)

74-88: 🩺 Stability & Availability | ⚡ Quick win

Prevent memory leak by removing the abort event listener.

If a long-lived AbortSignal is passed to this function, the anonymous listener attached to it is never removed when the generation operation successfully completes. Over multiple calls, this will cause an accumulation of event listeners on the signal, leading to a memory leak.

Extract the listener to a variable and explicitly remove it in the finally block.

🛡️ Proposed fix
     const controller = new AbortController();
     const timeout = setTimeout(() => controller.abort(), timeoutMs);
+    const onAbort = () => controller.abort();
     if (signal) {
-      if (signal.aborted) controller.abort();
-      else signal.addEventListener("abort", () => controller.abort(), { once: true });
+      if (signal.aborted) onAbort();
+      else signal.addEventListener("abort", onAbort, { once: true });
     }
     try {
       const result = this.usesQueue(model)
         ? await this.runQueue(model, req, controller.signal)
         : await this.runSync(model, req, controller.signal);
       return await this.download(result, model, req, controller.signal);
     } finally {
       clearTimeout(timeout);
+      if (signal) signal.removeEventListener("abort", onAbort);
     }
src/tools/layer2/index.ts (1)

105-106: LGTM!

Also applies to: 460-463

src/services/imageGen/cache.ts (1)

1-74: LGTM!

src/tools/layer1/createAiBackdrop.ts (1)

14-88: LGTM!

Also applies to: 94-174, 180-192

CHANGELOG.md (1)

11-48: LGTM!

Also applies to: 390-410

src/cli/agent.ts (1)

86-91: LGTM!

Also applies to: 481-481, 889-890, 3342-3371, 5008-5018

src/tools/layer1/index.ts (1)

12-14: LGTM!

Also applies to: 147-147, 351-356

src/tools/types.ts (1)

7-8: LGTM!

Also applies to: 65-89

src/agentCommandCatalog.ts (1)

725-755: LGTM!

Also applies to: 1211-1218

tests/unit/evolveParameters.test.ts (1)

1-177: LGTM!

Also applies to: 189-194

src/tools/layer1/createAiVideoBackdrop.ts (2)

1-140: LGTM!

Also applies to: 177-183


184-195: 🩺 Stability & Availability

This parse call is acceptable
The MCP SDK catches handler exceptions and returns them as tool errors, so createAiVideoBackdropSchema.parse(args) won’t escape the tool boundary.

			> Likely an incorrect or invalid review comment.
src/tools/layer1/evolveParameters.ts (1)

1-221: LGTM!

src/services/videoGen/resolve.ts (1)

1-10: LGTM!

Also applies to: 19-36, 52-56

src/server/context.ts (1)

13-16: LGTM!

Also applies to: 33-36, 68-88

tests/unit/createAiTexture.test.ts (2)

1-137: LGTM!

Also applies to: 158-194


138-156: 🎯 Functional Correctness

No issue here The unset-provider error already tells users to set TDMCP_IMAGE_GEN_PROVIDER=fal and TDMCP_FAL_KEY.

			> Likely an incorrect or invalid review comment.
.claude/agents/nevo-integration-scout.md (1)

7-83: LGTM!

tests/unit/createAiBackdrop.test.ts (1)

1-175: LGTM!

Also applies to: 197-221

src/utils/config.ts (1)

385-427: LGTM!

Also applies to: 528-537, 646-647

src/services/videoGen/comfyuiProvider.ts (1)

1-159: LGTM!

Also applies to: 177-194, 206-218, 230-250, 265-265

src/services/videoGen/types.ts (1)

13-67: LGTM!

tests/unit/createAiVideoBackdrop.test.ts (1)

136-175: 🎯 Functional Correctness

Verify the generated network layout, not only its topology.

Add assertions for node positions/spacing or record a manual TouchDesigner preview check so the generated container cannot ship with overlapping nodes.

As per coding guidelines, generated project components must use explicit spacing and never overlap.

Source: Coding guidelines

tests/unit/videoGenComfyui.test.ts (1)

1-155: LGTM!

src/services/videoGen/falProvider.ts (2)

115-131: 🎯 Functional Correctness

Verify or replace the undocumented one-step storage upload.

The official integration documents fal.storage.upload, while this implementation hard-codes an explicitly unverified raw endpoint and response shape. Validate this path live or use the supported client before enabling image-to-video. (fal.ai)


33-43: 🎯 Functional Correctness

No change needed. The current fal-ai/ltx-video/text-to-video slug matches fal’s documented text-to-video endpoint, and the test mock already uses the same slug.

			> Likely an incorrect or invalid review comment.

Comment on lines +1 to +5
---
name: nevo-integration-architect
description: Fan-in synthesizer + drafter for the NEvo→tdmcp integration study. Reads every nevo-integration-scout report, dedupes and reconciles them, and produces first the INTEGRATION_POINTS map ("levantar os pontos") and — only on go-ahead — a PROJECT_DRAFT that scopes concrete tdmcp deliverables (e.g. create_ai_video on the hosted media lane; an experimental evolve_* loop) with layer, schema sketch, bridge needs, effort and risks. Reconciles against docs/ROADMAP.md; never edits source, only writes _workspace/nevo/ artifacts.
model: opus
---

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the required Codex mirror for this agent.

Create .codex/agents/nevo-integration-architect.md with equivalent instructions so the quality workflow remains available across both agent systems.

As per coding guidelines, “The quality team is defined in .claude/agents/ and mirrored for Codex in .codex/agents/.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/agents/nevo-integration-architect.md around lines 1 - 5, Create the
Codex counterpart for the nevo-integration-architect agent at the mirrored
`.codex/agents` location, preserving equivalent metadata, model,
responsibilities, workflow constraints, and artifact-writing behavior from the
`.claude/agents/nevo-integration-architect.md` definition.

Source: Coding guidelines

Comment on lines +1 to +5
---
name: nevo-integration-scout
description: Maps ONE integration surface between the external NEvo research project (EPFL/JHU — Neural-Guided Evolutionary Video Synthesis, arXiv 2607.02317) and tdmcp. NEvo is neuroscience research (evolves AI video to maximally drive a brain ROI), NOT a shippable video generator — so the scout separates the reusable pieces (its off-the-shelf models like LTX-Video, its evolutionary prompt-search method) from the non-reusable objective (fMRI-guided fitness). Research-only: verifies facts from primary sources and writes a structured map of its assigned surface; never edits source.
model: opus
---

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the corresponding Codex agent mirror.

Create .codex/agents/nevo-integration-scout.md with equivalent behavior so the workflow remains available through both agent systems.

As per coding guidelines, “The quality team is defined in .claude/agents/ and mirrored for Codex in .codex/agents/.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/agents/nevo-integration-scout.md around lines 1 - 5, Create the
Codex mirror of the nevo-integration-scout agent at
.codex/agents/nevo-integration-scout.md, preserving the equivalent frontmatter
and research-only behavior defined by the existing nevo-integration-scout agent.
Ensure the mirrored agent maintains the same scope, integration-mapping
responsibilities, and prohibition on editing source.

Source: Coding guidelines

Comment on lines +1 to +4
---
name: tdmcp-nevo-integration
description: Study whether/how the external NEvo research project (EPFL/JHU — Neural-Guided Evolutionary Video Synthesis, arXiv 2607.02317, nevo-project.epfl.ch) can be integrated into tdmcp, then draft the resulting project. Use whenever the user wants to survey/raise the integration points for NEvo (or a similar external AI-video / evolutionary-synthesis research project), assess feasibility for generating video / real-time use / any interaction, or draft a tdmcp project from it — AND every follow-up: re-run, continue, refresh after a new NEvo release, scope to one surface (e.g. "só o LTX-Video", "só o método evolutivo"), fold in a decision, or go from the points map to the project draft ("agora rascunha o projeto", "levantar os pontos", "montar o build do nevo"). Runs a fan-out of nevo-integration-scouts → 1 nevo-integration-architect. This is a STUDY+DRAFT harness — it produces INTEGRATION_POINTS.md then PROJECT_DRAFT.md; it does NOT build. Once a deliverable is chosen, hand it to tdmcp-pipeline.
---

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the matching Codex skill mirror.

Mirror this skill under .codex/skills/tdmcp-nevo-integration/ so Claude and Codex expose the same workflow.

As per coding guidelines, “the skill is mirrored in .claude/skills/tdmcp-quality-audit/ and .codex/skills/tdmcp-quality-audit/.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/tdmcp-nevo-integration/SKILL.md around lines 1 - 4, Create a
Codex mirror of the tdmcp-nevo-integration skill under
.codex/skills/tdmcp-nevo-integration/, preserving the same SKILL.md content and
workflow as the existing Claude skill. Follow the established mirrored structure
used by tdmcp-quality-audit so both Claude and Codex expose equivalent behavior.

Source: Coding guidelines

Comment on lines +756 to +763
{
command: "create-ai-video-backdrop",
summary:
"Prompt → a fully wired AI-generated video-backdrop system (movie/level/transform + Play/Speed/Brightness/Scale).",
mutates: true,
unsafe: false,
source: "tool",
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Include the Blur stage and control in the command summary.

The implementation builds Movie → Level → Transform → Blur and exposes a Blur control, but this catalog entry omits both.

Proposed fix
-      "Prompt → a fully wired AI-generated video-backdrop system (movie/level/transform + Play/Speed/Brightness/Scale).",
+      "Prompt → a fully wired AI-generated video-backdrop system (movie/level/transform/blur + Play/Speed/Brightness/Scale/Blur).",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
command: "create-ai-video-backdrop",
summary:
"Prompt → a fully wired AI-generated video-backdrop system (movie/level/transform + Play/Speed/Brightness/Scale).",
mutates: true,
unsafe: false,
source: "tool",
},
{
command: "create-ai-video-backdrop",
summary:
"Prompt → a fully wired AI-generated video-backdrop system (movie/level/transform/blur + Play/Speed/Brightness/Scale/Blur).",
mutates: true,
unsafe: false,
source: "tool",
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentCommandCatalog.ts` around lines 756 - 763, Update the
create-ai-video-backdrop catalog entry’s summary to include the Blur stage in
the Movie → Level → Transform pipeline and mention the exposed Blur control
alongside Play, Speed, Brightness, and Scale.

Comment thread src/feedback/pngDecode.ts
Comment on lines +47 to +59
function reconstruct(filter: number, value: number, left: number, up: number, ul: number): number {
switch (filter) {
case 1:
return value + left;
case 2:
return value + up;
case 3:
return value + ((left + up) >> 1);
case 4:
return value + paeth(left, up, ul);
default:
return value; // 0 or unknown filter — best effort
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject unsupported scanline layouts instead of returning ok: true.

Interlacing is ignored, unknown filters are treated as filter 0, and missing scanline bytes become zero. Valid Adam7 images and corrupt/truncated payloads therefore produce fabricated pixels. Parse the IHDR interlace field, require filters 0–4, and verify the inflated length before unfiltering.

Also applies to: 63-79, 100-104, 147-149

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/feedback/pngDecode.ts` around lines 47 - 59, Update the PNG decode flow
around reconstruct, IHDR parsing, and scanline unfiltering to reject unsupported
layouts instead of fabricating pixels: parse and reject nonzero interlace modes,
validate every scanline filter is 0–4 rather than treating unknown values as
filter 0, and verify the inflated data contains the complete expected scanline
bytes before unfiltering. Propagate these validation failures so decoding does
not return ok: true for Adam7, corrupt, or truncated payloads.

Comment thread src/tools/layer1/evolveParameters.ts Outdated
Comment on lines +259 to +270
scored.push({ genome, fitness });
if (!best || fitness > best.fitness) best = { genome, fitness };
}
population = nextGeneration(scored, args.genes, opts, rng);
}
if (!best) {
return errorResult("evolve_parameters: no candidate could be scored; nothing to rebuild.", {
evaluated,
warnings,
});
}
return await rebuildWinner(ctx, args, recipe, best, evaluated, warnings);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not report a winner when every fitness evaluation failed.

-Infinity is the scoring-failure sentinel, but the first such candidate populates best, causing an arbitrary genome to be rebuilt successfully.

  • src/tools/layer1/evolveParameters.ts#L259-L270: require Number.isFinite(best.fitness) before rebuilding.
  • tests/unit/evolveParameters.test.ts#L178-L187: expect an error for an unresolved target instead of successful winner creation.
📍 Affects 2 files
  • src/tools/layer1/evolveParameters.ts#L259-L270 (this comment)
  • tests/unit/evolveParameters.test.ts#L178-L187
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/layer1/evolveParameters.ts` around lines 259 - 270, The
evolveParameters flow must not rebuild a winner when all fitness evaluations
failed and best.fitness is the -Infinity failure sentinel. In
src/tools/layer1/evolveParameters.ts:259-270, require
Number.isFinite(best.fitness) alongside the existing best check before calling
rebuildWinner, returning the existing errorResult otherwise. In
tests/unit/evolveParameters.test.ts:178-187, update the unresolved-target case
to expect an error instead of successful winner creation.

Comment on lines +276 to +311
export async function evolveParametersImpl(
ctx: ToolContext,
args: EvolveParametersArgs,
): Promise<CallToolResult> {
if (Object.keys(args.genes).length === 0) {
return errorResult(
"evolve_parameters: `genes` is empty; provide at least one '<node>.<param>' search dimension.",
);
}
const recipe = ctx.recipes.get(args.build_target);
if (!recipe) {
const ids = ctx.recipes.list().map((r) => r.id);
return errorResult(
`evolve_parameters: unknown build_target "${args.build_target}". Available recipe ids: ${ids.join(", ")}.`,
);
}
const budget = args.population * args.generations;
if (budget > HARD_CAP) {
return errorResult(
`evolve_parameters: population*generations (${budget}) exceeds the hard cap of ${HARD_CAP}. Reduce population or generations — this is an offline, minutes-long search that mutates a scratch graph.`,
);
}
return runBuild(() => runEvolution(ctx, args, recipe));
}

export const registerEvolveParameters: ToolRegistrar = (server, ctx) => {
server.registerTool(
"evolve_parameters",
{
title: "Evolve parameters (experimental, offline)",
description:
"EXPERIMENTAL, OFFLINE / DESIGN-TIME: search a categorical parameter genome over a recipe with a genetic algorithm, scored by a tdmcp-measurable fitness (audio_energy Info-CHOP energy / top_luma / top_motion). Each candidate is built from `build_target`, has its genome-selected params applied, cooked, scored, then its scratch subtree is deleted; across `generations` of μ+λ elitism only genomes+scores are kept, and at the end the single winning genome is rebuilt under `parent_path` with a finalize preview. A full run is minutes and mutates a scratch graph while running — it is NOT a real-time controller. Hard cap: population*generations ≤ 300. Method adopted from NEvo (arXiv 2607.02317, CC BY 4.0).",
inputSchema: evolveParametersSchema.shape,
},
(args) => evolveParametersImpl(ctx, args),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Gate this Python-executing tool with allowRawPython.

The tool invokes executePythonScript for cooking and fitness telemetry, but remains exposed when TDMCP_RAW_PYTHON=off. Skip registration when ctx.allowRawPython === false, and retain an implementation guard for direct callers.

As per coding guidelines, raw Python execution must be controlled by TDMCP_RAW_PYTHON=off and the injected allowRawPython flag; execution tools must not be exposed when disabled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/layer1/evolveParameters.ts` around lines 276 - 311, The
evolve_parameters tool must be unavailable when raw Python execution is
disabled. Add an implementation guard in evolveParametersImpl that rejects
direct calls when ctx.allowRawPython is false, and update
registerEvolveParameters to skip registration in that case while preserving the
existing validation and execution flow when enabled.

Source: Coding guidelines

Comment on lines +156 to +157
const cachePath = await writeCachedImage(cacheDir, cacheKey, image);
return { ok: true, value: { cachePath, image, cacheKey, cacheHit: false } };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Catch file system errors to prevent throwing from the tool handler.

As per coding guidelines, tool handlers must never throw. If writeCachedImage fails (e.g., due to file system permission issues or disk space), it will throw an unhandled exception that bubbles up and crashes the MCP server. Wrap it in a try/catch and return an errorResult.

🛡️ Proposed fix to handle the caching error
-  const cachePath = await writeCachedImage(cacheDir, cacheKey, image);
-  return { ok: true, value: { cachePath, image, cacheKey, cacheHit: false } };
+  let cachePath: string;
+  try {
+    cachePath = await writeCachedImage(cacheDir, cacheKey, image);
+  } catch (err) {
+    return {
+      ok: false,
+      error: errorResult(`Failed to write generated image to cache: ${(err as Error).message}`, {
+        provider: ctx.imageGen.id,
+        model,
+      }),
+    };
+  }
+
+  return { ok: true, value: { cachePath, image, cacheKey, cacheHit: false } };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const cachePath = await writeCachedImage(cacheDir, cacheKey, image);
return { ok: true, value: { cachePath, image, cacheKey, cacheHit: false } };
let cachePath: string;
try {
cachePath = await writeCachedImage(cacheDir, cacheKey, image);
} catch (err) {
return {
ok: false,
error: errorResult(`Failed to write generated image to cache: ${(err as Error).message}`, {
provider: ctx.imageGen.id,
model,
}),
};
}
return { ok: true, value: { cachePath, image, cacheKey, cacheHit: false } };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/layer2/createAiTexture.ts` around lines 156 - 157, Update the
caching flow around writeCachedImage in the tool handler to catch file-system
failures and prevent exceptions from escaping. On failure, return the existing
errorResult with the caught error details; preserve the successful cachePath
result and cacheHit: false behavior when writing succeeds.

Source: Coding guidelines

Comment on lines +190 to +228
const hit = await findCachedVideo(cacheDir, cacheKey);
if (hit) {
return {
ok: true,
value: {
cachePath: hit,
video: {
bytes: new Uint8Array(0),
mimeType: mimeFromPath(hit),
provider: ctx.videoGen.id,
model,
durationSec: req.durationSeconds,
seed: req.seed,
},
cacheKey,
cacheHit: true,
},
};
}

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), req.timeoutMs ?? DEFAULT_VIDEO_GEN_TIMEOUT_MS);
let video: VideoGenResult;
try {
video = await ctx.videoGen.generate({ ...req, model }, controller.signal);
} catch (err) {
return {
ok: false,
error: errorResult(`Video generation failed: ${(err as Error).message}`, {
provider: ctx.videoGen.id,
model,
}),
};
} finally {
clearTimeout(timer);
}

const cachePath = await writeCachedVideo(cacheDir, cacheKey, video);
return { ok: true, value: { cachePath, video, cacheKey, cacheHit: false } };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Represent cache I/O failures consistently as tool errors. The cache layer currently hides some failures while the tool layer lets others escape as rejected handlers.

  • src/tools/layer2/createAiVideo.ts#L190-L228: catch cache lookup/write failures and return errorResult.
  • src/services/videoGen/cache.ts#L51-L59: suppress only ENOENT; propagate permission and I/O failures.
📍 Affects 2 files
  • src/tools/layer2/createAiVideo.ts#L190-L228 (this comment)
  • src/services/videoGen/cache.ts#L51-L59
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/layer2/createAiVideo.ts` around lines 190 - 228, Update
createAiVideo to catch failures from findCachedVideo and writeCachedVideo,
returning consistently structured errorResult values with provider and model
context instead of allowing rejected handlers. In src/services/videoGen/cache.ts
lines 51-59, modify the cache lookup handling to suppress only ENOENT and
rethrow permission or other I/O failures; apply the tool-layer handling at
createAiVideo and the errno filtering at the cache implementation.

Source: Coding guidelines

Comment on lines +340 to +351
export const registerCreateAiVideo: ToolRegistrar = (server, ctx) => {
server.registerTool(
"create_ai_video",
{
title: "Create AI video",
description:
"Turn a text prompt (+ optional init image) into a short clip via a provider-agnostic engine — hosted fal OR local ComfyUI — and drop it into TouchDesigner as a Movie File In TOP the artist can wire by hand. NOT real time: generate ahead (seconds–minutes), then play live. The clip is generated Node-side, cached to a local dir, and delivered as an absolute file path (server + TD are colocated) — no key ever reaches the bridge. Default model ltx-video (cheapest, fixed 5s); ltx-2 for variable length + higher res + audio. Same request reuses the cached file (no API call). Requires TDMCP_VIDEO_GEN_PROVIDER=fal (+TDMCP_FAL_KEY) or =comfyui (+TDMCP_COMFYUI_VIDEO_WORKFLOW); without them the tool returns a friendly error and builds nothing. Use create_ai_video_backdrop for a fully wired, control-exposed playback system instead of a bare TOP.",
inputSchema: createAiVideoInputSchema.shape,
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
},
(args) => createAiVideoImpl(ctx, createAiVideoSchema.parse(args)),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -e
printf 'Inspecting relevant files...\n'
git ls-files 'src/tools/layer2/createAiVideo.ts' 'src/tools/layer2/*' | sed -n '1,120p'
printf '\n--- outline createAiVideo.ts ---\n'
ast-grep outline src/tools/layer2/createAiVideo.ts --view expanded || true
printf '\n--- key symbols around schema/registration ---\n'
rg -n "createAiVideo(Schema|InputSchema|Impl|registerCreateAiVideo|superRefine|safeParse|parse\\()" src/tools/layer2/createAiVideo.ts src/tools/layer2 -S

Repository: Pantani/tdmcp

Length of output: 7781


🏁 Script executed:

set -e
printf 'Reading relevant sections...\n'
wc -l src/tools/layer2/createAiVideo.ts
sed -n '1,220p' src/tools/layer2/createAiVideo.ts
printf '\n--- middle ---\n'
sed -n '220,420p' src/tools/layer2/createAiVideo.ts

Repository: Pantani/tdmcp

Length of output: 13384


🏁 Script executed:

set -e
printf 'Searching for tool handler wrapping and parse usage...\n'
rg -n "registerTool\\(|safeParse\\(|parse\\(args\\)|try \\{|catch \\(" src -S | sed -n '1,240p'
printf '\n--- tool/server types ---\n'
sed -n '1,240p' src/tools/types.ts
printf '\n--- result helpers ---\n'
sed -n '1,220p' src/tools/result.ts

Repository: Pantani/tdmcp

Length of output: 21531


🏁 Script executed:

set -e
printf 'Finding registerTool definition and dispatcher...\n'
rg -n "registerTool\\s*\\(" src -S
printf '\n--- likely server implementation files ---\n'
fd -a "server" src | sed -n '1,120p'
fd -a "tool" src | sed -n '1,120p'

Repository: Pantani/tdmcp

Length of output: 34416


🏁 Script executed:

set -e
printf 'Inspecting llm tool runtime...\n'
wc -l src/llm/tools.ts
sed -n '380,520p' src/llm/tools.ts
printf '\n--- server tool dispatch ---\n'
sed -n '1,260p' src/server/tdmcpServer.ts

Repository: Pantani/tdmcp

Length of output: 7461


🏁 Script executed:

set -e
printf 'Inspecting tool registration patterns in nearby files...\n'
sed -n '1,240p' src/tools/layer3/execNodeMethod.ts
printf '\n--- another layer2 tool with parse/safeParse ---\n'
sed -n '420,470p' src/tools/layer2/applyLut.ts

Repository: Pantani/tdmcp

Length of output: 3047


🏁 Script executed:

set -e
printf 'Inspecting the paired backdrop tool and shared schema usage...\n'
wc -l src/tools/layer1/createAiVideoBackdrop.ts
sed -n '1,260p' src/tools/layer1/createAiVideoBackdrop.ts
printf '\n--- references to createAiVideoSchema / createAiVideoImpl ---\n'
rg -n "createAiVideoSchema|createAiVideoImpl|createAiVideoInputSchema|refineVideoModel" src/tools -S

Repository: Pantani/tdmcp

Length of output: 8889


Return validation failures instead of throwing The createAiVideoSchema.parse(args) call in the registered handler can throw on invalid model/duration_seconds/resolution combinations. Use safeParse here and return errorResult so the tool handler stays non-throwing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/layer2/createAiVideo.ts` around lines 340 - 351, Update the
registered create_ai_video handler in registerCreateAiVideo to validate
arguments with createAiVideoSchema.safeParse instead of parse. When validation
fails, return the resulting errorResult, and pass the parsed data to
createAiVideoImpl only on successful validation so invalid model,
duration_seconds, or resolution combinations do not throw.

Source: Coding guidelines

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fbab3e3b7f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +92 to +94
if ("length" in inputs && req.durationSeconds !== undefined)
inputs.length = secondsToLtxFrames(req.durationSeconds);
if ("image" in inputs && uploadedImage) inputs.image = uploadedImage;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply requested dimensions to ComfyUI workflows

With TDMCP_VIDEO_GEN_PROVIDER=comfyui, toVideoRequest passes the parsed resolution as req.width/req.height, and the bundled LTX workflow has width/height inputs on the latent node, but this injection path only mutates length and image. Any request for 1280x720, 1920x1080, or 4k will still render at the workflow's baked dimensions while the cache key/result correspond to the requested size, so either inject width/height when those inputs exist or reject unsupported workflow dimensions.

Useful? React with 👍 / 👎.

for (const s of scored) if (!best || s.fitness > best.fitness) best = s;
population = nextGeneration(scored, args.genes, opts, rng);
}
if (!best) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject all-failed evolution runs

When every candidate fails to score, for example because fitness_target_path does not resolve or preview capture fails for all candidates, each candidate gets -Infinity but best is still assigned on the first scored item. This !best check therefore does not catch the all-failed run, and the tool rebuilds an arbitrary genome with best_fitness: -Infinity, which can corrupt experiment results instead of reporting that nothing was scored.

Useful? React with 👍 / 👎.

Comment on lines +52 to +54
model: z
.enum(["ltx-video", "ltx-2"])
.default("ltx-video")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the configured video model default

When callers omit model, this schema default writes ltx-video into the parsed args before both AI video tools call toVideoRequest, so req.model is always set and ctx.videoGen.defaultModel from TDMCP_VIDEO_GEN_MODEL is never used for normal tool calls. Operators who configure the default to ltx-2 or a provider-specific slug still get ltx-video unless every call explicitly supplies a model, unlike the image lane where model remains optional and the provider default can apply.

Useful? React with 👍 / 👎.


/** Map a friendly model key onto the fal slug for the requested mode. */
private resolveSlug(modelKey: string, req: VideoGenRequest): string {
const entry = MODEL_SLUGS[modelKey] ?? MODEL_SLUGS[DEFAULT_FAL_VIDEO_MODEL];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass unknown fal video slugs through

For a configured/custom fal video model not present in MODEL_SLUGS, this expression falls back to the default ltx-video entry before the raw-slug branch, making if (!entry) return modelKey unreachable. If TDMCP_VIDEO_GEN_MODEL or a lower-level request supplies a raw slug, the provider posts to the default ltx-video endpoint while reporting the custom model in metadata/cache results, so custom fal models cannot actually be used.

Useful? React with 👍 / 👎.


const { cachePath, video, cacheHit } = gen.value;
try {
const ref = await ctx.client.createNode({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Place standalone AI TOPs deterministically

AGENTS.md says every operator creation path must assign deterministic nodeX/nodeY; this direct createNode path, and the analogous direct TOP creation in create_ai_texture, has no coordinates or layout pass. On repeated create-ai-video/texture calls under the same parent, the nodes can land at the bridge/default drop position and overlap, so use the existing grid/layout helper after creating these standalone nodes.

Useful? React with 👍 / 👎.

Pantani and others added 2 commits July 15, 2026 20:44
The coverage gate dipped to 72.92% branch (threshold 73%) because the new
videoGen/falProvider.ts and feedback/pngDecode.ts branches were under-tested.
Add pure pngDecode/frameStats tests (every failure reason, scanline filters
0-4, grayscale + histogram fallback) and msw fal-provider tests (t2v/i2v slug
resolution, video.url vs videos[] shapes, cost, and every HTTP error path).
Global branch coverage back to 73.02%.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
HttpResponse.json needs a JsonBodyType; the mock helper's result param was
unknown. Type it as the fal result shape. vitest was green but tsc/build failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Pantani
Pantani marked this pull request as draft July 16, 2026 04:04
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.

2 participants