v2.6.0 — Code-Review Backlog Cut
OctoOffice v2.6.0
Released: 2026-05-08
Type: Minor release (security + architecture + testing)
TL;DR
Closes the entire 2026-05-07 code-review backlog: 41/41 high-and-critical
issues across five waves, plus three transitive-dependency security overrides.
Backend test coverage expands from 35 % statements to 39 % statements / 72 %
functions / 72 % branches; backend test count grows from ~1900 to 2492.
No breaking API changes. Existing runtime contracts preserved.
Overview by Stream
Wave 1 — Critical security (4 issues)
Already shipped via hotfixes between v2.5.2 and this cut:
- Shell-injection prevention in
GIT_ASKPASS(#51). - Whole-string validation for CLI API keys (#53).
safeCommunityPathcontainment fordefault_workspace(#88)./api/github/clonecontainment to home root (#54).
Wave 2 — Frontend & Accessibility (7 issues)
- #52
MobileBottomSheetexposesrole="dialog",aria-modal, focus-trap, and Escape-to-close. - #60
useMobilehook is SSR-safe and falls back to legacyaddListeneron older Safari. - #61 Pixi.js office canvas honors
prefers-reduced-motionfor wandering, walk-cycle, head-bob, typing-jitter, and pulse animations. - #62 Light theme
--text-muteddarkened to#52525b(≥7:1) and--accent-textto#047857(≥5.25:1) to meet WCAG AA. - #63 Topbar nav, ghost buttons,
+ NEW MISSION, and pack selector enlarged to ≥36×36 (WCAG 2.5.8). - #64 Pixi office canvas exposes a visually-hidden DOM-twin department list revealed on
:focus-within. - #65
App.tsxextracted intoRoomThemesContext,DecisionInboxContext, anduseOfficePackBootstrap; handlers memoized withuseCallback.
Wave 3 — Security & Architecture (8 issues)
- #89 SSRF guard pre-resolves DNS and pins the validated IP to a request-scoped
undici.Agentdispatcher, closing the TOCTOU window. Reviewer-driven follow-up (#89 review #2): redirect loop re-validates every hop with a 5-hop limit and stripsAuthorizationon cross-origin redirects. - #90
pnpm.overridespinip-address >= 10.1.1(GHSA-v2v4-37r5-5v8g, XSS inAddress6). - #91
pnpm.overridespinhono >= 4.12.16(GHSA-9vqf-7f2p-gf9v bodyLimit bypass + GHSA-69xw-7hcm-h432 JSX injection). - #55 ComfyUI HTTP helpers moved into
connectors/built-in/comfyui/http.ts; legacymodules/workflow/comfyui/*exports are@deprecatedre-exports. New architecture test enforces one-way layering. - #56
graph-builder.tsmoved intoserver/packs/; orchestration now imports frompacks/, breaking the mutual recursion. New architecture test enforcespacks/does not import frommodules/workflow/. - #57 Approve handler stops the active agent process before mutating phase state, mirroring
/resetand/reset-from. - #58
routeFollowUpViaCeoreturns a discriminatedRoutingResult({decision, source} | {decision: null, reason}) with metrics emitted per branch (ceo.followup.routing). - #59 Phase-approve re-run uses
deps.runTaskinstead of an HTTP self-loop, removing deadSESSION_AUTH_TOKEN/port logic.
Wave 4 — TypeScript Strictness (10 issues)
- #78 Drop
Promise<any>casts fromsrc/api/messaging-runtime-oauth.ts; six functions now use typedpost<T>()andresp.json() as Promise<T>matching their declared signatures. - #79
CrossDeptCooperationDepsreplaced with concretePick<RuntimeContext, …>interface (32 fields). - #80
ReportRoutingDeps,ReviewConsensusDeps, andOutcomeContextreplaced with concretePick<RuntimeContext, …>interfaces. - #81
PUT /api/settingsenforces a 32-key allowlist; unknown keys return400 unknown_setting_key.GET /api/settingsfilters internal keys (access_password_hash,mcp_servers,remote_session:*, etc.) so they cannot leak via authenticated round-trip. - #82
PUT /api/ops/workflow-packs/:key/positionsvalidates the body with a strict Zod schema (≤200 phases, finite numbers,strictObjectfor{x, y}). - #83
db: anyreplaced with structuralDbLikeacross 13 deps interfaces. New sharedserver/types/db-like.ts. - #84
useAgentLayerper-agent sprite-load failures isolated viaPromise.all(agents.map(...))and logged. - #85 LLM API responses validated with per-provider Zod schemas (Anthropic, Google, OpenAI). Throws typed
LlmResponseParseErroron shape mismatch instead of silently returning empty strings. - #86 CSRF guard
resparameter typed asexpress.Responseacrossexecution-control,api-providers,operations, andmcp-servers. - #87
cli-authroute handlers usecatch (err: unknown)+toErrorMessage(err).
Wave 5 — Test Coverage (11 issues)
| Module | Coverage gain |
|---|---|
message-idempotency.ts |
0 % → 99 % statements (#66) |
oauth/encryption (encrypt/decrypt) |
0 % → 100 % branch (#67) |
decision-inbox-routes.ts |
23.66 % → 96.79 % (#68) |
opencode adapter |
30.95 % → 100 % (#69) |
context-sharing.ts |
0 % → 100 % (#70) |
planned-approval.ts |
0 % → 100 % statements / 80.76 % branch (#71) |
planning-archive-tools.ts |
0 % → 100 % statements / 90.21 % branch (#72) |
oauth/helpers (PKCE/redirect) |
27.27 % func → 100 % branch (combined with #67, #73) |
oauth-runtime.ts |
0 % → 95.91 % (#74) |
cli-runtime.ts (agent spawn path) |
0 % → 86.92 % (#76) |
ceo-orchestrator.ts (tick loop) |
18.55 % → 97.30 % (#77) |
Plus a Vitest regression suite that asserts every mergeSettingsWithDefaults({}) key is in ALLOWED_SETTING_KEYS, preventing future drift.
Bonus — fast-uri override
Surfaced during Wave 5 CI: pnpm audit flagged fast-uri <= 3.1.0 (GHSA-q3j6-qgpj-74h6, path traversal via percent-encoded dot segments) pulled transitively via @modelcontextprotocol/sdk > ajv. Pinned via pnpm.overrides to >= 3.1.1.
Reviewer Findings Resolved Mid-Stream
The release also incorporates reviewer-driven follow-up commits:
- PR #100 / #61 review: Reduced-motion gate extended to per-tick path-following so sprites freeze entirely (not just stop typing-jitter).
- PR #101 / #63 review: Nav tabs,
+ NEW MISSION, and pack selector all bumped from 32/28 px to ≥36 px so the title commitment matched the diff. - PR #98 / #64 review:
.sr-only-focusableutility introduced; the department panel becomes a visible, framed widget when any inner button has focus. - PR #103 / #89 review #1 (TOCTOU):
safeFetch()helper introduced with pinned-IPundici.Agent. - PR #103 / #89 review #2 (redirects): Redirect loop re-validates every hop, hop limit 5,
Authorizationstripped on cross-origin. - PR #110 / #59 review: Build break from required
runTaskdep fixed in test harnesses. - PR #115 / #81 review: GET filter added so internal keys cannot round-trip back through PUT.
- PR #123 / #67 review: Encryption tests made
.env-independent viavi.doMock("…/runtime.ts"). - PR #129 / #73 review:
sanitizeOAuthRedirectrejects protocol-relative//redirects.
Known Follow-ups
Captured as low-priority issues for a later cycle:
- #134
encryptSecret("")produces a payload that cannot round-trip; current behavior is pinned by a regression test. - #135
sanitizeOAuthRedirectrejects IPv6[::1]redirects becauseURL.hostnamereturns the bracketed form; equality check should normalize. - #136 Vitest scope includes type-only files (
server/types/*) which suppress reported statement coverage; adding them tocoverage.excludeis a one-line fix.
Numbers
| Metric | v2.5.2 | v2.6.0 |
|---|---|---|
| Open code-review issues | 41 | 0 |
| Backend test count | ~1900 | 2492 (+30 %) |
| Backend statement coverage | 35.47 % | 39.34 % |
| Backend branch coverage | not measured | 71.78 % |
| Backend function coverage | not measured | 72.97 % |
| Frontend test count | 217 | 270 (+24 %) |
pnpm audit --prod --audit-level=high |
4 advisories | 0 advisories |
Upgrade Notes
No migrations required. After pulling:
pnpm install --frozen-lockfile
pnpm run setupExisting OAuth tokens, settings rows, and pack positions continue to work without
re-encryption or schema changes.