Conversation
… a project-start sink (#5920) `pipeline/episodeVideo.js` importing `creativeDirector/completionHook.js` was the only static edge running pipeline -> creativeDirector, and the CD tool registry starts a Series Autopilot in the other direction — together they closed a 22-module strongly-connected component, the largest in `server/services`. In a static ESM cycle whichever member evaluates first sees `undefined` for the others' bindings, so any top-level const derived from an import in the ring is a TDZ crash waiting on a module-ordering change. New leaf `creativeDirector/projectStartSink.js` inverts that one edge: the pipeline depends on the sink, the completion hook registers the concrete starter on it at module evaluation, and `server/index.js` imports the hook for that side effect at boot. An unregistered request throws rather than silently dropping the start, because a CD project that never advances is otherwise invisible. Also applied the barrel rule the issue names: `planAdvance.js` and `creative/tools/pipeline.js` now import the modules that DECLARE the autopilot symbols instead of re-entering the `seriesAutopilot.js` barrel from outside the package. Deferring an import with `await import()` would have hidden the cycle from the guard without removing the hazard, so it was not used. The component is gone, not shrunk — `findImportCycleComponents` reports nothing in that half of the graph, and the #5920 baseline entry is deleted from KNOWN_CYCLIC_COMPONENTS in serviceImportCycles.test.js.
…ed (#6013) Review follow-up: the job lingers in the map until closeJobAfterDelay evicts it, so guarding cancel on the canceled flag alone made a cancel for a completed import answer ok:true. Mark the job done in the kickoff's finally and require a running status.
…per-step reasoning (#5992) A CoS task resolves one provider and one model for its whole run, so a user who wants strong reasoning for the planning also pays that model's rate for the mechanical editing. This adds an opt-in orchestration profile: three roles (architect plans and delegates, implementer executes one spec, reviewer checks it), each with its own provider/model/effort. - `server/lib/orchestrationProfile.js` owns the vocabulary, the normalizer, and the six-part spec contract a context-free delegated lane needs. Its `parseReasoningDirective` NEVER rounds an unsupported rung — a substituted level would run a step at an effort nobody chose while reporting success. - `selectModelForRole` sits alongside `selectModelForTask`, which now resolves through the architect role; with no profile the selection is unchanged. - `resolveStepEffort` resolves a delegated step's rung as spec directive → role default → run effort, instead of one effort for the whole run. - The architect doctrine renders into both prompt paths and is empty for every `direct`-mode task, which is the default, so an install that configures no profile behaves exactly as before. Named `orchestrationMode` rather than the issue's `executionMode`: agent metadata already carries an `executionMode` meaning tui/runner/direct, and reusing the name — including the value `direct` — for an unrelated axis would have made both unreadable.
…r a manual Run Resume and Relaunch both requeued the paused agent's task and stopped there. The requeue only makes a task ELIGIBLE — what spawns it is the automatic dequeue `completeAgent` schedules, and that path admits pending user tasks plus auto-approved system tasks under CoS auto-run in `execute` mode. So on an install with auto-run off, or for a task still awaiting approval, the task sat `pending` until the user opened the task list and pressed Run. One click became two, on the screen the user had just acted from. `resumeAgent` now force-spawns the task it requeued, through `forceSpawnTask` — the same door "Run now" uses — so it inherits those refusals rather than restating them. A stopped/paused daemon, an unreachable runner, a task needing approval, and a full agent pool all still mean "leave it queued"; the refusal comes back as `spawnHold` so the dialog names it instead of toasting a resume that silently didn't start. Relaunch is a pause plus a resume, so it inherits the dispatch through that composition. Deliberately not pushed deeper into `reviveBlockedTask` or the `tasks:changed` unblock listener: those are shared with the autonomous revival paths (investigation retry, orphan cooldown, completion cleanup), where force-spawning would strip the auto-run gate that withholds unattended spawns. Both doors into the new dispatch are a human clicking a button. Both dialogs render the outcome through one shared helper so neither can say "queued" for a run that already started, or omit the reason it didn't.
… name the new-task resume outcome A spawn registers its agent as running BEFORE it flips the task off pending, and the refusal that lands in that window is forceSpawnTask's own holder guard — so reading the task status alone still saw 'pending' and reported a hold for a run already under way. Check the running-agent holder too. AgentsTab had no 'new-task' wording, so a resumed replacement task that the server started reported only the generic 'Created resume task'.
hoist the SongBook import Save action into PageHeader so it stays above the fold
fix: cancel yt-dlp audio imports reliably when no child process is running
replace real sleeps and a 22.7M-element Python fixture in the detachedSpawn and trellis2 runner suites
Break the peerSync <-> peerSyncReceive import cycle by extracting the subscription store to a leaf
break the 22-module seriesAutopilot/creativeDirector import cycle with a project-start sink
start a resumed CoS agent's task immediately instead of leaving it pending for a manual Run
…PERATIONAL.md under data/ (#6032) GOALS.md lives at the repo root and docs/GOALS_OPERATIONAL.md lives under docs/ — neither exists under data/. Corrected the documented tree to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…6033) The documented template list (vite+express, node-server, static) no longer matches SCAFFOLD_TEMPLATES in server/lib/validation.js, so following the docs produces a template name that fails validation. Updated the list to the 6 real templates and added the 2 scaffold endpoints missing from the table (GET /api/scaffold/directories, POST /api/scaffold/templates/create). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tion (#6059) The test asserted `.not.toContain('wt-a'/'wt-b')` against the raw `git worktree list --porcelain` output, which always includes the parent sandbox's own entry. When that entry's random mkdtemp() suffix happens to start with 'a' or 'b', the substring can coincidentally appear inside the parent's own path and the assertion false-fails (~3.2% chance per prefix). Replaced it with a parse of the porcelain output into worktree entries (mirroring the parsing resetGitWorktreeSandbox itself already does in gitTestRepo.js, which drops the first entry as always being the parent) and assert exactly 1 entry remains. This avoids the collision and also sidesteps comparing full paths directly, which git can respell on Windows (see the assertPath test in this same file, #6003). Verified by forcing the exact collision deterministically (parent sandbox created at a path ending in `wt-b-XXXXXX`): confirmed the old assertion fails in that case and the new one does not. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d set (#6010) server/lib/conflictJournal.js's RESTORABLE_FIELDS.fableLoom (documented as the single source of truth for conflict restore/merge) was missing productionStatus, protagonistCharacterId, protagonistWardrobeId, and protagonistWardrobeLocked, even though records.js already treats them as restorable. Concretely: restoring a conflict via "Restore All" silently stripped those 4 fields, and "merge fields" rejected them outright with ERR_VALIDATION — a real data-loss path for protagonist continuity and production sign-off after a sync conflict. Added the 4 fields to close the gap. Added restore-all and merge-fields coverage in conflictJournalResolver.test.js, split so the protagonist-fields case and the productionStatus case don't collide with mutateLoom's own (unrelated, intentional) rule that clears productionStatus whenever editorial content actually changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ure (#6018) bulkStar in MediaCollectionDetail.jsx ignored updateAnnotation's result and always fired a success toast, while useMediaAnnotations.js already toasted an individual error for each failure — contradictory toasts, plus the success toast lied about items that were actually reverted. updateAnnotation now takes a { silent } option and returns { ok, entry } instead of a bare entry — entry alone can't signal success/failure because the server legitimately returns entry: null on a real success too (an unstar with no note clears the annotation entirely). bulkStar passes { silent: true }, counts successes/failures, and shows one consolidated toast for all-success, all-failure, and partial-failure. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
server/routes/update.test.js had zero tests for /api/update/sync-fork despite the route implementing 7 distinct error codes plus success — regressions in schema validation, status codes, or error mapping had no automated guard. Added the 9 cases from the issue's Fix section: success with default and custom branch, all 4 pre-flight 400s (NO_ORIGIN, NOT_GITHUB, ALREADY_UPSTREAM, NOT_A_FORK), 502 GIT_UNAVAILABLE, 409 FORK_DIVERGED (asserting the recovery guidance in the message), 502 FORK_SYNC_FAILED, and a schema rejection on an invalid branch name. No production code changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add an opt-in orchestrated CoS mode with per-role provider/model and per-step reasoning effort
Break the pipeline manuscriptReview <-> manuscriptFix static import cycle
Render practice rating hints as visible button subtext (#6000)
Fix songbook transpose readout width collapsing mobile controls bar (#5999)
Break the loraDatasetCaption <-> loraDatasetGenerate static import cycle (#5917)
Rigged + animated image-to-3D records are selectable through the avatar variant namespace (rigged-<modelId>, same traversal guard), listed with server-computed clip coverage, offered in the CoS avatar-style selector, and played back with a deterministic present-clip fallback for uncovered states. Eidoverse player-model wiring stays open: no GLB player surface exists on main (human/cos avatars are VRM-typed for the external host).
… landed A recent main commit split the rating button's text into a label span plus a visible hint span, which made the button's accessible name the full label+hint string instead of just the label — breaking SongBookViewer.test.jsx's 'Solid' button lookup and, with it, CI on this branch. Pin the accessible name back to the label via aria-label.
…-location docs: fix ARCHITECTURE.md directory tree (GOALS.md / GOALS_OPERATIONAL.md location)
feat: expose rigged animated records to CoS avatars
docs: fix app-wizard.md template list and scaffold endpoints table
A suite that writes into the developer's live `data/` is invisible: unlike a read leak, persisting fixture bytes over their records changes nothing about whether assertions pass. #6171 was the proof — `providerUsage.test.js` wrote its fixture quota cards over the machine's real `data/provider-quotas.json`, then invalidated the sync checksum so peers pulled the fabricated values. Close that half at runtime, the way `db.js` already refuses row writes to a non-test database. `lib/testDataIsolation.js` throws when a write resolves inside the real data root under the test runner, naming the path and the `createTempDataRoot()` escape hatch. It fires from `atomicWrite`, `ensureDir`'s create path (an existing dir is a no-op, so read-only suites stay quiet), new `writeFile`/`appendFile`/`copyFile` wrappers in `fileCore.js`, and `collectionStore`'s record delete — so the next raw writer inherits the guard instead of having to remember an import. `aiToolkit/` is untouched, per its self-containment rule. It found six real leaks on its first run: - `backup.test.js` — `vi.doUnmock` also drops the file-level `vi.mock`, so every later suite resolved PATHS.data to the real tree and `runBackup` rewrote the user's genuine `data/backup/state.json` on every run. - `providers.readiness.test.js` and `loraTraining/captionLeakStaging.test.js` — took the MACHINE-WIDE heavy-local-job claim in the live tree, the file that gates the user's real local renders. - `sharing/integration.test.js` — a hand-rolled PATHS proxy listed `data`, `images` and `videos` by hand, so the later-added `imageRefs` still pointed at `data/image-refs` and the importer copied bundled sheets there. - `assetHash.test.js` and `modelAbuseGuard.materialize.test.js` — wrote fixtures into the real tree deliberately, swept up only when nothing threw. Supporting changes: `isTestRunner` moves out of `db.js` into a dependency-free `lib/runtimeEnv.js` (the file primitives must not pull in `pg`, and the many suites spelling `vi.mock('../lib/db.js', () => ({ query }))` were stripping it out of the graph for every other consumer); `lib/pathContainment.js` owns the root-inclusive containment and ancestor-canonicalizing helpers, kept a leaf so `fileCore` reaches them without dragging `errorHandler`/`paths` into every suite's closure; the guard itself loads through a memoized `await import()` behind `isTestRunner()`, so production never loads it and the tree-wide import budget is unchanged. `mockPathsDataRoot.js` gains `lazyTempDataRoot` / `cleanupTempDataRoots` so the hoisting hazard is explained once rather than in each suite that redirects a data root. Writes only: reads stay covered by the two-run probe, and roughly forty services still reach `PATHS.*` with raw `fs`. Both limits are stated in the module header rather than implied away, with the sweep tracked separately.
…ment Local review found three defects in the guard shipped in the previous commit. The one that mattered: `writeFile`, `appendFile` and `copyFile` FOLLOW a symlinked destination, but the guard judged only the link's own location. A fixture symlink inside a temp root pointing at `data/provider-quotas.json` therefore wrote straight through into live data — the exact leak the guard exists to stop. Both landing sites are now checked, resolved through `readlink` + `canonicalizePath` rather than `realpathSync` so a DANGLING link (one naming a file the real tree has not created yet) still reports its target. `isPathAtOrInsideDir` also reported nothing as inside a filesystem or drive root: those already end in the separator, so anchoring on `root + sep` compared against `//`. Unreachable from today's `<install>/data` root, but the helper is a new public export whose contract promises otherwise. And the `..`-climb case in the guard's own test was vacuous — `path.join` normalizes its arguments, so the value under test arrived already collapsed and the assertion held even with `resolve()` removed. Rebuilt by concatenation, alongside new relative-path, symlink and filesystem-root cases. Also routes `services/runner.js`'s raw write of `data/runs/<id>/output.txt` through the guarded wrapper (`agentRunTracking.js` writes the same file), and exempts the guard's contract test from the static isolation rule: it has to name the real root to prove a refusal, and every assertion against a real path asserts the call rejects.
The rebase onto main tightened importScoping.test.js's tree-wide budget just enough that this file's static import of testDataIsolation.js (and its pathContainment.js closure) pushed the suite to 85,129 instantiations, 85 over the 85,000 ceiling. fileCore.js already lazy-loads the same guard behind isTestRunner() for exactly this reason; mirror that pattern here instead of raising the budget.
spawn a restricted public-review TUI as a direct PTY instead of hosting it in a login shell
# Conflicts: # server/services/sprites/walk.test.js
…-parallelize-walkset
…ier-report feat: report Tribe emails/phones shared by more than one person
…/ leak CI caught this after the rebase: the annotated-regen route stages its init-image snapshot under PATHS.imageRefs (ensureDir + write), and this suite never redirected PATHS away from the real install tree, so it wrote that snapshot into the developer's live data/image-refs on every run. It only looked green locally because that directory already existed from prior runs — ensureDir's create-path guard is a no-op against an existing dir, so the write went unnoticed until a fresh checkout (or a first-time directory) exposed it. This is the same class of leak #6176 already fixed in six other files; this one's #7. Also mocks lib/paths.js alongside lib/fileUtils.js: pathSafety.js's resolveGalleryImage/resolveImageRef/resolveImageInputPath read PATHS from paths.js directly, so the fileUtils.js redirect alone left the runner's own re-validation of the staged path checking against the real root.
…l defaults
A `/do:next` claim resolves its reviewers from the claim-work task metadata
FIRST and only falls back to the install-wide Code Review Defaults. Both manual
claim surfaces seeded their reviewer display from `GET /api/code-review/defaults`
alone, which cannot see that override — so a claim-work pin saved months earlier
kept running codex + claude while every reviewer control on screen showed the
antigravity chain the user had since configured. The Issues tab, which offers no
reviewer picker at all, showed nothing.
- New `GET /api/apps/:id/claim-reviewers` resolves the chain through the same
`resolveClaimWorkMetadata` → `resolveClaimReviewerConfig` path
`buildClaimWorkTask` uses to fill the prompt's `{reviewers}` token, and reports
`source` (`task-override` vs `defaults`) so the UI can name the layer that won.
- `useClaimReviewers` backs both surfaces. A failed lookup stays unresolved
rather than reporting an empty chain — "couldn't ask" must not read as "merges
with no review".
- The run drawer seeds its untouched picker from that resolution and, on an
override, points at Chief of Staff → Schedule rather than Models → Code
Reviewers. The Issues tab renders the same read-only summary beside its
provider pin.
- `REVIEWER_OVERRIDE_KEYS` / `hasReviewerOverride` replace the hand-listed roster
in GlobalConfigControls, so the picker's "Use system Code Review Defaults"
reset clears exactly what the server counts as an override; the client mirror
is pinned by the existing parity test.
… clearable Cleanup on the claim-reviewer lookup, plus the one gap that made its advice unfollowable. - `claimReviewersFrom` / `resolveAppClaimReviewers` (cosTaskGenerator) now own the layer precedence for both the claim builder and the lookup route. The route had hand-copied that chain, which is the drift the lookup exists to prevent. - `hasReviewerOverride` keys on `REVIEWER_LIST_OVERRIDE_KEYS`, not the full roster: `reviewStopMode` / `reviewerApplies` are slashdo run flags and a claim prompt has no flag string to put them in, so neither can change which reviewers run. Reporting a stop-mode as the source sent the user to clear a pin that wasn't supplying the list they were looking at. The wide roster stays for the picker's reset, which does clear both. - `ClaimReviewerSource` renders the "where this came from" sentence for both surfaces; they had already diverged on which panel they pointed at. - The reviewer picker — and the "Use system Code Review Defaults" reset beside it — now render for claimFlow task types. Their shipped metadata sets neither `openPR` nor `reviewLoop`, so the picker never appeared for `claim-work`: the override every claim obeys had no control anywhere that could clear it, while the claim surfaces told the user to come here and do exactly that. - `useClaimReviewers` returns the payload or `null` instead of a 9-field sentinel with a derivable `resolved` flag, which flattens the drawer's seeding memo. - Trimmed the retold rationale to the resolution site, and the route's response test down to what the route itself decides — the resolution it previews is covered behaviorally on the shared resolver. Follow-ups filed: #6208 (the picker persists a defaults snapshot as an override, which is what manufactured the stale pin) and #6210 (the JIRA play button skips the claim-work layer its own docstring promises to honor).
- ClaimReviewerSource sent the user to "this app's Automation tab" to clear the override. The reviewer picker is rendered only by GlobalConfigControls, reached only through Chief of Staff → Schedule; the Automation tab has no such control, so that half of the sentence was a dead end. Names the one screen that works. - The apiApps doc said `task-override` means the defaults "were never consulted". resolveReviewerConfig falls back per FIELD, so a task pinning only `reviewers` still takes its models and usernames from the defaults. - IssuesTab carried an empty-reviewers branch saying a Claim "will merge without one". claimSafeReviewers never returns an empty list, so the branch was unreachable and its claim was wrong either way.
fix: preserve active model tuning across context-window reloads (#6200)
CI's smoke-boot step (npm run smoke) starts the real server and deliberately sets NODE_ENV=test to select the file-backend escape hatch documented in AGENTS.md — it is not a Vitest suite. The #6176 write guard gated on isTestRunner() (NODE_ENV==='test' OR VITEST), so that legitimate real boot got treated as a test writing into its own data/ tree and every startup write (usage.json, instances.json, cos/, brain/, voice-timers.json, loops) was refused, crashing the smoke-boot job in CI. Add isVitestRunner() (VITEST only) and use it everywhere the guard decides whether to fire: fileCore.js's ensureDir/atomicWrite/writeFile/appendFile/ copyFile, collectionStore's record delete, and userActions.js's local read+write guard. Left isTestRunner() itself, and the backend-selection call sites that key on it (postRunStore.js, userActions.js's isFile, sprites/records.js, db.js's non-test-database refusal), unchanged — those correctly want the broader "NODE_ENV=test OR VITEST" signal. Verified: `npm run smoke` now boots clean, and the guard still throws for an actual Vitest suite writing outside its redirected data root.
refuse test writes into the install's real data/ tree at runtime
show which reviewers a manual claim will actually run
…ze-walkset test(sprites): hoist lockAllAnchors + parallelize buildFinalizedWalkSet's writes
…ion dirs Grok writes prompt_history.jsonl directly inside a cwd-keyed sessions folder, as a sibling of the per-session-id directories. Treating every entry as a session id crashed the reconciler with ENOTDIR trying to read <file>/summary.json. Fixes #6218.
pitchDetect's throttle test waited on a real 120ms clock, so CPU contention could starve the unthrottled setTimeout(intervalMs: 1) loop down to the same tick count as the throttled one; switch it to fake timers like the rest of the file already does. localLlm's plain-install tests ran the real disk-space preflight against the actual filesystem, so a host with less free space than a curated model's advertised size (16.5GB for Qwen3.8-27B) hit a false DISK_INSUFFICIENT before ever reaching downloadModel/pullModel; inject an abundant fake statfsImpl so these tests no longer depend on host disk capacity.
…RA play-button prompt resolveClaimReviewerPrompt resolved the Code Review Defaults alone, so a reviewer chain pinned for claims reached GitHub/GitLab/PLAN.md runs but silently not JIRA ones, and GET /api/apps/:id/claim-reviewers previewed a chain the JIRA run would not use. It now takes the app and layers claim-work metadata over the defaults through the same claimReviewersFrom the scheduled path uses; buildJiraTicketTask passes its app through.
… default claim path
fix([issue-6210]): layer the claim-work reviewer override into the JIRA play-button prompt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v2.58.0
Released: 2026-09-04
Highlights
CoS / agent orchestration
Local LLM / model management
OLLAMA_CONTEXT_LENGTHis already set.Animation / sprites / rigging
Reliability and data integrity
safeJSONParse's scalar-parsing change had broken object-shape assumptions downstream — guarded, and the relatedisValidJSONfork was inlined back intosafeJSONParseto stop the two from drifting.Performance
Windows & platform fixes
npm install's inline audit no longer stalls install paths.Accessibility & UI polish
Full Changelog
Full Diff: v2.57.0...v2.58.0